Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/archetype-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ on:
- 'maven/integration-tests/cn1app-archetype-test.sh'
- 'maven/integration-tests/cn1app-staged-jar-test.sh'
- 'maven/integration-tests/cn1app-minimal-layout-test.sh'
- 'maven/integration-tests/backend-cn1lib-test.sh'
- 'scripts/initializr/common/src/main/resources/common-hosted-platform-profiles.xml'
- 'scripts/initializr/common/src/main/resources/backend-only-pom.xml'
# What a generated project stages for upload also depends on the
Expand Down Expand Up @@ -54,6 +55,7 @@ on:
- 'maven/integration-tests/cn1app-archetype-test.sh'
- 'maven/integration-tests/cn1app-staged-jar-test.sh'
- 'maven/integration-tests/cn1app-minimal-layout-test.sh'
- 'maven/integration-tests/backend-cn1lib-test.sh'
- 'scripts/initializr/common/src/main/resources/common-hosted-platform-profiles.xml'
- 'scripts/initializr/common/src/main/resources/backend-only-pom.xml'
# What a generated project stages for upload also depends on the
Expand Down Expand Up @@ -138,6 +140,15 @@ jobs:
run: |
cd maven/integration-tests
xvfb-run -a bash cn1app-archetype-test.sh
# A cn1lib with a server half, used by a generated server: the library's
# controller, bean, entity, repository and migration are wired by the
# server's build, on the JVM and natively.
- name: Check a cn1lib's backend part in a generated server
env:
CN1_BACKEND_PACKAGE_REQUIRED: '1'
run: |
cd maven/integration-tests
env -u DISPLAY -u WAYLAND_DISPLAY bash backend-cn1lib-test.sh
# The jar cn1:build would upload, for every target the generated build.sh
# offers. #5380 put ~300 MB of ffmpeg natives into every desktop upload and
# the build client refused them; nothing looked at this jar for two months.
Expand Down
33 changes: 29 additions & 4 deletions Ports/iOSPort/nativeSources/CodenameOne_GLViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -3402,12 +3402,40 @@ - (void)viewDidLayoutSubviews {
// never leaves landscape, so when the user stays in landscape its bounds never
// change and no layout pass runs to correct the published size. viewDidAppear
// and the transition's completion therefore re-read the real bounds as well.
// Reads the window's safe-area insets into the globals the Java side asks for.
// Answers whether any of them changed.
- (BOOL)cn1ReadSafeAreaInsets {
UIEdgeInsets in = self.view.window.safeAreaInsets;
JAVA_INT l = (JAVA_INT)in.left * scaleValue;
JAVA_INT r = (JAVA_INT)in.right * scaleValue;
JAVA_INT t = (JAVA_INT)in.top * scaleValue;
JAVA_INT b = (JAVA_INT)in.bottom * scaleValue;
if (l == safeLeft && r == safeRight && t == safeTop && b == safeBottom) {
return NO;
}
safeLeft = l;
safeRight = r;
safeTop = t;
safeBottom = b;
return YES;
}

- (void)cn1PublishViewSizeIfChanged {
// The same reason viewWillTransitionToSize: gives: iOS lays the view out
// at the opposite orientation for its background snapshot.
if (isAppSuspended || self.view == nil) {
return;
}
// The insets go stale the same way the size does. viewSafeAreaInsetsDidChange
// is not sent to a view that a presented controller covers, so a rotation
// under one -- an orientation-locked screen, a camera -- left the landscape
// insets in place after the app was back in portrait, and every form with a
// safe area was squeezed between the old notch margins.
if (self.view.window != nil && [self cn1ReadSafeAreaInsets]) {
[self cn1UpdateStatusBarTapProxyFrame];
lockDrawing = NO;
repaintUI();
}
CGSize sz = self.view.bounds.size;
int newW = (int)(sz.width * scaleValue);
int newH = (int)(sz.height * scaleValue);
Expand Down Expand Up @@ -5025,10 +5053,7 @@ - (void)viewSafeAreaInsetsDidChange {
#if TARGET_OS_OSX
#else
[super viewSafeAreaInsetsDidChange];
safeLeft = (JAVA_INT)self.view.window.safeAreaInsets.left * scaleValue;
safeRight = (JAVA_INT)self.view.window.safeAreaInsets.right * scaleValue;
safeTop = (JAVA_INT)self.view.window.safeAreaInsets.top * scaleValue;
safeBottom = (JAVA_INT)self.view.window.safeAreaInsets.bottom * scaleValue;
[self cn1ReadSafeAreaInsets];

// Status-bar tap proxy height tracks safeAreaInsets.top, so refresh it
// here so rotations and other safe-area changes keep the proxy aligned
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline.

// tag::backend-beans-library-dependency[]
<dependency>
<groupId>com.example</groupId>
<artifactId>notelib-backend</artifactId>
<version>1.0</version>
</dependency>
// end::backend-beans-library-dependency[]
3 changes: 3 additions & 0 deletions docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -1081,6 +1081,8 @@ include::../demos/common/src/main/snippets/developer-guide/advanced-topics-under

Once the project is generated, add your cross-platform Java sources under `common/src/main/java`, CSS under `common/src/main/css`, and run `mvn install` from the project root to build. The build produces both a Maven artifact for pom-style consumption and a legacy `.cn1lib` file in `common/target` for users still on Ant.

A library can also have a server half. The archetype's `backend` module holds classes for an application's server: its controllers, beans, entities, repositories and migrations are wired into any server that depends on the library, as if that server had compiled them. When the module has sources or resources, the `.cn1lib` carries them as `backend.zip`. See <<backend-libraries>>.

==== Build hints in cn1libs

Some cn1libs are pretty simple to install - just add them as a Maven dependency in `common/pom.xml`. For example, many of the more elaborate cn1libs need some pretty complex configurations. This is the case when native code is involved where you need to add permissions or plist entries for the various native platforms to get everything to work. This makes the cn1lib's helpful but less than seamless which is where you want to go.
Expand Down Expand Up @@ -1159,6 +1161,7 @@ The table below covers the files that can/should be a part of a cn1lib file:
| nativewin.zip | &#215; | Native Windows sources if applicable
// vale-skip: Microsoft.FirstPerson: "ME" in "Java ME" is the Java Micro Edition platform abbreviation, not a first-person pronoun.
| nativeme.zip | &#215; | Native Java ME sources if applicable
| backend.zip | &#215; | The library's server half, compiled, if it has one. See <<backend-libraries>>
|====

****
Expand Down
37 changes: 37 additions & 0 deletions docs/developer-guide/Backend-Beans.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,43 @@ A start-up that fails partway runs the destroy methods of the beans already
built, so a server a supervisor restarts doesn't leak what their constructors
opened.

[[backend-libraries]]
=== Controllers and beans from a library

A server's wiring is generated while it builds, from the classes it compiles.
A library can take part in that wiring: its `@RestController` classes get
routes, its `@Component` and `@Service` classes become beans, its entities and
repositories get their data access, and its `db/migration` scripts run, all as
if the server had compiled them itself. The server's own code can inject the
library's beans like any other.

A jar opts in by carrying the empty resource
`META-INF/codenameone/backend-library`. A jar without the marker is an ordinary
dependency: its classes are on the classpath, but none of them are wired.

A cn1lib carries its server half inside the `.cn1lib` file, as `backend.zip`
beside the client parts:

* A library made from the Maven cn1lib archetype has a `backend` module. Put
the server classes in `backend/src/main/java`; the module already holds the
marker. When the module has sources or resources of its own, the `.cn1lib`
includes it, and a server
that uses the library depends on the `<library>-backend` artifact (or the
`backend` classifier of an installed `.cn1lib`):
+
[source,xml]
----
include::../demos/common/src/main/snippets/developer-guide/backend-beans.xml[tag=backend-beans-library-dependency,indent=0]
----
* A Gradle cn1lib puts the server classes in `src/backend/java` and publishes
them as `<library>-backend`, marked.
* A library with no client side is a plain jar that depends on
`codenameone-backend` with `provided` scope and carries the marker.

The library itself is never wired at its own build; only the server that uses
it generates the routes and beans. When the server defines a class with the
same name as one in a library, the server's class is used.

=== What the build refuses

Everything a container would discover at start-up is checked while the module
Expand Down
3 changes: 3 additions & 0 deletions docs/developer-guide/Backend-Data.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,9 @@ name. The set gets its own history table, so its versions never collide with
the application's, and it runs before the application's scripts so they can
refer to its tables.

A library jar marked as a backend library ships its own `db/migration`
scripts the same way; see <<backend-libraries>>.

[source,java,role=cn1-no-try]
----
include::../demos/backend/src/main/java/com/codenameone/developerguide/backend/MigrationSnippets.java[tag=backend-migrations-library,indent=0]
Expand Down
1 change: 1 addition & 0 deletions docs/developer-guide/Gradle-Project-Workflow.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ The library is published in the same shape the Maven `cn1lib-archetype` produces
`N-common`, classifier `cn1css`:: The library's CSS, as a zip.
`N-<platform>`:: One jar per platform: compiled classes for `javase`, sources for the others, which the native builders compile.
`N-lib`:: A `pom` that applications depend on, with one `codename1.platform` profile per platform.
`N-backend`:: The library's server half, compiled from `src/backend/java` and `src/backend/resources`, when there is one. A server that depends on it wires its controllers, beans and entities as its own; see <<backend-libraries>>.

Gradle cn1libs don't produce `.cn1lib` files.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,19 @@ public AnnotationProcessing withOwnClassDirectories(List<File> directories) {
return this;
}

/// Whether backend libraries on the classpath are merged into the output
/// first; see [BackendLibraries].
private boolean mergeBackendLibraries;

/// Merges the backend libraries on the compile classpath into the output
/// directory before anything scans it, so their controllers, beans and
/// entities are wired as the module's own. For a module's main classes
/// only: its tests see the merged classes through the main output.
public AnnotationProcessing withBackendLibraries(boolean merge) {
this.mergeBackendLibraries = merge;
return this;
}

/// The compile classpath without [#ownClassDirectories].
private List<String> dependencyClasspath() {
if (ownClassDirectories.isEmpty()) {
Expand Down Expand Up @@ -142,10 +155,28 @@ public void run() throws BuildExecutionException {
classpath.add(new File(element));
}
com.codename1.maven.annotations.JavaSourceCompiler.setProjectClasspath(classpath);
boolean processed = false;
try {
runProcessors();
processed = true;
} finally {
com.codename1.maven.annotations.JavaSourceCompiler.clearProjectClasspath();
// The processors rewrite merged classes in place; the record must
// describe what this build left -- a failed build too, since a
// processor may have woven some classes before another one threw --
// or the next build reads every rewritten library class as the
// module's own and never refreshes it.
if (mergeBackendLibraries) {
try {
BackendLibraries.restamp(outputDirectory);
} catch (IOException e) {
if (processed) {
throw new BuildExecutionException("Could not record the merged backend "
+ "libraries in " + outputDirectory + ": " + e.getMessage(), e);
}
// Otherwise the processors' own failure is the one to report.
}
}
}
}

Expand All @@ -154,6 +185,27 @@ private void runProcessors() throws BuildExecutionException {
log.debug("cn1: nothing compiled at " + outputDirectory + " -- skipping annotation processing");
return;
}
if (new File(outputDirectory, BackendLibraries.MARKER).isFile()) {
// A backend library is wired by the server that uses it, as that
// server's own classes. Processed here as well, it would ship
// generated classes with fixed names -- cn1app.DaoBootstrap, the
// wiring record -- that collide with the server's.
log.info("cn1: " + outputDirectory + " is a backend library; it is processed "
+ "by the server that depends on it");
return;
}
if (mergeBackendLibraries) {
try {
int merged = BackendLibraries.merge(compileClasspath, outputDirectory);
if (merged > 0) {
log.info("cn1: merged " + merged + " backend librar"
+ (merged == 1 ? "y" : "ies") + " into " + outputDirectory);
}
} catch (IOException e) {
throw new BuildExecutionException("Could not merge the backend libraries into "
+ outputDirectory + ": " + e.getMessage(), e);
}
}
List<AnnotationProcessor> processors = loadProcessors();
if (processors.isEmpty()) {
log.debug("cn1: no AnnotationProcessor services registered -- nothing to do");
Expand Down
Loading