{"id":20208958,"url":"https://github.com/palantir/conjure-java-runtime","last_synced_at":"2026-01-07T14:15:58.647Z","repository":{"id":37514726,"uuid":"43088077","full_name":"palantir/conjure-java-runtime","owner":"palantir","description":"Opinionated libraries for HTTP\u0026JSON-based RPC using Dialogue, Feign, OkHttp as clients and Jetty/Jersey as servers","archived":false,"fork":false,"pushed_at":"2024-05-21T21:51:16.000Z","size":7104,"stargazers_count":78,"open_issues_count":41,"forks_count":97,"subscribers_count":250,"default_branch":"develop","last_synced_at":"2024-05-22T01:05:11.453Z","etag":null,"topics":["octo-correct-managed"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/palantir.png","metadata":{"files":{"readme":"readme.md","changelog":"changelog/4.29.1/pr-1141.v2.yml","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-09-24T19:24:22.000Z","updated_at":"2024-05-28T06:22:15.189Z","dependencies_parsed_at":"2023-09-29T00:46:21.184Z","dependency_job_id":"3b11f90f-9d9b-445f-a2e0-cc776c3be7a7","html_url":"https://github.com/palantir/conjure-java-runtime","commit_stats":null,"previous_names":[],"tags_count":396,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/palantir%2Fconjure-java-runtime","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/palantir%2Fconjure-java-runtime/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/palantir%2Fconjure-java-runtime/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/palantir%2Fconjure-java-runtime/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/palantir","download_url":"https://codeload.github.com/palantir/conjure-java-runtime/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247289429,"owners_count":20914464,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["octo-correct-managed"],"created_at":"2024-11-14T05:38:21.627Z","updated_at":"2026-01-07T14:15:58.610Z","avatar_url":"https://github.com/palantir.png","language":"Java","funding_links":[],"categories":["开发框架","Service Toolkits"],"sub_categories":["Java VM"],"readme":"\u003cp align=\"right\"\u003e\n\u003ca href=\"https://autorelease.general.dmz.palantir.tech/palantir/conjure-java-runtime\"\u003e\u003cimg src=\"https://img.shields.io/badge/Perform%20an-Autorelease-success.svg\" alt=\"Autorelease\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n[![CircleCI Build Status](https://circleci.com/gh/palantir/conjure-java-runtime/tree/develop.svg?style=shield)](https://circleci.com/gh/palantir/conjure-java-runtime)\n\n# Conjure Java Runtime (formerly http-remoting)\nThis repository provides an opinionated set of libraries for defining and creating RESTish/RPC servers and clients based\non [Feign](https://github.com/OpenFeign/feign) as a client and\n[Dropwizard](http://www.dropwizard.io/)/[Jersey](https://jersey.java.net/) with [JAX-RS](https://jax-rs-spec.java.net/)\nservice definitions as a server. Refer to the [API Contract](#api-contract) section for details on the contract between\nclients and servers. This library requires Java 8.\n\nCore libraries:\n- conjure-java-jaxrs-client: Clients for JAX-RS-defined service interfaces\n- conjure-java-jersey-server: Configuration library for Dropwizard/Jersey servers\n- [conjure-java-runtime-api](https://github.com/palantir/conjure-java-runtime-api): API classes for service configuration, tracing, and error propagation\n\n# Usage\n\nMaven artifacts are published to Maven Central. Example Gradle dependency configuration:\n\n```groovy\nrepositories {\n  mavenCentral()\n}\n\ndependencies {\n  compile \"com.palantir.conjure.java.runtime:conjure-java-jaxrs-client:$version\"\n  compile \"com.palantir.conjure.java.runtime:conjure-java-jersey-server:$version\"\n}\n```\n\n\n## conjure-java-jaxrs-client\nProvides the `JaxRsClient` factory for creating Feign-based clients for JAX-RS APIs. SSL configuration is mandatory for\nall clients, plain-text HTTP is not supported. Example:\n\n```java\nSslConfiguration sslConfig = SslConfiguration.of(Paths.get(\"path/to/trustStore\"));\nUserAgent userAgent = UserAgent.of(UserAgent.Agent.of(\"my-user-agent\", \"1.0.0\"));\nClientConfiguration config = ClientConfigurations.of(\n        ImmutableList.of(SERVER_URI),\n        SslSocketFactories.createSslSocketFactory(sslConfig),\n        SslSocketFactories.createX509TrustManager(sslConfig));\nHostMetricsRegistry hostMetricsRegistry = new HostMetricsRegistry();  // can call .getMetrics() and then collect them to a central metrics repository\nMyService service = JaxRsClient.create(MyService.class, userAgent, hostMetricsRegistry, config);\n```\n\nThe `JaxRsClient#create` factory comes in two flavours: one for creating immutable clients given a fixed\n`ClientConfiguration`, and one for creating mutable clients whose configuration (e.g., server URLs, timeouts, SSL\nconfiguration, etc.) changes when the underlying `ClientConfiguration` changes.\n\n## conjure-java-jersey-server\nProvides Dropwizard/Jersey configuration for handling conjure types, and also exception mappers for translating common\nruntime exceptions as well as our own `ServiceException` (see the [errors section](#errors-conjure-java-runtime-api))\nto appropriate HTTP error codes. A Dropwizard server is configured for conjure as follows:\n\n```java\npublic class MyServer extends Application\u003cConfiguration\u003e {\n    @Override\n    public final void run(Configuration config, final Environment env) throws Exception {\n        env.jersey().register(ConjureJerseyFeature.INSTANCE);\n        env.jersey().register(new MyResource());\n    }\n}\n```\n\n## tracing\nProvides [Zipkin](https://github.com/openzipkin/zipkin)-style call tracing libraries. All `JaxRsClient` instances are instrumented by default. Jersey server instrumentation is enabled via the\n`ConjureJerseyFeature` (see above).\n\nPlease refer to [tracing-java](https://github.com/palantir/tracing-java) for more details on the `tracing` library usage.\n\n## service-config (conjure-java-runtime-api)\nProvides utilities for setting up service clients from file-based configuration. Example:\n\n```yaml\n# config.yml\nservices:\n  security:\n    # default truststore for all clients\n    trustStorePath: path/to/trustStore.jks\n  myService:  # the key used in `factory.get(\"myService\")` below\n    uris:\n      - URI\n    # optionally set a myService-specific truststore\n    # security:\n    #   trustStorePath: path/to/trustStore.jks\n```\n\n```java\nServiceConfigBlock config = readFromYaml(\"config.yml\");\nServiceConfigurationFactory factory = ServiceConfigurationFactory.of(config);\nHostMetricsRegistry hostMetricsRegistry = new HostMetricsRegistry();\nMyService client = JaxRsClient.create(MyService.class, UserAgents.parse(\"my-agent\"), hostMetricsRegistry, ClientConfigurations.of(factory.get(\"myService\")));\n```\n\n\n## keystores and ssl-config (conjure-java-runtime-api)\n\nProvides utilities for interacting with Java trust stores and key stores and acquiring `SSLSocketFactory` instances\nusing those stores, as well as a configuration class for use in server configuration files.\n\nThe `SslConfiguration` class specifies the configuration that should be used for a particular `SSLContext`. The\nconfiguration is required to include information for creating a trust store and can optionally be provided with\ninformation for creating a key store (for client authentication).\n\nThe configuration consists of the following properties:\n\n* `trustStorePath`: path to a file that contains the trust store information. The format of the\n  file is specified by the `trustStoreType` property.\n* `trustStoreType`: the type of the trust store. See section below for details. The default value\n  is `JKS`.\n* (optional) `keyStorePath`: path to a file that contains the key store information. If unspecified,\n  no key store will be associated with this configuration.\n* (optional) `keyStorePassword`: password for the key store. Will be used to read the keystore\n  provided by `keyStorePath` (if relevant for the format), and will also be used as the password\n  for the in-memory key store created by this configuration. Required if `keyStorePath` is specified.\n* (optional) `keyStoreType`: the type of the key store. See section below for details. The default\n  value is `JKS`.\n* (optional) `keyStoreAlias`: specifies the alias of the key that should be read from the key store\n  (relevant for file formats that contain multiple keys). If unspecified, the first key returned by\n  the store is used.\n\nAn `SslConfiguration` object can be constructed using the static `of()` factory methods of the\nclass, or by using the `SslConfiguration.Builder` builder. `SslConfiguration` objects can be\nserialized and deserialized as JSON.\n\nOnce an `SslConfiguration` object is obtained, it can be passed as an argument to the\n`SslSocketFactories.createSslSocketFactory` method to create an `SSLSocketFactory` object that can\nbe used to configure Java SSL connections.\n\n#### Store Types\n\nThe following values are supported as store types:\n\n* `JKS`: a trust store or key store in JKS format. When used as a trust store, the\n  `TrustedCertificateEntry` entries are used as certificates. When used as a key store, the\n  `PrivateKeyEntry` specified by the `keyStoreAlias` parameter (or the first such entry returned if\n  the parameter is not specifeid) is used as the private key.\n* `PEM`: for trust stores, an X.509 certificate file in PEM format, or a directory of such\n  files. For key stores, a PEM file that contains a PKCS#1 RSA private key or PKCS#8 followed by the\n  certificates that form the trust chain for the key in PEM format, or a directory of such files. In\n  either case, if a directory is specified, every non-hidden file in the directory must be a file of\n  the specified format (they will all be read).\n* `PKCS12`: a trust store or key store in PKCS12 format. Behavior is the same as for the `JKS` type,\n  but operates on stores in PKCS12 format.\n* `Puppet`: a directory whose content conforms to the\n  [Puppet SSLdir](https://puppet.com/docs/puppet/7/dirs_ssldir.html) format. For trust stores, the\n  certificates in the `certs` directory are added to the trust store.  For key stores, the PEM files in the\n  `private_keys` directory are added as the private keys and the corresponding files in `certs` are used as the trust\n  chain for the key.\n\n## errors (conjure-java-runtime-api)\nProvides utilities for relaying service errors across service boundaries (see below).\n\n\n# API Contract\n\nconjure-java-runtime makes the following opinionated customizations to the standard Dropwizard/Feign behavior.\n\n#### Object serialization/deserialization\n\nAll parameters and return values of `application/json` endpoints are serialized/deserialized to/from JSON using a\nJackson `ObjectMapper` with `GuavaModule`, `ShimJdk7Module` (same as Jackson’s `Jdk7Module`, but avoids Jackson 2.6\nrequirement) and `Jdk8Module`. Servers must not expose parameters or return values that cannot be handled by this object\nmapper.\n\n\n#### Error propagation\nServers should use the `ServiceException` class to propagate application-specific errors to its callers. The\n`ServiceException` class exposes standard error codes that clients can handle in a well-defined manner; further,\nServiceException implements [SafeLoggable](https://github.com/palantir/safe-logging) and thus allows logging\ninfrastructure to handle \"unsafe\" and \"safe\" exception parameters appropriately. Typically, services define its error\ntypes as follows:\n\n```\nclass Errors {\n  private static final ErrorType DATASET_NOT_FOUND =\n    ErrorType.create(ErrorType.Code.INVALID_ARGUMENT, \"MyApplication:DatasetNotFound\");\n\n  static ServiceException datasetNotFound(DatasetId datasetId, String userName) {\n    // Both the safe and unsafe params will be sent back to the client; the client needs\n    // to decide themselves if any of these are safe to log. We're only marking things as\n    // safe or unsafe for this server to log.\n    return new ServiceException(\n            DATASET_NOT_FOUND, SafeArg.of(\"datasetId\", datasetId), UnsafeArg.of(\"userName\", userName));\n  }\n}\n\nvoid someMethod(String datasetId, String userName) {\n  if (!exists(datasetId)) {\n    throw Errors.datasetNotFound(datasetId, userName);\n  }\n}\n```\n\nThe `ConjureJerseyFeature` installs an exception mapper for `ServiceException`. The exception mapper sets the\nresponse media type to `application/json` and returns as response body a JSON representation of a `SerializableError`\ncapturing the error code, error name, and error parameters. The resulting JSON response is:\n\n```json\n{\n  \"errorCode\": \"INVALID_ARGUMENT\",\n  \"errorName\": \"MyApplication:DatasetNotFound\",\n  \"errorInstanceId\": \"xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx\",\n  \"parameters\": {\n    \"datasetId\": \"123abc\",\n    \"userName\": \"yourUserName\"\n  }\n}\n```\n\nJaxRsClient intercepts non-successful HTTP responses and throw a `RemoteException` wrapping the\ndeserialized server-side `SerializableError`. The error codes and names of the `ServiceException` and\n`SerializableError` are defined by the service API, and clients should handle errors based on the error code and name:\n\n```\ntry {\n    service.someMethod();\ncatch (RemoteException e) {\n    if (e.getError().errorName().equals(\"MyApplication:DatasetNotFound\")) {\n        handleError(e.getError().parameters().get(\"datasetId\"));\n    } else {\n        throw new RuntimeException(\"Failed to call someMethod()\", e);\n    }\n}\n```\n\nFrontends receiving such errors should use a combination of error code, error name, and parameters to display localized,\nuser friendly error information. For example, the above error could be surfaced as *\"The requested dataset with id\n123abc could not be found\"*.\n\nTo support **legacy server implementations**, the `ConjureJerseyFeature` also installs exception mappers for\n`IllegalArgumentException`, `NoContentException`, `RuntimeException` and `WebApplicationException`. The exceptions\ntypically yield `SerializableError`s with `exceptionClass=errorCode=\u003cexception classname\u003e` and\n`message=errorName=\u003cexception message\u003e`. Clients should refrain from displaying the `message` or `errorName` fields to\nuser directly. Services should prefer to throw `ServiceException`s instead of the above, since they are easier to\nconsume for clients and support transmitting exception parameters in a safe way.\n\n##### `RemoteException` vs `ServiceException` vs `SerializableError` vs `ErrorType`\n\n - `ErrorType` is a record type, meant to be used as 'compile time constants' - essentially used by services to define the 'enum' of their service exceptions\n - `SerializableError` defines the wire format for serializing ServiceExceptions in HTTP response bodies and contains the error code, error instance id, and application-defined parameters\n - `ServiceException` is a final subclass of `Exception`, thrown by the server\n - `RemoteException` is what the client sees if a remote call results in the server internally throwing a `ServiceException`\n\nThe workflow is:\n\n - Server code throws an instance of `ServiceException`, containing some `ErrorType`\n - The `com.palantir.conjure.java.server.jersey.ServiceExceptionMapper` exception mapper\n   - determines the response code for this service exception\n   - converts this into a `SerializableError`\n   - serializes this into the response body as JSON\n - The client sees a `RemoteException`, which contains the `SerializableError` which was sent over the wire\n - The client can inspect the `SerializableError` and choose to act\n - If the client is itself a server and does not handle the `RemoteException`, a `SerializableError` error will be sent\n   as the response with and `errorCode` of `INTERNAL`, `errorName` of `Default:Internal`, the same `errorInstanceId` as\n   the original `RemoteException` and no `parameters`.\n\n#### Serialization of Optional and Nullable objects\n`@Nullable` or `Optional\u003c?\u003e` fields in complex types are serialized using the standard Jackson mechanism:\n- a present value is serialized as itself (in particular, without being wrapped in a JSON object representing the `Optional` object)\n- an absent value is serialized as a JSON `null`.\nFor example, assume a Java type of the form\n```java\npublic final class ComplexType {\n    private final Optional\u003cComplexType\u003e nested;\n    private final Optional\u003cString\u003e string;\n}\n```\n, and an instance\n```java\nComplexType value = new ComplexType(\n        Optional.of(\n                new ComplexType(\n                        Optional.\u003cComplexType\u003eabsent(),\n                        Optional.\u003cString\u003eabsent(),\n        Optional.of(\"baz\"));\n```\nThe JSON-serialized representation of this object is:\n```json\n{\"nested\":{\"nested\":null,\"string\":null},\"string\":\"baz\"}\n```\n\n#### Optional return values\nWhen a call to a service interface declaring an `Optional\u003cT\u003e` return value with media type `application/json` yields:\n- a `Optional#empty` return value, then the HTTP response has error code 204 and an empty response body.\n- a non-empty return value, then the HTTP response has error code 200 and the body carries the deserialized `T` object\n  directly, rather than a deserialized `Optional\u003cT\u003e` object.\n\nJaxRsClients intercept such responses, deserialize the `T`-typed return value and return it to the caller wrapped as an\n`Optional\u003cT\u003e`.\n\n#### Call tracing\n\nClients and servers propagate call trace ids across JVM boundaries according to the\n[Zipkin](https://github.com/openzipkin/zipkin) specification. In particular, clients insert `X-B3-TraceId: \u003cTrace ID\u003e`\nHTTP headers into all requests which get propagated by Jetty servers into subsequent client invocations.\n\n#### Endpoints returning plain strings\n\nEndpoints returning plain strings should produce media type `text/plain`. Return type `Optional\u003cString\u003e` is only\nsupported for media type `application/json`.\n\n#### Quality of service: retry, failover, throttling, backpressure\n\nFlow control in Conjure is a collaborative effort between servers and clients.\n\nServers advertise an overloaded state using 429/503 responses, which clients interpret by throttling the number of in-flight requests they will send (currently according to an additive increase, multiplicative decrease based algorithm). Requests are retried a fixed number of times, scheduled with an exponential backoff algorithm.\n\nConcurrency permits are only released when the response body is closed, so large streaming responses are correctly tracked.\n\nconjure-java-runtime servers can use the `QosException` class to advertise the following conditions:\n\n* `throttle`: Returns a `Throttle` exception indicating that the calling\n  client should throttle its requests.  The client may retry against an arbitrary node of this service.\n* `retryOther`: Returns a `RetryOther` exception indicating that the calling client should retry against the\n  given node of this service.\n* `unavailable`: An exception indicating that (this node of) this service is currently unavailable and the client\n  may try again at a later time, possibly against a different node of this service.\n\nThe `QosExceptions` have a stable mapping to HTTP status codes and response headers:\n* `throttle`: 429 Too Many Requests, plus optional `Retry-After` header\n* `retryOther`: 308 Permanent Redirect, plus `Location` header indicating the target host\n* `unavailable`: 503 Unavailable\n\nconjure-java-runtime clients handle the above error codes and take the appropriate action:\n* `throttle`: reschedule the request with a delay: either the indicated `Retry-After` period, or a configured\n  exponential backoff\n* `retryOther`: retry the request against the indicated service node; all request parameters and headers are maintained\n* `unavailable`: retry the request on a different host after a configurable exponential delay\n\nAdditionally, connection errors (e.g., `connection refused` or DNS errors) yield a retry against a different node of the\nservice. Retries pick a target host by cycling through the list of URLs configured for a Service (see\n`ClientConfiguration#uris`). Note that the \"current\" URL is maintained across calls; for example, if a first call yields\na `retryOther`/308 redirect, then any subsequent calls will be made against that URL. Similarly, if the first URL yields\na DNS error and the retried call succeeds against the URL from the list, then subsequent calls are made against that URL.\n\nThe number of retries for `503` and connection errors can be configured via `ClientConfiguration#maxNumRetries` or\n`ServiceConfiguration#maxNumRetries`, defaulting to 4.\n\n#### Metrics\n\nThe `HostMetricsRegistry` uses `HostMetrics` to track per-host response metrics. `HostMetrics` provides the following metrics:\n- `get1xx()`: A timer of 1xx responses.\n- `get2xx()`: A timer of 2xx responses.\n- `get3xx()`: A timer of 3xx responses.\n- `get4xx()`: A timer of 4xx responses, excluding 429s.\n- `get5xx()`: A timer of 5xx responses, excluding 503s.\n- `getQos()`: A timer of 429 and 503 responses.\n- `getOther()`: A timer of all other responses.\n- `getIoExceptions()`: A timer of all failed requests.\n\n# License\nThis repository is made available under the [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpalantir%2Fconjure-java-runtime","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpalantir%2Fconjure-java-runtime","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpalantir%2Fconjure-java-runtime/lists"}