CLI to generate Java POJOs and interfaces from Conjure API definitions.
Note that all Java APIs in this repository (including conjure-java-core) are considered "internal" in the sense that
they may change at any time and without prior warning or deprecation notice. These packages are published for
convenience, but users must assume responsibility for adapting their code when dependencies change.
The recommended way to use conjure-java is via a build tool like gradle-conjure. However, if you don't want to use gradle-conjure, there is also an executable which conforms to RFC 002, published on bintray.
Usage: conjure-java generate [...options] <input> <output>
Generate Java bindings for a Conjure API
<input> Path to the input IR file
<output> Output directory for generated source
Options:
--objects Generate POJOs for Conjure type definitions
--jersey Generate jax-rs annotated interfaces for client or server-usage
--undertow Generate undertow handlers and interfaces for server-usage
--retrofit Generate retrofit interfaces for streaming/async clients
--requireNotNullAuthAndBodyParams
Generate @NotNull annotations for AuthHeaders and request body params
--retrofitCompletableFutures
Generate retrofit services which return Java8 CompletableFuture instead of OkHttp Call (deprecated)
--retrofitListenableFutures
Generate retrofit services which return Guava ListenableFuture instead of OkHttp Call
--undertowServicePrefixes
Generate service interfaces for Undertow with class names prefixed 'Undertow'
--undertowListenableFutures
Generate Undertow services which return Guava ListenableFuture for asynchronous processing
--useImmutableBytes
Generate binary fields using the immutable 'Bytes' type instead of 'ByteBuffer'
--strictObjects
Generate POJOs that by default will fail to deserialize unknown fields
--nonNullCollections
Generate POJOs that by default will fail to deserialize collections with null values
Conjure-java supports feature flags to enable additional opt-in features. To enable features provided by a feature flag, simply supply the feature flag as an additional parameter when executing conjure-java. Available feature flags can be found in the Options class.
Conjure-java objects are always immutable and thread-safe. Fields are never null; instead, Java 8 Optionals are used. JSON serialization is handled using Jackson annotations.
Conjure object: ManyFieldExample
Objects can only be instantiated using the builder pattern:
ManyFieldExampleexample = ManyFieldExample.builder() .string("foo") .integer(123) .optionalItem("bar") .addAllItems(iterable) .build();
Or using Jackson via
conjure-jackson-serialization:ObjectMappermapper = ObjectMappers.newServerObjectMapper(); ManyFieldExamplefromJson = mapper.readValue("{\"string\":\"foo\", ...}", ManyFieldExample.class);
Conjure union: UnionTypeExample
Union types can be one of a few variants. To interact with a union value, users should use the
.acceptmethod and define a Visitor that handles each of the possible variants, including the possibility of an unknown variant.Foooutput = unionTypeExample.accept(newVisitor<Foo>() { publicFoovisitStringExample(StringExamplevalue) { // your logic here! } publicFoovisitSet(Set<String> value) {} // ...publicFoovisitUnknown(StringunknownType) {} });
Visitors may seem clunky in Java, but they have the upside of compile-time assurance that you've handled all the possible variants. If you upgrade an API dependency and the API author added a new variant, the Java compiler will force you to explicitly deal with this new variant. We intentionally avoid
switchstatements andinstanceofchecks for this exact reason.Conjure enum: EnumExample
Enums are subtly different from regular Java enums because they tolerate deserializing unknown values. This is important because it ensures server authors can add new variants to an enum without causing runtime errors for consumers that use older API jars.
EnumExampleone = EnumExample.ONE; System.out.println(one); // prints: 'ONE'EnumExamplefromJson = mapper.readValue("\"XYZ\"", EnumExample.class); System.out.println(fromJson); // prints: 'XYZ'
Conjure alias: StringAliasExample
Aliases have exactly the same JSON representation as their inner type, so they are useful for making error-prone function signatures more bulletproof:
-doSomething(String, String, String);+doSomething(ProductId, UserId, EmailAddress);
Conjure-java generates Java interfaces with JAX-RS annotations, so they can be used on the client side or on the server-side.
Example jersey interface: EteService
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("/")
@Generated("com.palantir.conjure.java.services.JerseyServiceGenerator")
publicinterfaceEteService {
@GET@Path("base/string")
Stringstring(@HeaderParam("Authorization") AuthHeaderauthHeader);
}Use conjure-java-runtime's JaxRsClient which configures Feign with sensible defaults:
RecipeBookServicerecipes = JaxRsClient.create(
RecipeBookService.class,
userAgent,
hostMetrics,
clientConfiguration);
List<Recipe> results = recipes.getRecipes();You need a Jersey-compatible server. We recommend Dropwizard (which is based on Jetty), but Grizzly, Tomcat, etc should also work fine. Use conjure-java-runtime's ConjureJerseyFeature to configure Jackson and Exception mappers appropriately. Then, you just need to implement the interface:
publicfinalclassRecipeBookResourceimplementsRecipeBookService {
@OverridepublicList<Recipe> getRecipes() {
// ...
}
}Then in your server main method, register your resource. For example, using Dropwizard:
public void run(YourConfiguration configuration, Environment environment) {
// ...
+ environment.jersey().register(new RecipeBookResource());
}As an alternative to the JAX-RS interfaces above, conjure-java can generate equivalent interfaces with Retrofit2 annotations. These clients are useful if you want to stream binary data or make non-blocking async calls:
Example Retrofit2 interface: EteServiceRetrofit
publicinterfaceEteServiceRetrofit {
@GET("./base/binary")
@StreamingCall<ResponseBody> binary(@Header("Authorization") AuthHeaderauthHeader);
}Use conjure-java-runtime's Retrofit2Client which configures Retrofit2 with sensible defaults:
RecipeBookServiceRetrofitrecipes = Retrofit2Client.create(
RecipeBookServiceRetrofit.class,
userAgent,
hostMetrics,
clientConfiguration);
Call<List<Recipe>> asyncResults = recipes.getRecipes();In the undertow setting, for a ServiceName conjure defined service, conjure will generate an interface: ServiceName to be extended by your resource and an UndertowService named ServiceNameEndpoints
To avoid conflicts with the equivalent jersey interface (when you are generating both), use in your build.gradle:
conjure {
java {
undertowServicePrefixes =true
}
}With this option, Undertow generated interfaces will be prefixed with Undertow. Here the interface would be called: UndertowServiceName
To use the generated handlers:
publicstaticvoidmain(String[] args) {
Undertowserver = Undertow.builder()
.addHttpListener(8080, "0.0.0.0")
.setHandler(ConjureHandler.builder()
.services(RecipeBookServiceEndpoints.of(newRecipeBookResource()))
.build())
.build();
server.start();
}The Conjure Undertow generator supports asynchronous request processing allowing all service methods to return a
Guava ListenableFuture. These methods may be
implemented synchronously by replacing return object with return Futures.immediateFuture(object).
Asynchronous request processing decouples the HTTP request lifecycle from server task threads, allowing you to replace waiting on shared resources with callbacks, reducing the number of required threads.
This feature is enabled using:
conjure {
java {
undertowListenableFutures =true
}
}Asynchronous request processing is helpful for endpoints which do not need a thread for the entirety of the request.
👍 Delegation to an asynchronous client, for instance either retrofit or dialogue 👍
@OverridepublicListenableFuture<String> getValue() {
// Assuming this retrofit client was compiled with --retrofitListenableFuturesreturnretrofitClient.getValue();
}👍 Long polling 👍
Long polling provides lower latency than simple repeated polling, but requests take a long time relative to computation. A single thread can often handle updating all polling requests without blocking N request threads waiting for results.
@OverridepublicListenableFuture<String> getValue() {
SettableFuture<String> result = SettableFuture.create();
registerFuture(result);
returnresult;
}👎 Not for delegation to synchronous operations, Feign clients for example 👎
This example is less efficient than return Futures.immediateFuture(feignClient.getValue()) because you pay an
additional cost to switch threads, and maintain an additional executor beyond the configured server thread pool.
ListeningExecutorexecutor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
@OverridepublicListenableFuture<String> getValue() {
// BAD: do not do this!returnexecutor.submit(() -> feignClient.getValue());
}👎 Not waiting on another thread 👎
This is even less efficient than the previous example because it requires two entire threads for the duration
of the request. It's reasonable to defer computation to an executor bounded to the work, but you should
wrap the executor with MoreExecutors.listeningDecorator and return the future to avoid blocking a server
worker thread for both the queued and execution durations of the task.
ExecutorServiceexecutor = Executors.newFixedThreadPool(CORES);
@OverridepublicListenableFuture<BigInteger> getValue() {
// BAD: do not do this!Future<BigInteger> future = executor.submit(() -> complexComputation());
BigIntegerresult = Uninterruptibles.getUninterruptibly(future);
returnFutures.immediateFuture(result);
}By default, conjure-java will use java.nio.ByteByffer to represent fields of Conjure type binary. However, the ByteBuffer class has many subtleties, including interior mutability.
To avoid many of these foot-guns, we recommend using the useImmutableBytes feature flag to opt-in to using the com.palantir.conjure.java.lib.Bytes class.
This will become the default behaviour in a future major release of conjure-java.
For instructions on how to set up your local development environment, check out the Contributing document.
This project is made available under the Apache 2.0 License.