Codename One build (build)
The build goal is used to send builds to the Codename One build server. It also supports a few local build targets, such as ios-source, which generates an Xcode project, and android-source which generates an Android Gradle project.
This goal is bound to the package phase of the Codename One application project archetype (cn1app-archetype), so you don’t need to run this directly.
Example
mvn cn1:build -Dcodename1.platform=javase -Dcodename1.buildTarget=mac-os-x-desktopProperties
- codename1.platform
Specifies the platform to build for. Values include Java SE, android, ios, JavaScript, and win.
- codename1.buildTarget
The build target. Different platforms support different build sets of build targets.
Build Target Platform Description mac-os-x-desktop
Java SE
Sends a Mac desktop build to the Codename One build server.
windows-desktop
Java SE
Sends a Windows desktop build to the Codename One build server.
android-device
android
Sends an Android build to the Codename One build server.
android-source
android
Generates an Android Gradle project locally that can be opened and built in Android Studio.
ios-device
ios
Sends an iOS build to the Codename One build server.
ios-source
ios
Generates an iOS Xcode project locally that can be opened and built in Xcode.
JavaScript
JavaScript
Sends a JavaScript build to the Codename One build server.
- automated
Set to
trueto submit build as an automated build. When using this flag, the goal will wait for the build server to complete the build, then download it and save it in the target directory (of the associated module) using standard maven artifact file naming conventions. This allows you to set up automated CI workflows more.Default value is
false.Requires a Codename One enterprise subscription.
- open
If set to
true, this will automatically open the generated Gradle or Xcode project in Android studio or Xcode. Only applicable to theios-sourceandandroid-sourcebuildTargets.Default is
false
Generate legacy cn1lib (cn1lib)
This goal generates a legacy.cn1lib file. It’s bound to the package phase of cn1lib-archetype projects so you will never need to run this goal directly.
Output location
This will output the cn1lib file inside the common/target directory of the root multimodule maven project.
Compliance check (compliance-check)
Checks API usage in the project’s "common" module to ensure that it doesn’t use any classes or methods that aren’t supported by Codename One.
This goal is bound to the process-classes phase of both Codename One application project archetype (cn1app-archetype) projects and Codename One library project archetype (cn1lib-archetype) projects, so you shouldn’t ever need to run this goal directly.
Compile CSS (css)
Compiles the project’s CSS files, generating a theme.res file which is placed in the build/classes directory.
This goal is bound to the process-classes phase of Codename One application project archetype (cn1app-archetype) projects, so you shouldn’t need to run this goal directly.
Generate app project (generate-app-project)
Generates a Maven project using the Codename One application project archetype (cn1app-archetype) as a basis, and applying an optional project template. This goal can also be used to migrate legacy Codename One Ant-based Application projects into maven.
Usage example
This goal shouldn’t be run inside an existing Maven project directory. It will output a project into a directory named after the artifactId parameter.
Because there is no existing project, you will need to provide the full maven path to the goal:
mvn com.codenameone:codenameone-maven-plugin:$CN1VERSION:generate-app-project \
-DsourceProject=/path/to/my/ProjectTemplate \
-DgroupId=com.example \
-DartifactId=myapp \
-Dcn1Version=$CN1VERSION
<release> in the repository metadata, for the $CN1VERSION in the above example. Because this goal runs outside any project, Maven can only find that version if you have configured the Codename One repository in your Maven settings; see the Configuring the Codename One repository section of Creating a new project.In the above example, assuming all went well, it would output your project into a directory named myapp.
Parameters
- cn1Version
The Codename One version that you want to use for the project. This will be manifested as the
cn1.versionandcn1.plugin.versionproperties in the common/pom.xml file of the generated project. If omitted it will default to the cn1Version that’s hard-coded in the cn1app-archetype artifact.- sourceProject
The path to an optional project template to use. This may be either a legacy Ant project, or a Maven project that follows the structure of Codename One application project archetype (cn1app-archetype).
- artifactId
The
artifactIdto use for the new project.- groupId
The
groupIdto use for the new project.- version
The
versionto use for the new project. Optional.Default value is "1.0-SNAPSHOT"
- packageName
The package name to use for the new project.
This is necessary if the
sourceProjectproperty is a Maven project. If thesourceProjectis a legacy Ant project, then this property is ignored.- mainName
The main class name to use for the new project.
This is necessary if the
sourceProjectproperty is a Maven project. If thesourceProjectis a legacy Ant project, then this property is ignored.
Migrating legacy Ant projects into Maven
When providing an Ant-based Codename One application project as the sourceProject parameter, this goal will generate an equivalent Maven project to the Ant project, with the same settings and sources.
See Migrating an existing project for examples using this goal to migrate an existing project into Maven.
Project templates
When using a Codename One application project archetype (cn1app-archetype) maven project as the sourceProject parameter, the project will be treated project template, and perform some basic processing of the source files as necessary convert the template into a real project. This includes replacing all occurrences of ${mainName} and ${packageName} in project sources with the value of the mainName and packageName parameters provided on the command-line.
Any occurrences of mainName and packagePath in file or directory names will be swapped with the values of mainName and packagePath (which is automatically derived from packageName by substituting '.' with file separators).
The generate-app-project.rpf file
The (optional) secret sauce that differentiates a regular Maven project from a Maven project template is the existence of a generate-app-project.rpf file in the root project directory. This file is in rich property file format, and allows you to define a minimal amount of configuration details that the generate-app-project goal needs to convert the template into a real project.
This file may contain the following properties:
- template.mainName
The name of the main class that’s used in this project. This property isn’t required if the project is already using the
mainNameplaceholder in the file name that contains the main class, and the${mainName}placeholder in any source code referring to the main class.Often times it’s easier to specify this property here rather than injecting placeholders into the template source base, because that way the template itself can be used as a valid project.
- template.packageName
The package name for the app. This property isn’t required if the project is already using the
packagePathplaceholder in directories containing your main package files, and the${packageName}placeholder in any source code that refers to the main package.Often times it’s easier to specify this property here rather than injecting placeholders into the template source base, because that way the template itself can be used as a valid project.
- template.type
Either
mavenorant, depending on the project type.- dependencies
An XML snippet containing any more Maven dependencies that should be added to the project. This is handy of the project template relies on other cn1libs that are on Maven central.
Sample generate-app-project.rpf file
template.mainName=MyApp
template.packageName=com.example
template.type=maven
[dependencies]
====
<dependency>
<groupId>com.codenameone</groupId>
<artifactId>googlemaps-lib</artifactId>
<version>1.0.1</version>
<type>pom</type>
</dependency>
====
Sample: The bare-bones Kotlin app project
As a fuller example of a project template, see the generate-app-project.rpf file in the bare-bones Kotlin app template.
This is the template that’s used in Codename One initializr for the Bare-bones Kotlin project.
Clone project (clone)
Clones the current project as a new project with a different groupId, artifactId, packageName, and mainName. Resulting project will be made available in the common/target/generated-sources/cn1-cloned-projects directory.
Usage example
mvn cn1:clone \
-DgroupId=com.example.newgroup \
-DartifactId=newapp \
In the above example, assuming all went well, it would output your project into a directory named myapp.
Parameters
- artifactId
The
artifactIdto use for the cloned project.- groupId
The
groupIdto use for the cloned project.
Create GUI Form (create-gui-form)
The create-gui-form goal will generate a GUI form that can be edited in the GUI builder.
Basic Usage Example
mvn cn1:create-gui-form -DclassName=com.example.MyForm
The above example will generate a GUIBuilder form with the provided class name. It effectively generates two files:
common/src/main/guibuilder/com/example/MyForm.guicommon/src/main/java/com/example/MyForm.java
You can then open the GUI builder to edit this form using the cn1:guibuilder goal:
mvn cn1:guibuilder -DclassName=com.example.MyForm
Parameters
- className
(Required) The class name of the form that you wish to generate. E.g
com.example.MyForm.- guiType
The kind of GUI component to generate. Supports
Form,Dialog, andContainer. Default value is "Form"- autoLayout
Whether to use autolayout. This is boolean (true/false), and the default value is
true.
Generate cn1lib project (generate-cn1lib-project)
Generates a Maven cn1lib project (using the cn1lib-archetype) given an ANT cn1lib project as a template.
This is to assist in migrating Ant projects to Maven projects. This won’t make any changes to the source Ant project. It generates a new project using the Codename One library project archetype (cn1lib-archetype) and copies all files and configuration from the source project, into the new project.
Usage example
Suppose you have an Ant Codename One library project at /path/to/MyLegacyAntLibraryProject and you want to convert it to a Maven project.
You can run the generate-cn1lib-project goal as follows:
mvn com.codenameone:codenameone-maven-plugin:$CN1VERSION:generate-cn1lib-project \
-DsourceProject=/path/to/MyLegacyAntLibraryProject \
-DgroupId=com.example \
-DartifactId=my-maven-lib \
-Dversion=1.0-SNAPSHOT \
-Ucodenameone-maven-plugin version, listed as <release> in the repository metadata, for the $CN1VERSION in this command. Because this goal runs outside any project, Maven can only find that version if you have configured the Codename One repository in your Maven settings; see the Configuring the Codename One repository section of Creating a new project.Some notes about this command as shown above:
This command is formatted for Unix/Mac on multiple lines, using the
\character to escape the new-lines. On Windows the command will need to be all one line, and you should omit those\escape characters.The
-Uflag tells Maven to update its catalogs to ensure that it can find the$CN1VERSIONthat you specify.
If all goes well, you should find a new maven project generated in the "your-maven-lib" directory (named after the artifactId that you specified).
To test that the project was generated successfully, try opening the resulting project in your IDE or run its "install" goal on the command-line.
For example:
cd my-maven-lib
mvn install
After running the "install" command, you should be able to add your library as a dependency to a Codename One application project archetype (cn1app-archetype) project using the following dependency:
<dependency>
<groupId>com.example</groupId>
<artifactId>my-maven-lib-lib</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
</dependency>
artifactId has an extra "-lib" appended. That is, it’s <artifactId>my-maven-lib-lib</artifactId> and not <artifactId>my-maven-lib</artifactId>. This is because the artifactId that you specify in the generate-cn1lib-project goal is used for the "root" module of the multimodule maven project. The actual "lib" project that you can use as a Maven dependency is the "lib" submodule, which uses the specified artifactId with a "-lib" suffix.See Codename One libraries for more information about the resulting maven library project.
Properties
- sourceProject
The path to the legacy Ant project that you want to convert to a Maven project.
- groupId
The maven
groupIdto use for the resulting project.- artifactId
The maven
artifactIdto use for the resulting project.- version
The maven
versionto use for the resulting project. Default "1.0-SNAPSHOT"
Generate desktop app wrapper (generate-desktop-app-wrapper)
Generates the bootstrapping wrapper class for the Java SE desktop app. This is used by the Java SE module of Codename One application project archetype (cn1app-archetype) projects.
You shouldn’t need to call this goal directly.
Generate GUI sources (generate-gui-sources)
Generates Java sources from the Codename One GUI builder files. This goal is bound to the process-sources phase of Codename One application project archetype (cn1app-archetype) projects, so you should never need to run this goal explicitly.
Generate native interfaces (generate-native-interfaces)
Generates stub implementations for all native interfaces defined in the project. This won’t overwrite any existing implementations that may exist.
You should run this goal explicitly after you create a native interface in your class.
See the Codename One Developer guide section on native interfaces for more information on creating native interfaces.
Usage example
Suppose you’ve created a native interface as the Java interface com.mycompany.myapp.MyNative, as described in the example in native interfaces.
After creating this (and possibly other) native interfaces in your project, run the generate-native-interfaces Maven goal as follows:
mvn cn1:generate-native-interfaces
By default this generates Java/Objective-C stubs. You can optionally include Swift and Kotlin stubs (both off by default):
mvn cn1:generate-native-interfaces \
-Dcn1.generateNativeInterfaces.swift=true \
-Dcn1.generateNativeInterfaces.kotlin=true
This will generate the following files (if they don’t exist yet).
- Java SE
Java SE/src/main/java/com/mycompany/myapp/MyNativeImpl.java- ios
ios/src/main/objectivec/com_mycompany_myapp_MyNativeImpl.hios/src/main/objectivec/com_mycompany_myapp_MyNativeImpl.m
- android
android/src/main/java/com/mycompany/myapp/MyNativeImpl.java- android (optional Kotlin)
android/src/main/java/com/mycompany/myapp/MyNativeImpl.kt- JavaScript
JavaScript/src/main/JavaScript/com_mycompany_myapp_MyNativeImpl.js- ios (optional Swift)
ios/src/main/objectivec/com_mycompany_myapp_MyNativeImpl.swift
Open and edit these files to implement your native interface methods as desired.
Generate OpenAPI client (generate-openapi)
Generates a typed Codename One client from an OpenAPI 3.x JSON
specification. Writes one @Mapped record (Java 17+) or class (Java
8 target) per components.schemas entry and one
@RestClient-annotated interface per OpenAPI tag. The generated
files land in common/src/main/java so the project owns the
contract; the matching networking implementation is emitted into
common/target/generated-sources by the build-time annotation
processor so the project source stays clean.
The mojo is paired with the existing process-annotations pipeline:
identical schemas across operations collapse to one record/class, and
operationIds become interface methods that resolve at runtime via a
com.codename1.io.rest.RestClients registry populated by the generated
bootstrap class (the same splice pattern as @Mapped mappers).
Usage example
mvn -pl common cn1:generate-openapi \
-Dcn1.openapi.spec=petstore.json \
-Dcn1.openapi.basePackage=com.example.petstore
Configuration:
| Property | Description |
|---|---|
| Local file path or URL of the OpenAPI 3.x JSON document, for example
|
| Java package the generated sources are written under. Records / classes
go under |
| Defaults to |
| Defaults to |
Generated output
The listings in this section are the goal’s own output, generated from a
Swagger Petstore specification cut down to the pet, store and user
tags. That spec is in the repository at
docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-openapi-petstore.json,
so you can run the goal against it and compare. The committed copies carry a
license header and the tag:: markers this guide includes them by; strip
those two additions and the rest is exactly what the goal wrote.
One @RestClient interface per tag and one model per schema land under
common/src/main/java:
com/example/petstore/
PetApi.java // @RestClient interface, methods addPet, updatePet,
// findPetsByStatus, getPetById, deletePet
StoreApi.java // @RestClient interface, methods placeOrder,
// getOrderById, deleteOrder
UserApi.java // @RestClient interface, methods createUser,
// getUserByName
com/example/petstore/model/
Pet.java // @Mapped record (Java 17+) or class (Java 8)
Order.java
User.java
Category.javaTag.java is missing from that list on purpose. The Petstore declares
Tag and Category with the same two properties, and identical shapes
collapse to one record, so Pet.tags() comes back typed as a list of
Category. Give the two schemas different shapes if you want them to
stay separate classes.
Each @RestClient interface method is annotated with the HTTP verb
and path; parameters are annotated @Path / @Query / @Header /
@Body so the processor knows how to assemble the Rest call. The
emitted method shape:
@RestClient
public interface PetApi {
@POST("/pet")
void addPet(@Body com.example.petstore.model.Pet body, @Header("Authorization") String bearerToken, OnComplete<Response<com.example.petstore.model.Pet>> callback);
@PUT("/pet")
void updatePet(@Body com.example.petstore.model.Pet body, @Header("Authorization") String bearerToken, OnComplete<Response<com.example.petstore.model.Pet>> callback);
@GET("/pet/findByStatus")
void findPetsByStatus(@Query("status") String status, @Header("Authorization") String bearerToken, OnComplete<Response<java.util.List<com.example.petstore.model.Pet>>> callback);
@GET("/pet/{petId}")
void getPetById(@Path("petId") Long petId, @Header("Authorization") String bearerToken, OnComplete<Response<com.example.petstore.model.Pet>> callback);
@DELETE("/pet/{petId}")
void deletePet(@Path("petId") Long petId, @Header("Authorization") String bearerToken, OnComplete<Response<String>> callback);
static PetApi of(String baseUrl) {
return RestClients.create(PetApi.class, baseUrl);
}
}
The model type referenced by the generated API is emitted into the generated model package:
@Mapped
public record Pet(@JsonProperty("id") Long id, @JsonProperty("name") String name, @JsonProperty("category") com.example.petstore.model.Category category, @JsonProperty("photoUrls") java.util.List<String> photoUrls, @JsonProperty("tags") java.util.List<com.example.petstore.model.Category> tags, @JsonProperty("status") String status) {}
Model types are emitted as fully qualified names (the API interface
lives in <basePackage> and models under <basePackage>.model) so
the generator never needs to track imports or worry about
collisions between an API class name and a same-named model.
Call sites use the static factory:
void loadPet(String bearerToken) {
PetApi api = PetApi.of("https://petstore3.swagger.io/api/v3");
api.getPetById(10L, bearerToken, response -> {
// Two things have to be settled before the payload is a Pet.
// The generated impl passes null when the request never
// completed, and on an error status it forwards the raw
// Response<String> under this type -- so on that path
// getResponseData() is the error body, not a Pet, and
// touching it as one is a cast that does not throw on iOS.
if (response == null) {
ToastBar.showErrorMessage("The request did not complete");
} else if (response.getResponseCode() < 200 || response.getResponseCode() > 299) {
ToastBar.showErrorMessage(response.getResponseErrorMessage());
} else if (response.getResponseData() == null) {
ToastBar.showErrorMessage("The server returned no pet");
} else {
ToastBar.showInfoMessage(response.getResponseData().name());
}
});
}
Pet
is a record. On a Java 8 target the generator emits a class with public
fields instead, so pet.name() becomes pet.name.The <Tag>ApiImpl class that actually performs the HTTP call lives
in target/generated-sources — the project source never references
it directly. The build server probes the project zip for the
generated cn1app.RestClientBootstrap and splices the registry
wiring in, mirroring the existing cn1app.MapperBootstrap pattern.
Scope
HTTP verbs:
GET,POST,PUT,DELETE,PATCH.Parameter locations:
path,query,header,cookie. Multiplecookieparameters on the same operation are joined into a singleCookie: a=1; b=2request header.Request bodies:
application/json— serialized viaMappers.toJson(body)before being attached.Response schemas:
$refresolution, primitives (string/number/integer/boolean), arrays, object schemas.oneOf/anyOf/allOfcollapse toObject— callers cast.String
enumschemas are emitted as Java enums and bound by the JSON mapper through each constant’sname(), so a model property typed by an enum$refgets the generated enum type. This requires every enum value to be a valid (non-reserved) Java identifier; an enum whose values are not — for example"two-day"— degrades toStringrather than generating an enum that couldn’t round-trip. Integer/number enums keep their numeric type.Schema unification: two
components.schemasentries with identical property shapes collapse to a single record/class to avoid an explosion of duplicates.Authentication: bearer token is exposed as a
@Header("Authorization") String bearerTokenparameter on every operation. API-key auth declared in the spec is emitted as@Headeror@Querylike any other parameter; basic auth and OAuth bearer tokens travel through the samebearerTokenslot.
Generate gRPC client (generate-grpc)
Generates a typed Codename One gRPC-Web client from a proto3 .proto
specification. Writes one @ProtoMessage record (Java 17+) or class
(Java 8 target) per message, one @ProtoEnum enum per enum, and
one @GrpcClient-annotated interface per service. The generated
files land in common/src/main/java so the project owns the
contract; the matching protobuf codec and gRPC-Web call site are
emitted into common/target/generated-sources by the build-time
annotation processors so the project source stays clean.
The mojo is paired with the existing process-annotations
pipeline: @ProtoMessage triggers per-class binary protobuf codec
generation and a cn1app.ProtoBootstrap registration entry;
@GrpcClient triggers per-interface <Service>GrpcImpl generation
chained through com.codename1.io.grpc.GrpcWeb.invokeUnary(…)
and a cn1app.GrpcClientBootstrap that wires everything to the
com.codename1.io.grpc.GrpcClients registry (same splice pattern
as @Mapped mappers).
Usage example
mvn -pl common cn1:generate-grpc \
-Dcn1.grpc.proto=helloworld.proto \
-Dcn1.grpc.basePackage=com.example.hello
Configuration:
| Property | Description |
|---|---|
| Local path to the |
| Java package the generated sources are written under. Messages,
enums, and the |
| Defaults to |
| Defaults to |
Generated output
For a helloworld.proto declaring message HelloRequest,
message HelloReply, enum Mood, and service Greeter { rpc
SayHello(HelloRequest) returns (HelloReply); }, the goal emits
under common/src/main/java:
com/example/hello/ HelloRequest.java // @ProtoMessage record / class HelloReply.java Mood.java // @ProtoEnum enum with `number` accessor GreeterGrpc.java // @GrpcClient interface
Each @ProtoMessage carries one @ProtoField(tag = N) per field;
scalar fields use the default varint wire kind, sint32 / sint64
use wireType = ProtoField.WireKind.SINT, and fixed32 /
fixed64 / sfixed32 / sfixed64 use
wireType = ProtoField.WireKind.FIXED. Renamed fields (e.g.
snake_case → camelCase) carry the original proto name via the
optional name attribute so introspection tooling can recover it.
The @GrpcClient interface looks like:
@GrpcClient("helloworld.Greeter")
public interface GreeterGrpc {
@Rpc("SayHello")
void sayHello(com.example.hello.HelloRequest request, @Header("Authorization") String bearerToken, OnComplete<GrpcResponse<com.example.hello.HelloReply>> callback);
static GreeterGrpc of(String baseUrl) {
return GrpcClients.create(GreeterGrpc.class, baseUrl);
}
}
Call sites use the static factory:
void sayHello(String bearerToken) {
GreeterGrpc greeter = GreeterGrpc.of("https://grpc.example.com");
HelloRequest request = new HelloRequest("Ada", Arrays.asList("ada", "countess"), Mood.HAPPY);
greeter.sayHello(request, bearerToken, response -> {
if (response.isOk() && response.getResponseData() != null) {
ToastBar.showInfoMessage(response.getResponseData().message());
} else if (response.isOk()) {
ToastBar.showErrorMessage("The server returned an empty reply");
} else {
// getResponseCode() is the gRPC status, not the HTTP one --
// a gRPC-Web call can carry a failure under HTTP 200.
ToastBar.showErrorMessage("gRPC status " + response.getResponseCode()
+ ": " + response.getResponseErrorMessage());
}
});
}
new HelloRequest(); r.name = … and a read is reply.message rather than reply.message().The <Service>GrpcImpl class that actually performs the gRPC-Web
POST lives in target/generated-sources — the project source
never references it directly. The build server probes the project
zip for cn1app.ProtoBootstrap and cn1app.GrpcClientBootstrap
and splices the registry wiring in, mirroring the existing
cn1app.MapperBootstrap pattern.
Wire protocol
The runtime speaks gRPC-Web binary
(application/grpc-web+proto) over HTTP/1.1, which is the standard
mobile / browser variant of gRPC supported by Envoy, the official
grpcweb Go proxy, and the gRPC-Web filter in modern gRPC server
implementations. Plain HTTP/2 gRPC requires trailers that
ConnectionRequest doesn’t expose; gRPC-Web carries grpc-status
in a synthetic trailer frame in the response body instead.
A successful call:
Encodes the request message via the build-time-generated
ProtoCodecfor the request type.Wraps the encoded bytes in a 5-byte frame header (
flag + length) and posts to<baseUrl>/<service-fqn>/<method>withContent-Type: application/grpc-web+protoandX-Grpc-Web: 1.Reads the response body, iterates frames, accumulates data payload, and parses the trailer frame for
grpc-status/grpc-message.Decodes the accumulated payload via the response codec and invokes the supplied
OnComplete<GrpcResponse<T>>.
Scope
Unary RPCs (single request, single response). Streaming RPCs (
streamkeyword on request or response) are rejected at generation time — gRPC-Web client streaming requires HTTP/2.proto3 syntax. proto2
requiredis rejected;optionalis accepted but treated as a nullable field.Scalar types:
int32,int64,uint32,uint64,sint32,sint64,fixed32,fixed64,sfixed32,sfixed64,float,double,bool,string,bytes.enumdeclarations (nested or top-level) emit@ProtoEnumenums with apublic final int numberfield and astatic T forNumber(int n)lookup.Nested
messagedeclarations are emitted as top-level siblings in the same package so the generated codec layout stays flat.repeatedfields land asjava.util.List<T>. Scalar lists are encoded packed on the wire (proto3 default); the reader accepts both packed and unpacked forms.oneofcollapses to a group of nullable fields on the parent message — the mutual-exclusion guarantee is lost but round-trips work. Use application code to enforce the invariant if needed.Not yet supported:
map<K, V>, well-known types (Timestamp, Empty, etc.), fileimport, streaming RPCs. These produce a clear error from the parser pointing at the offending line.Authentication: a bearer token is exposed uniformly as a
@Header("Authorization") String bearerTokenparameter on every RPC method, mirroring the OpenAPI bearer-token slot.
Generate GraphQL client (generate-graphql)
Generates a typed Codename One GraphQL client from a GraphQL SDL
schema, optionally driven by a set of operation documents. Writes one
@Mapped record (Java 17+) or class (Java 8 target) per response shape
and input type, a Java enum per GraphQL enum, and one
@GraphQLClient-annotated interface carrying one method per operation.
The generated files land in common/src/main/java so the project owns
the contract; the matching client implementation and JSON mappers are
emitted into common/target/generated-sources by the build-time
annotation processors so the project source stays clean.
The mojo is paired with the existing process-annotations pipeline:
the generated @Mapped types trigger per-class JSON mapper generation
and a cn1app.MapperBootstrap entry, while @GraphQLClient triggers
per-interface <Name>Impl generation chained through
com.codename1.io.graphql.GraphQL.execute(…) (queries and mutations)
or com.codename1.io.graphql.GraphQL.subscribe(…) (subscriptions),
plus a cn1app.GraphQLClientBootstrap that wires everything to the
com.codename1.io.graphql.GraphQLClients registry (same splice pattern
as @Mapped mappers and @GrpcClient clients).
Usage example
mvn -pl common cn1:generate-graphql \
-Dcn1.graphql.schema=schema.graphqls \
-Dcn1.graphql.operations=operations.graphql \
-Dcn1.graphql.basePackage=com.example.starwars
Configuration:
| Property | Description |
|---|---|
| Local path to the GraphQL SDL schema ( |
| Java package the generated sources are written under. Models, enums,
input types, and the |
| Path to a |
| Simple name of the generated |
| Default endpoint URL recorded on the generated annotation. The
effective endpoint is always the argument passed to |
| Maximum selection-set depth used by the schema-only quick-start mode.
Defaults to |
| Defaults to |
| Defaults to |
Generation modes
Unlike OpenAPI paths or gRPC service methods, a GraphQL schema doesn’t enumerate the operations a client wants — the client chooses which fields to select. The goal therefore supports two modes:
Operations mode (recommended): supply
cn1.graphql.operations. For each namedquery/mutation/subscriptionthe generator emits one interface method plus a precise@Mappedresponse-type tree that mirrors exactly that operation’s selection set (nested selections become nested generated types). Referenced fragment definitions are merged into the request document automatically.Schema-only quick-start mode: omit
cn1.graphql.operations. For each field of the rootQuery/Mutation/Subscriptiontype the generator emits one method whose selection set is automatically expanded tocn1.graphql.maxDepthlevels, stopping at recursive types. This is a convenience for getting started; it may over- or under-fetch, so prefer operations mode for production code.
Generated output
For a Star Wars schema and an operations.graphql declaring
query HeroName, mutation AddReview, and
subscription OnReview, the goal emits under common/src/main/java:
com/example/starwars/ Episode.java // GraphQL enum -> Java enum ReviewInput.java // @Mapped input type HeroNameData.java // @Mapped query response root HeroNameData_Hero.java // nested selection type AddReviewData.java // @Mapped mutation response root OnReviewData.java // @Mapped subscription response root StarWarsApi.java // @GraphQLClient interface
GraphQL enums map to a generated Java enum in response types, input
types, and method variables alike — the JSON mapper binds enums by
their name(). Built-in scalars map to their boxed Java type
(Int → Integer, Float → Double, Boolean → Boolean,
String / ID → String); custom scalars fall back to String.
The @GraphQLClient interface looks like:
@GraphQLClient("https://example.com/graphql")
public interface StarWarsApi {
@Query(value = "query HeroName($episode: Episode) { hero(episode: $episode) { ...HeroFields friends { name } } } fragment HeroFields on Character { name }", operationName = "HeroName")
void heroName(@Var("episode") Episode episode, @Header("Authorization") String bearerToken, OnComplete<GraphQLResponse<HeroNameData>> callback);
@Mutation(value = "mutation AddReview($ep: Episode!, $review: ReviewInput!) { createReview(episode: $ep, review: $review) { stars commentary } }", operationName = "AddReview")
void addReview(@Var("ep") Episode ep, @Var("review") ReviewInput review, @Header("Authorization") String bearerToken, OnComplete<GraphQLResponse<AddReviewData>> callback);
@Subscription(value = "subscription OnReview($ep: Episode!) { reviewAdded(episode: $ep) { stars } }", operationName = "OnReview")
GraphQLSubscription onReview(@Var("ep") Episode ep, @Header("Authorization") String bearerToken, GraphQLSubscription.Handler<OnReviewData> handler);
static StarWarsApi of(String endpoint) {
return GraphQLClients.create(StarWarsApi.class, endpoint);
}
}
Call sites use the static factory. Queries and mutations report through
an OnComplete<GraphQLResponse<T>>:
void heroName(String bearerToken) {
StarWarsApi api = StarWarsApi.of("https://swapi.example.com/graphql");
api.heroName(Episode.EMPIRE, bearerToken, response -> {
// isOk() classifies the GraphQL payload, not the trip: a call that
// never reached the server, and a 2xx whose body would not parse,
// both arrive with an empty errors array and null data, so isOk()
// answers true for them. getResponseErrorMessage() is the one
// thing that is null only on a clean success -- it carries the
// first GraphQL error, the transport failure, or the parse
// failure -- and the HTTP code catches an error status whose body
// happened to decode.
boolean reachedTheServer = response.getResponseCode() >= 200
&& response.getResponseCode() <= 299;
if (!reachedTheServer || response.getResponseErrorMessage() != null) {
ToastBar.showErrorMessage(response.getResponseErrorMessage());
} else if (response.getData() == null || response.getData().hero() == null) {
// And an error-free answer still need not have found anything:
// the schema declares hero as Character, not Character!.
ToastBar.showInfoMessage("No hero for that episode");
} else {
ToastBar.showInfoMessage(response.getData().hero().name());
}
});
}
response.getData().hero().name() becomes
response.getData().hero.name.A subscription returns a GraphQLSubscription handle whose cancel()
ends the stream:
GraphQLSubscription watchReviews(String bearerToken) {
StarWarsApi api = StarWarsApi.of("https://swapi.example.com/graphql");
return api.onReview(Episode.JEDI, bearerToken,
new GraphQLSubscription.Handler<OnReviewData>() {
@Override
public void onNext(GraphQLResponse<OnReviewData> response) {
// onError is for the end of the stream. A per-field failure
// arrives here instead, as a next payload whose errors array
// is non-empty and whose data may be partial or absent.
if (response.hasErrors()) {
ToastBar.showErrorMessage(response.getResponseErrorMessage());
}
OnReviewData data = response.getData();
if (data != null && data.reviewAdded() != null) {
ToastBar.showInfoMessage(data.reviewAdded().stars() + " stars");
}
}
@Override
public void onError(GraphQLResponse<OnReviewData> response) {
ToastBar.showErrorMessage(response.getResponseErrorMessage());
}
@Override
public void onComplete() {
}
});
// Hold on to the handle and call cancel() to end the stream --
// leaving the form is the usual place to do it.
}
The <Name>Impl class that performs the request lives in
target/generated-sources — the project source never references it
directly. The build server probes the project zip for
cn1app.GraphQLClientBootstrap and splices the registry wiring in,
mirroring the existing cn1app.MapperBootstrap pattern.
Wire protocol
Queries and mutations are sent as an HTTP POST to the endpoint with a
JSON body {"query":…,"operationName":…,"variables":{…}} and a
Content-Type: application/json header. The response envelope
({"data":…,"errors":[…]}) is parsed into a typed
GraphQLResponse<T>; because a GraphQL response can carry both data and
errors, GraphQLResponse exposes getData() and getErrors()
independently and isOk() reports whether the errors array was empty.
Subscriptions run over a WebSocket using the graphql-transport-ws
protocol (offered via the Sec-WebSocket-Protocol handshake header):
the client sends connection_init, waits for connection_ack, sends a
subscribe message carrying the operation, and maps each next
payload’s data to T before delivering it to the handler. The
WebSocket endpoint defaults to the query endpoint with its scheme
rewritten to ws / wss; pass a ws:// / wss:// URL to of(…) to
override it. All handler callbacks are dispatched on the EDT.
Scope
Queries, mutations, and subscriptions.
GraphQL enums map to generated Java enums in every position (response fields, input fields, and method variables); unknown enum names decode to
nullrather than throwing.Input object types map to generated
@Mappedclasses; variables of scalar, enum, input-object, and list types are supported.Named and inline fragments are resolved against the schema and merged into the generated response types and the request document.
Built-in scalars (
Int,Float,Boolean,String,ID) map to their boxed Java types. Custom scalars fall back toString.union/interfaceselections are emitted with their common fields in the quick-start mode; use explicit operation documents with inline fragments for full polymorphic typing.Authentication: a bearer token is exposed uniformly as a
@Header("Authorization") String bearerTokenparameter on every method, mirroring the OpenAPI and gRPC bearer-token slots.
GUIBuilder goal (guibuilder)
The guibuilder goal opens the Codename One GUI builder to edit a specified GUIBuilder form.
Usage
mvn cn1:guibuilder -DclassName=com.example.MyForm
This will open the GUI builder to edit the form whose class is com.example.MyForm.
Parameters
- className
The fully qualified name to the form class that you wish to edit. This must have been earlier generated using Create GUI Form (
create-gui-form).
Generate archetype (generate-archetype)
The generate-archetype mojo will generate a new archetype maven project based on an existing archetype project. It’s designed to ease the maintenance of several similar archetypes which differ in some dependencies or default project source code. It takes as input a template file which references the base archetype project and specifies what to customize.
Example Usage
mvn cn1:generate-archetype -Dtemplate=/path/to/mytemplate.java
Parameters
- template
Required. The path to a template file that should be used to generate the archetype project.
- outputDir
Optional. The output directory where the archetype project should be written to. The project will be created at
outputDir/artifactId, where theartifactIdis as specified in the[archetype]section of the template. By default this will be the current working directory.
This goal doesn’t require a project to run. You can run it directly using the full goal coordinates:
mvn com.codenameone:codenameone-maven-plugin:7.0-SNAPSHOT:generate-archetype \ -Dtemplate=/path/to/mytemplate.java
Template syntax
Here is a sample template file:
#set( $symbol_pound = '#' )
#set( $symbol_dollar = '$' )
#set( $symbol_escape = '\' )
package ${package};
/*
[archetype] (1)
----
extends=../cn1app-archetype (2)
groupId=com.codenameone.archetypes
artifactId=helloworld2-archetype
version=7.0-SNAPSHOT
----
[dependencies] (3)
----
<dependency>
<groupId>com.codenameone.libs</groupId>
<artifactId>filechooser-lib</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
</dependency>
----
*/
import static com.codename1.ui.CN.*;
import com.codename1.ui.*;
import com.codename1.ui.layouts.*;
import com.codename1.io.*;
import com.codename1.ui.plaf.*;
import com.codename1.ui.util.Resources;
/**
* This file was generated by <a href="https://www.codenameone.com/">Codename One</a> for the purpose
* of building native mobile applications using Java.
*/
public class ${mainName} {
private Form current;
private Resources theme;
public void init(Object context) {
// use two network threads instead of one
updateNetworkThreadCount(2);
theme = UIManager.initFirstTheme("/theme");
// Enable Toolbar on all Forms by default
Toolbar.setGlobalToolbar(true);
// Pro feature
Log.bindCrashProtection(true);
addNetworkErrorListener(err -> {
// prevent the event from propagating
err.consume();
if(err.getError()!= null) {
Log.e(err.getError());
}
Log.sendLogAsync();
Dialog.show("Connection Error", "There was a networking error in the connection to " + err.getConnectionRequest().getUrl(), "OK", null);
});
}
public void start() {
if(current!= null){
current.show();
return;
}
//WebSocket sock;
Form hi = new Form("Hi World", BoxLayout.y());
hi.add(new Label("Hello World"));
hi.show();
}
public void stop() {
current = getCurrentForm();
if(current instanceof Dialog) {
((Dialog)current).dispose();
current = getCurrentForm();
}
}
public void destroy() {
}
}The
[archetype]section specifies details about the archetype project to be generated. For example, thegroupId,artifactId, etc.The
extendsproperty in the[archetype]section is required, and points to the location of the archetype project that this template is based on. Path is relative to the location of the template file.The
[dependencies]section includes content that should be injected into the<dependencies>section of the pom.xml file. Note that this isn’t the pom file for the archetype project itself. It’s the pom.xml file for the project that the archetype project is to generate.
Notice that the above template is velocity template for a Java file. It will be used as the source code for the main class in the resulting project. It’s a velocity template because maven’s archetype:generate goal will process it to replace properties such as package and the main class name.
Template sections
- archetype
This is a required section and specifies both the location of the base archetype project from which this project is to be derived, and the coordinates of the output archetype project, such as
groupId,archetypeId, andversion.Example
[archetype] ---- extends=../cn1app-archetype groupId=com.codenameone.archetypes artifactId=helloworld2-archetype version=7.0-SNAPSHOT ----Properties
- extends
Required. The path to the archetype project that this is based on. This should usually be the
cn1app-archetypeproject as it includes the placeholders that this generator relies on for injecting content into the pom.xml file and its project structure was used as a basis for developing this mojo.- groupId
The groupID of the resulting archetype project. You can or use the
idproperty to specifygroupId,artifactIdandversionin a single string.- artifactId
The artifactId of the resulting archetype project. You can or use the
idproperty to specifygroupId,artifactIdandversionin a single string.- version
The version of the resulting archetype project. You can or use the
idproperty to specifygroupId,artifactIdandversionin a single string.- id
A colon-separated string in the format
groupId:artifactId:versionthat can be used as an alternative togroupId,artifactId, andversion.- parentGroupId
If the output archetype project should be part of a multi-module project, then this will specify the parent
groupIdfor the<parent>tag in the pom.xml file.- parentArtifactId
If the output archetype project should be part of a multi-module project, then this will specify the parent
archetypeIdfor the<parent>tag in the pom.xml file.- parentVersion
If the output archetype project should be part of a multi-module project, then this will specify the parent
archetypeIdfor the<parent>tag in the pom.xml file.- parent
A colon-delimited string in the format
parentGroupId:parentArtifactId:parentVersionthat can be used as an alternative to theparentGroupId,parentArtifactId, andparentVersionproperties.
- dependencies
Specify more dependencies that should be injected into the
<dependencies>section of the pom.xml file for the common module of the maven project that the archetype will generate. The content of this section will be injected into thesrc/main/resources/archetype-resources/common/pom.xmlof the archetype project.Example
[dependencies] --- <dependency> <groupId>com.codenameone.libs</groupId> <artifactId>filechooser-lib</artifactId> <version>1.0-SNAPSHOT</version> <type>pom</type> </dependency> ---- css
CSS content that should be injected into the theme.css file of the project. This CSS will be injected into the
src/main/resources/archetype-resources/common/src/main/css/theme.cssfile of the archetype project.Example
[css] --- #Constants { includeNativeBool: true; } Button { color:green; border:1px solid green; border-radius: 2mm; margin: 5mm; } ---- properties
Properties that should be appended to the
codenameone_settings.propertiesfile. These will be added to thesrc/main/resources/archetype_resources/common/codenameone_settings.propertiesfile of the archetype project.Example
[properties] --- codename1.arg.win.desktop-vm=zuluFx8-32bit codename1.arg.win.desktopExtractDll=true codename1.arg.win.launchOnStart=true codename1.arg.win.runAfterInstall=true ---- files
Files contains a list of files that should be created in the archetype project. All file paths are relative to the "common" project root directory. Each path should have a corresponding section with the heading
[file:path/to/file].Example
[files] ---- src/main/guibuilder/__mainName__MainForm.gui src/main/java/__mainName__MainForm.java ---- [file:src/main/guibuilder/__mainName__MainForm.gui] ---- <?xml version="1.0" encoding="UTF-8"?> <component type="Form" layout="LayeredLayout" layeredLayoutPreferredWidthMM="0.0" layeredLayoutPreferredHeightMM="0.0" autolayout="true" title="${mainName}MainForm" scrollableY="true" name="${mainName}MainForm"> <component type="Button" text="Click Me" name="Button"> <layoutConstraint insets="auto auto auto auto" referenceComponents="-1 -1 -1 -1" referencePositions="0.0 0.0 0.0 0.0" /> </component> </component> ---- [file:src/main/java/__mainName__MainForm.java] ---- package ${package}; public class ${mainName}MainForm extends com.codename1.ui.Form { public ${mainName}MainForm() { this(com.codename1.ui.util.Resources.getGlobalResources()); } public ${mainName}MainForm(com.codename1.ui.util.Resources resourceObjectInstance) { initGuiBuilderComponents(resourceObjectInstance); } //-- DON'T EDIT BELOW THIS LINE!!! protected com.codename1.ui.Button gui_Button = new com.codename1.ui.Button(); // <editor-fold defaultstate="collapsed" desc="Generated Code"> private void initGuiBuilderComponents(com.codename1.ui.util.Resources resourceObjectInstance) { setLayout(new com.codename1.ui.layouts.LayeredLayout()); setInlineStylesTheme(resourceObjectInstance); setScrollableY(true); setInlineStylesTheme(resourceObjectInstance); setTitle("MyForm"); setName("MyForm"); gui_Button.setText("Click Me"); gui_Button.setInlineStylesTheme(resourceObjectInstance); gui_Button.setName("Button"); addComponent(gui_Button); ((com.codename1.ui.layouts.LayeredLayout)gui_Button.getParent().getLayout()).setInsets(gui_Button, "auto auto auto auto").setReferenceComponents(gui_Button, "-1 -1 -1 -1").setReferencePositions(gui_Button, "0.0 0.0 0.0 0.0"); }// </editor-fold> //-- DON'T EDIT ABOVE THIS LINE!!! } ----
Install Codename One (install-codenameone)
Installs/Updates Codename One into the user’s home directory. This goal is automatically run if a Maven build is attempted and it detects that Codename One isn’t yet installed (in the $HOME/.codenameone) directory.
Install legacy Cn1lib (install-cn1lib)
Installs a legacy cn1lib file as a dependency in this application project.
See Adding project dependencies for fuller coverage of project dependencies.
Usage example
mvn cn1:install-cn1lib -Dfile=/path/to/MyLegacyLib.cn1lib
This will generate a Maven pom project for this lib inside the "cn1libs" directory, and it will add a dependency inside the common/pom.xml file.
Removing the Cn1lib later
If you need to remove the cn1lib later (that is, revert an installation), you can remove the <dependency> tag that was added to the common/pom.xml file for the cn1lib.
You can also remove the directory that was created inside the cn1libs folder for this cn1lib - but this step isn’t necessary.
Parameters
- file
Path to the cn1lib file you want to install.
- groupId
The groupId to use for the generated pom project. Optional. If not specified, it will use the same groupId as the root project.
- artifactId
The artifactId to use for the generated pom project. Optional. If not provided, it will generate an artifactId derived from the project’s artifactId.
- version
The version to use for the generated pom project. Optional. If not provided, it will use the project version.
- updatePom
A boolean flag indicating whether it should automatically update the pom.xml file with the dependency.
Default is
true- overwrite
A boolean flag indicating whether it should overwrite an existing project of the same name. Default false.
Default is
false
Open Designer (designer)
Opens the legacy Codename One designer app to edit the project "theme.res" file.
Usage example
mvn cn1:designer
This will open the Codename One designer.
Prepare simulator classpath (prepare-simulator-classpath)
Sets up some properties and environment variables required for the Codename One simulator to run properly.
This goal is bound to the initialize phase of the Java SE module of the Codename One application project archetype (cn1app-archetype) and shouldn’t be executed directly.
Open settings (settings)
Downloads and opens the standalone Codename One Settings editor bound to the current Maven project. The editor manages basic project properties, build hints, and extensions.
mvn cn1:settings
Run tests (test)
Runs Codename One unit tests.
This goal is bound to the test phase of Codename One application project archetype (cn1app-archetype) projects, so that running it directly isn’t necessary. If you build a cn1app-archetype project using:
mvn install
or
mvn package
Then this goal will be executed automatically.
skipTests flag. For example, mvn install -DskipTestsUpdate Codename One (update)
Updates Codename One. This updates tools that live inside $HOME/.codenameone, such as the GUI builder, and tries to update the cn1.version and cn1.plugin.version properties in the project’s pom.xml. The standalone Settings editor is resolved from Maven using cn1.plugin.version when you run mvn cn1:settings.
Usage example
mvn cn1:update
Parameters
- newVersion
(Optional) The version to update to. This should be a published Codename One version. Will accept a value of "LATEST" to cause it to resolve to the latest version in the Codename One Maven repository.
LATESTnever downgrades a project: if the newest version offered is older than the one the project already uses, the goal reports it and leavescn1.versionandcn1.plugin.versionuntouched.If this parameter is omitted, then it will be implicitly set to
LATEST, but it won’t update thecn1.versionorcn1.plugin.versionproperties if they’re set to a SNAPSHOT version.
See Updating Codename One for more information about updating Codename One.