An open API service indexing awesome lists of open source software.

https://github.com/palantir/conjure-java

Conjure generator for Java clients and servers
https://github.com/palantir/conjure-java

octo-correct-managed

Last synced: 9 months ago
JSON representation

Conjure generator for Java clients and servers

Awesome Lists containing this project

README

          


Autorelease

# Conjure-Java [![License](https://img.shields.io/badge/License-Apache%202.0-lightgrey.svg)](https://opensource.org/licenses/Apache-2.0)

_CLI to generate Java POJOs and interfaces from [Conjure API definitions](https://github.com/palantir/conjure)._

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.

## Usage

The recommended way to use conjure-java is via a build tool like [gradle-conjure](https://github.com/palantir/gradle-conjure). However, if you don't want to use gradle-conjure, there is also an executable which conforms to [RFC 002](https://palantir.github.io/conjure/#/docs/rfc/002-contract-for-conjure-generators), published on [maven central](https://mvnrepository.com/artifact/com.palantir.conjure.java/conjure-java).

Usage: conjure-java generate [...options]

Generate Java bindings for a Conjure API
Path to the input IR file
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
--requireNotNullAuthAndBodyParams
Generate @NotNull annotations for AuthHeaders and request body params
--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
--useStagedBuilders
Generates compile-time safe builders to ensure all attributes are set, except those of type list,
set, map, or optional. If --useStrictStagedBuilders is set, this option is ignored.
--useStrictStagedBuilders
Generates compile-time safe builders to ensure all attributes are set.
--jakartaPackages
Generates jax-rs annotated interfaces which use the newer 'jakarta` packages instead of the
legacy 'javax' packages.
--externalFallbackTypes
Java external type imports are generated using their fallback type.
--excludeDialogueAsyncInterfaces
Exclude the generation of asynchronous interfaces for Dialogue clients.
--preferObjectBuilders
Exclude static factory methods from generated objects with one or more fields. Note that for
objects without any fields, this will still generate the static factory method.
--generateDialogueEndpointErrorResultTypes
This is an experimental feature that may cause compilation failures! If enabled, endpoints that
have associated errors will return a result type: a sealed interface permitting subclasses for the
endpoint's return value, and each endpoint error. Producing JARs with this feature enabled will
result in a compile-time break when consumers bump their dependency on the JAR. This is because the
return types of every endpoint with associated errors will change.
--defensiveCollections
Makes immutable copies of collections for union and alias types, respecting the
--nonNullCollections flag.

### Known Tag Values

#### Endpoint Tags

* `incubating`: Describes an endpoint as incubating and likely to change. These endpoints are generated with an `@Incubating` annotation.
* `server-request-context`: Opt into an additional [RequestContext](conjure-undertow-lib/src/main/java/com/palantir/conjure/java/undertow/lib/RequestContext.java) parameter in conjure-undertow interfaces, which allows request metadata to be read, and additional arguments to be associated with the request log.
* `server-async`: Opt into [asynchronous request processing](#asynchronous-request-processing) in conjure-undertow. The generated interface returns a `ListenableFuture` of the defined return type, allowing processing to occur in the background without blocking the request thread.
* `deprecated-for-removal`: For endpoint definitions that [are marked deprecated](https://github.com/palantir/conjure/blob/master/docs/spec/conjure_definitions.md#endpointdefinition), indicates that the endpoint will be removed in a future release. This has the effect of Java code being generated with an `@Deprecated(forRemoval = true)` annotation on the endpoint method in the Dialogue service interface.

#### Endpoint Argument Tags

* `server-squelch`: Opts out of attaching safe non-body parameters to the request. By default, all known log-safe non-body inputs are included.

_The following argument tags are deprecated, replaced by `safety` declarations._
* `safe`: Annotates parameters as `@Safe` to log using safe-logging annotations. Implementations may add this data to the request log.
* `unsafe`: Annotates parameters as `@Unsafe` to log using safe-logging annotations. Implementations may add this data to the request log.

### Feature Flags

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](./conjure-java-core/src/main/java/com/palantir/conjure/java/Options.java) class.

## Example generated objects

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](https://github.com/FasterXML/jackson) annotations.

- **Conjure object: [ManyFieldExample](./conjure-java-core/src/integrationInput/java/com/palantir/product/ManyFieldExample.java)**

Objects can only be instantiated using the builder pattern:

```java
ManyFieldExample example = ManyFieldExample.builder()
.string("foo")
.integer(123)
.optionalItem("bar")
.addAllItems(iterable)
.build();
```

Or using Jackson via [`conjure-jackson-serialization`](https://github.com/palantir/conjure-java-runtime):

```java
ObjectMapper mapper = ObjectMappers.newServerObjectMapper();
ManyFieldExample fromJson = mapper.readValue("{\"string\":\"foo\", ...}", ManyFieldExample.class);
```

- **Conjure union: [UnionTypeExample](./conjure-java-core/src/integrationInput/java/com/palantir/product/UnionTypeExample.java)**

Union types can be one of a few variants. To interact with a union value, users should use the `.accept` method and define a Visitor that handles each of the possible variants, including the possibility of an unknown variant.

```java
Foo output = unionTypeExample.accept(new Visitor() {

public Foo visitStringExample(StringExample value) {
// your logic here!
}

public Foo visitSet(Set value) {}

// ...

public Foo visitUnknown(String unknownType) {}

});
```

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 `switch` statements and `instanceof` checks for this exact reason.

There is also a more concise visitor builders pattern that can be used to create a visitor:

```java
Foo output = unionTypeExample.accept(Visitor.builder()
.stringExample(value -> ...)
.set(value -> ...)
.throwOnUnknown()
.build());
});
```

- **Conjure enum: [EnumExample](./conjure-java-core/src/integrationInput/java/com/palantir/product/EnumExample.java)**

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.

```java
EnumExample one = EnumExample.ONE;
System.out.println(one); // prints: 'ONE'

EnumExample fromJson = mapper.readValue("\"XYZ\"", EnumExample.class);
System.out.println(fromJson); // prints: 'XYZ'
```

- **Conjure alias: [StringAliasExample](./conjure-java-core/src/integrationInput/java/com/palantir/product/StringAliasExample.java)**

Aliases have exactly the same JSON representation as their inner type, so they are useful for making error-prone function signatures more bulletproof:

```diff
-doSomething(String, String, String);
+doSomething(ProductId, UserId, EmailAddress);
```

## Example Jersey interfaces

Conjure-java generates Java interfaces with [JAX-RS](http://jax-rs-spec.java.net/) annotations, so they can be used on the client side or on the server-side.

Example jersey interface: [EteService](./conjure-java-core/src/integrationInput/java/com/palantir/product/EteService.java)

```java
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("/")
@Generated("com.palantir.conjure.java.services.JerseyServiceGenerator")
public interface EteService {
@GET
@Path("base/string")
String string(@HeaderParam("Authorization") AuthHeader authHeader);
}
```

### Jersey clients

Use [conjure-java-runtime's `JaxRsClient`](https://github.com/palantir/conjure-java-runtime#conjure-java-jaxrs-client) which configures Feign with sensible defaults:

```java
RecipeBookService recipes = JaxRsClient.create(
RecipeBookService.class,
userAgent,
hostMetrics,
clientConfiguration);

List results = recipes.getRecipes();
```

### Jersey servers

You need a [Jersey](https://jersey.github.io/)-compatible server. We recommend Dropwizard (which is based on Jetty), but Grizzly, Tomcat, etc should also work fine. Use [conjure-java-runtime's `ConjureJerseyFeature`](https://github.com/palantir/conjure-java-runtime#conjure-java-jersey-server) to configure Jackson and Exception mappers appropriately. Then, you just need to implement the interface:

```java
public final class RecipeBookResource implements RecipeBookService {

@Override
public List getRecipes() {
// ...
}
}
```

Then in your server main method, [register your resource](https://github.com/dropwizard/dropwizard/blob/caab32042c0f930d290b35248282c5623ea6cd82/docs/source/getting-started.rst#registering-a-resource). For example, using Dropwizard:

```diff
public void run(YourConfiguration configuration, Environment environment) {
// ...
+ environment.jersey().register(new RecipeBookResource());
}
```

## Undertow

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](https://github.com/palantir/conjure-java/blob/develop/conjure-undertow-lib/src/main/java/com/palantir/conjure/java/undertow/lib/UndertowService.java) named `ServiceNameEndpoints`

To avoid conflicts with the equivalent jersey interface (when you are generating both), use in your build.gradle:

```groovy
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:

```java
public static void main(String[] args) {
Undertow server = Undertow.builder()
.addHttpListener(8080, "0.0.0.0")
.setHandler(ConjureHandler.builder()
.services(RecipeBookServiceEndpoints.of(new RecipeBookResource()))
.build())
.build();
server.start();
}
```

### Asynchronous Request Processing

The Conjure Undertow generator supports asynchronous request processing allowing all service methods to return a
[Guava ListenableFuture](https://github.com/google/guava/wiki/ListenableFutureExplained). 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 on individual endpoints using the `server-async` endpoint tag:

```yaml
getEvents:
http: GET /events
returns: list
tags:
- server-async
```

Or for all endpoints using the `undertowListenableFutures` generator flag:
```groovy
conjure {
java {
undertowListenableFutures = true
}
}
```

#### Timeouts

By default, asynchronous request processing imposes a 3-minute timeout on the asynchronous component of the
request, canceling the return future after this duration is exceeded. A custom duration may be used by
extending the tag value using the form `server-async{timeout=5 minutes}`.

#### Examples

*Asynchronous request processing is helpful for endpoints which do not need a thread for the entirety of
the request.*

:+1: Delegation to an asynchronous client, for instance dialogue :+1:

```java
@Override
public ListenableFuture getValue() {
// Assuming this dialogue client was compiled with --undertowListenableFutures
return dialogueClient.getValue();
}
```

:+1: Long polling :+1:

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.

```java
@Override
public ListenableFuture getValue() {
SettableFuture result = SettableFuture.create();
registerFuture(result);
return result;
}
```

:-1: Not for delegation to synchronous operations, Feign clients for example :-1:

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.

```java
ListeningExecutor executor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());

@Override
public ListenableFuture getValue() {
// BAD: do not do this!
return executor.submit(() -> feignClient.getValue());
}
```

:-1: Not waiting on another thread :-1:

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.

```java
ExecutorService executor = Executors.newFixedThreadPool(CORES);

@Override
public ListenableFuture getValue() {
// BAD: do not do this!
Future future = executor.submit(() -> complexComputation());
BigInteger result = Uninterruptibles.getUninterruptibly(future);
return Futures.immediateFuture(result);
}
```

### Request Metadata

The [RequestContext](conjure-undertow-lib/src/main/java/com/palantir/conjure/java/undertow/lib/RequestContext.java) may be requested on an opt-in basis per-endpoint using the `server-request-context`
conjure endpoint `tag`:

```yaml
services:
ExampleService:
name: Example
package: com.palantir.example
base-path: /example

endpoints:
context:
http: GET /ping
returns: string
tags: [server-request-context]
```

This tag generates an additional parameter in the generated service interface:

```java
public interface ExampleService {
String ping(RequestContext context);
}
```

Implementations may read arbitrary headers and query parameters, or associate additional data with the request log:

```java
import java.util.concurrent.atomic.AtomicInteger;

public final class ExampleResource implements ExampleService {
private final AtomicInteger requestIndex = new AtomicInteger();

@Override
public String ping(RequestContext context) {
// Apply a unique index value to the request log:
context.requestArg(SafeArg.of("requestNumber", requestIndex.getAndIncrement()));
// Read the User-Agent header from the current request:
String userAgent = context.firstHeader("User-Agent");
// And add it to the request log:
context.requestArg(SafeArg.of("userAgent", userAgent));
return "pong";
}
}
```

### Conjure-Undertow Annotation Processor

The conjure-undertow annotation processor generates Undertow handler for services that can not easily be represented
using Conjure. You can find more details in the [respective readme](conjure-undertow-annotations/readme.md).

## conjure-lib `Bytes` class

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`](conjure-lib/src/main/java/com/palantir/conjure/java/lib/Bytes.java) class.
This will become the default behaviour in a future major release of conjure-java.

## Contributing

For instructions on how to set up your local development environment, check out the [Contributing document](./CONTRIBUTING.md).

## License
This project is made available under the [Apache 2.0 License](/LICENSE).