From 016a630dd8d0ae9aa3b6188b74a9fd34498fb3e7 Mon Sep 17 00:00:00 2001 From: Patrick Koenig Date: Thu, 26 Mar 2026 15:37:55 -0400 Subject: [PATCH 1/6] Vendor Feign --- conjure-java-jaxrs-client/build.gradle | 7 - .../src/main/java/feign/Body.java | 42 ++ .../src/main/java/feign/Client.java | 175 +++++ .../feign/ConjureCborDelegateDecoder.java | 43 -- .../feign/ConjureCborDelegateEncoder.java | 46 -- .../feign/ConjureEmptyContainerDecoder.java | 43 -- .../ConjureGuavaOptionalAwareDecoder.java | 42 -- .../ConjureInputStreamDelegateDecoder.java | 41 -- .../ConjureInputStreamDelegateEncoder.java | 41 -- .../ConjureJava8OptionalAwareDecoder.java | 42 -- .../feign/ConjureNeverReturnNullDecoder.java | 41 -- .../feign/ConjureTextDelegateEncoder.java | 42 -- .../src/main/java/feign/Contract.java | 327 +++++++++ .../main/java/feign/DefaultMethodHandler.java | 77 ++ .../src/main/java/feign/Feign.java | 206 ++++++ .../src/main/java/feign/FeignException.java | 68 ++ .../src/main/java/feign/HeaderMap.java | 70 ++ .../src/main/java/feign/Headers.java | 69 ++ .../java/feign/InvocationHandlerFactory.java | 45 ++ .../src/main/java/feign/Logger.java | 217 ++++++ .../src/main/java/feign/MethodMetadata.java | 159 +++++ .../src/main/java/feign/Param.java | 56 ++ .../src/main/java/feign/QueryMap.java | 65 ++ .../src/main/java/feign/ReflectiveFeign.java | 364 ++++++++++ .../src/main/java/feign/Request.java | 135 ++++ .../main/java/feign/RequestInterceptor.java | 44 ++ .../src/main/java/feign/RequestLine.java | 68 ++ .../src/main/java/feign/RequestTemplate.java | 658 ++++++++++++++++++ .../src/main/java/feign/Response.java | 262 +++++++ .../main/java/feign/RetryableException.java | 54 ++ .../src/main/java/feign/Retryer.java | 115 +++ .../java/feign/SynchronousMethodHandler.java | 195 ++++++ .../src/main/java/feign/Target.java | 193 +++++ .../src/main/java/feign/Types.java | 459 ++++++++++++ .../src/main/java/feign/Util.java | 321 +++++++++ .../src/main/java/feign/auth/Base64.java | 160 +++++ .../auth/BasicAuthRequestInterceptor.java | 69 ++ .../java/feign/codec/DecodeException.java | 45 ++ .../src/main/java/feign/codec/Decoder.java | 87 +++ .../java/feign/codec/EncodeException.java | 45 ++ .../src/main/java/feign/codec/Encoder.java | 93 +++ .../main/java/feign/codec/ErrorDecoder.java | 151 ++++ .../StringDecoder.java} | 34 +- .../java/feign/jackson/JacksonDecoder.java | 72 ++ .../java/feign/jackson/JacksonEncoder.java | 58 ++ versions.lock | 8 +- versions.props | 1 - 47 files changed, 5240 insertions(+), 415 deletions(-) create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/Body.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/Client.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/feign/ConjureCborDelegateDecoder.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/feign/ConjureCborDelegateEncoder.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/feign/ConjureEmptyContainerDecoder.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/feign/ConjureGuavaOptionalAwareDecoder.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/feign/ConjureInputStreamDelegateDecoder.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/feign/ConjureInputStreamDelegateEncoder.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/feign/ConjureJava8OptionalAwareDecoder.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/feign/ConjureNeverReturnNullDecoder.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/feign/ConjureTextDelegateEncoder.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/Contract.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/DefaultMethodHandler.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/Feign.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/FeignException.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/HeaderMap.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/Headers.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/InvocationHandlerFactory.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/Logger.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/MethodMetadata.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/Param.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/QueryMap.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/ReflectiveFeign.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/Request.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/RequestInterceptor.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/RequestLine.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/RequestTemplate.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/Response.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/RetryableException.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/Retryer.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/SynchronousMethodHandler.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/Target.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/Types.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/Util.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/auth/Base64.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/auth/BasicAuthRequestInterceptor.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/codec/DecodeException.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/codec/Decoder.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/codec/EncodeException.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/codec/Encoder.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/codec/ErrorDecoder.java rename conjure-java-jaxrs-client/src/main/java/feign/{ConjureTextDelegateDecoder.java => codec/StringDecoder.java} (54%) create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/jackson/JacksonDecoder.java create mode 100644 conjure-java-jaxrs-client/src/main/java/feign/jackson/JacksonEncoder.java diff --git a/conjure-java-jaxrs-client/build.gradle b/conjure-java-jaxrs-client/build.gradle index 257c6a170..39c9babfa 100644 --- a/conjure-java-jaxrs-client/build.gradle +++ b/conjure-java-jaxrs-client/build.gradle @@ -7,11 +7,6 @@ dependencies { api project(":client-config") api project(":conjure-java-legacy-clients") api "com.google.code.findbugs:jsr305" - // TODO(dsanduleac): Should be implementation, but can't because we expose feign.TextDelegateEncoder - api "com.netflix.feign:feign-core", { - // prefer jakarta.ws.rs:jakarta.ws.rs-api - exclude group: 'javax.ws.rs', module: 'javax.ws.rs-api' - } api "com.palantir.dialogue:dialogue-target" implementation project(":conjure-java-annotations") @@ -35,11 +30,9 @@ dependencies { implementation project(":conjure-java-jackson-serialization") implementation "com.google.guava:guava" implementation "com.github.ben-manes.caffeine:caffeine" - implementation "com.netflix.feign:feign-jackson" testImplementation project(":conjure-java-jersey-jakarta-server") testImplementation project(':keystores') testImplementation project(':undertow-jakarta-testing') - testImplementation "com.netflix.feign:feign-jackson" testImplementation "com.squareup.okhttp3:mockwebserver" testImplementation "org.junit.jupiter:junit-jupiter" testImplementation 'org.junit.jupiter:junit-jupiter-api' diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Body.java b/conjure-java-jaxrs-client/src/main/java/feign/Body.java new file mode 100644 index 000000000..67703222c --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/Body.java @@ -0,0 +1,42 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package feign; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.util.Map; + +/** + * A possibly templated body of a PUT or POST command. variables wrapped in curly braces are + * expanded before the request is submitted.
ex.
+ *
+ * @Body("<v01:getResourceRecordsOfZone><zoneName>{zoneName}</zoneName><rrType>0</rrType></v01:getResourceRecordsOfZone>")
+ * List<Record> listByZone(@Param("zoneName") String zoneName);
+ * 
+ *
Note that if you'd like curly braces literally in the body, urlencode them first. + * + * @see RequestTemplate#expand(String, Map) + */ +@Target(METHOD) +@Retention(RUNTIME) +public @interface Body { + + String value(); +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Client.java b/conjure-java-jaxrs-client/src/main/java/feign/Client.java new file mode 100644 index 000000000..9a3a66c0a --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/Client.java @@ -0,0 +1,175 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign; + +import static feign.Util.CONTENT_ENCODING; +import static feign.Util.CONTENT_LENGTH; +import static feign.Util.ENCODING_DEFLATE; +import static feign.Util.ENCODING_GZIP; +import static java.lang.String.format; + +import feign.Request.Options; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.GZIPOutputStream; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLSocketFactory; + +/** + * Submits HTTP {@link Request requests}. Implementations are expected to be thread-safe. + */ +public interface Client { + + /** + * Executes a request against its {@link Request#url() url} and returns a response. + * + * @param request safe to replay. + * @param options options to apply to this request. + * @return connected response, {@link Response.Body} is absent or unread. + * @throws IOException on a network error connecting to {@link Request#url()}. + */ + Response execute(Request request, Options options) throws IOException; + + public static class Default implements Client { + + private final SSLSocketFactory sslContextFactory; + private final HostnameVerifier hostnameVerifier; + + /** + * Null parameters imply platform defaults. + */ + public Default(SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier) { + this.sslContextFactory = sslContextFactory; + this.hostnameVerifier = hostnameVerifier; + } + + @Override + public Response execute(Request request, Options options) throws IOException { + HttpURLConnection connection = convertAndSend(request, options); + return convertResponse(connection); + } + + HttpURLConnection convertAndSend(Request request, Options options) throws IOException { + final HttpURLConnection connection = (HttpURLConnection) new URL(request.url()).openConnection(); + if (connection instanceof HttpsURLConnection) { + HttpsURLConnection sslCon = (HttpsURLConnection) connection; + if (sslContextFactory != null) { + sslCon.setSSLSocketFactory(sslContextFactory); + } + if (hostnameVerifier != null) { + sslCon.setHostnameVerifier(hostnameVerifier); + } + } + connection.setConnectTimeout(options.connectTimeoutMillis()); + connection.setReadTimeout(options.readTimeoutMillis()); + connection.setAllowUserInteraction(false); + connection.setInstanceFollowRedirects(true); + connection.setRequestMethod(request.method()); + + Collection contentEncodingValues = request.headers().get(CONTENT_ENCODING); + boolean gzipEncodedRequest = contentEncodingValues != null && contentEncodingValues.contains(ENCODING_GZIP); + boolean deflateEncodedRequest = + contentEncodingValues != null && contentEncodingValues.contains(ENCODING_DEFLATE); + + boolean hasAcceptHeader = false; + Integer contentLength = null; + for (String field : request.headers().keySet()) { + if (field.equalsIgnoreCase("Accept")) { + hasAcceptHeader = true; + } + for (String value : request.headers().get(field)) { + if (field.equals(CONTENT_LENGTH)) { + if (!gzipEncodedRequest && !deflateEncodedRequest) { + contentLength = Integer.valueOf(value); + connection.addRequestProperty(field, value); + } + } else { + connection.addRequestProperty(field, value); + } + } + } + // Some servers choke on the default accept string. + if (!hasAcceptHeader) { + connection.addRequestProperty("Accept", "*/*"); + } + + if (request.body() != null) { + if (contentLength != null) { + connection.setFixedLengthStreamingMode(contentLength); + } else { + connection.setChunkedStreamingMode(8196); + } + connection.setDoOutput(true); + OutputStream out = connection.getOutputStream(); + if (gzipEncodedRequest) { + out = new GZIPOutputStream(out); + } else if (deflateEncodedRequest) { + out = new DeflaterOutputStream(out); + } + try { + out.write(request.body()); + } finally { + try { + out.close(); + } catch (IOException suppressed) { // NOPMD + } + } + } + return connection; + } + + Response convertResponse(HttpURLConnection connection) throws IOException { + int status = connection.getResponseCode(); + String reason = connection.getResponseMessage(); + + if (status < 0) { + throw new IOException(format( + "Invalid status(%s) executing %s %s", + status, connection.getRequestMethod(), connection.getURL())); + } + + Map> headers = new LinkedHashMap>(); + for (Map.Entry> field : + connection.getHeaderFields().entrySet()) { + // response message + if (field.getKey() != null) { + headers.put(field.getKey(), field.getValue()); + } + } + + Integer length = connection.getContentLength(); + if (length == -1) { + length = null; + } + InputStream stream; + if (status >= 400) { + stream = connection.getErrorStream(); + } else { + stream = connection.getInputStream(); + } + return Response.create(status, reason, headers, stream, length); + } + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/ConjureCborDelegateDecoder.java b/conjure-java-jaxrs-client/src/main/java/feign/ConjureCborDelegateDecoder.java deleted file mode 100644 index 1533d7066..000000000 --- a/conjure-java-jaxrs-client/src/main/java/feign/ConjureCborDelegateDecoder.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package feign; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.palantir.conjure.java.client.jaxrs.feignimpl.CborDelegateDecoder; -import feign.codec.Decoder; -import java.io.IOException; -import java.lang.reflect.Type; - -/** - * Use {@link CborDelegateDecoder}. - * - * @deprecated Use {@link CborDelegateDecoder}. - */ -@Deprecated -public final class ConjureCborDelegateDecoder implements Decoder { - - private final CborDelegateDecoder delegate; - - public ConjureCborDelegateDecoder(ObjectMapper cborMapper, Decoder delegate) { - this.delegate = new CborDelegateDecoder(cborMapper, delegate); - } - - @Override - public Object decode(Response response, Type type) throws IOException, FeignException { - return delegate.decode(response, type); - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/ConjureCborDelegateEncoder.java b/conjure-java-jaxrs-client/src/main/java/feign/ConjureCborDelegateEncoder.java deleted file mode 100644 index 7192e4ffc..000000000 --- a/conjure-java-jaxrs-client/src/main/java/feign/ConjureCborDelegateEncoder.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package feign; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.palantir.conjure.java.client.jaxrs.feignimpl.CborDelegateEncoder; -import feign.codec.EncodeException; -import feign.codec.Encoder; -import java.lang.reflect.Type; - -/** - * Use {@link CborDelegateEncoder}. - * - * @deprecated Use {@link CborDelegateEncoder}. - */ -@Deprecated -public final class ConjureCborDelegateEncoder implements Encoder { - - @SuppressWarnings("unused") // public API - public static final String MIME_TYPE = CborDelegateEncoder.MIME_TYPE; - - private final Encoder delegate; - - public ConjureCborDelegateEncoder(ObjectMapper cborMapper, Encoder delegate) { - this.delegate = new CborDelegateEncoder(cborMapper, delegate); - } - - @Override - public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException { - delegate.encode(object, bodyType, template); - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/ConjureEmptyContainerDecoder.java b/conjure-java-jaxrs-client/src/main/java/feign/ConjureEmptyContainerDecoder.java deleted file mode 100644 index c3d4e88cd..000000000 --- a/conjure-java-jaxrs-client/src/main/java/feign/ConjureEmptyContainerDecoder.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * (c) Copyright 2018 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package feign; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.palantir.conjure.java.client.jaxrs.feignimpl.EmptyContainerDecoder; -import feign.codec.Decoder; -import java.io.IOException; -import java.lang.reflect.Type; - -/** - * Use {@link EmptyContainerDecoder}. - * - * @deprecated Use {@link EmptyContainerDecoder}. - */ -@Deprecated -public final class ConjureEmptyContainerDecoder implements Decoder { - - private final Decoder delegate; - - public ConjureEmptyContainerDecoder(ObjectMapper mapper, Decoder delegate) { - this.delegate = new EmptyContainerDecoder(mapper, delegate); - } - - @Override - public Object decode(Response response, Type type) throws IOException { - return delegate.decode(response, type); - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/ConjureGuavaOptionalAwareDecoder.java b/conjure-java-jaxrs-client/src/main/java/feign/ConjureGuavaOptionalAwareDecoder.java deleted file mode 100644 index 81d0ad267..000000000 --- a/conjure-java-jaxrs-client/src/main/java/feign/ConjureGuavaOptionalAwareDecoder.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package feign; - -import com.palantir.conjure.java.client.jaxrs.feignimpl.GuavaOptionalAwareDecoder; -import feign.codec.Decoder; -import java.io.IOException; -import java.lang.reflect.Type; - -/** - * Use {@link GuavaOptionalAwareDecoder}. - * - * @deprecated Use {@link GuavaOptionalAwareDecoder}. - */ -@Deprecated -public final class ConjureGuavaOptionalAwareDecoder implements Decoder { - - private final Decoder delegate; - - public ConjureGuavaOptionalAwareDecoder(Decoder delegate) { - this.delegate = new GuavaOptionalAwareDecoder(delegate); - } - - @Override - public Object decode(Response response, Type type) throws IOException, FeignException { - return delegate.decode(response, type); - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/ConjureInputStreamDelegateDecoder.java b/conjure-java-jaxrs-client/src/main/java/feign/ConjureInputStreamDelegateDecoder.java deleted file mode 100644 index 1cc65ce4a..000000000 --- a/conjure-java-jaxrs-client/src/main/java/feign/ConjureInputStreamDelegateDecoder.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package feign; - -import com.palantir.conjure.java.client.jaxrs.feignimpl.InputStreamDelegateDecoder; -import feign.codec.Decoder; -import java.io.IOException; -import java.lang.reflect.Type; - -/** - * Use {@link InputStreamDelegateDecoder}. - * - * @deprecated Use {@link InputStreamDelegateDecoder}. - */ -@Deprecated -public final class ConjureInputStreamDelegateDecoder implements Decoder { - private final Decoder delegate; - - public ConjureInputStreamDelegateDecoder(Decoder delegate) { - this.delegate = new InputStreamDelegateDecoder("unknown", delegate); - } - - @Override - public Object decode(Response response, Type type) throws IOException, FeignException { - return delegate.decode(response, type); - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/ConjureInputStreamDelegateEncoder.java b/conjure-java-jaxrs-client/src/main/java/feign/ConjureInputStreamDelegateEncoder.java deleted file mode 100644 index 326209c5b..000000000 --- a/conjure-java-jaxrs-client/src/main/java/feign/ConjureInputStreamDelegateEncoder.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package feign; - -import com.palantir.conjure.java.client.jaxrs.feignimpl.InputStreamDelegateEncoder; -import feign.codec.EncodeException; -import feign.codec.Encoder; -import java.lang.reflect.Type; - -/** - * Use {@link InputStreamDelegateEncoder}. - * - * @deprecated Use {@link InputStreamDelegateEncoder}. - */ -@Deprecated -public final class ConjureInputStreamDelegateEncoder implements Encoder { - private final Encoder delegate; - - public ConjureInputStreamDelegateEncoder(Encoder delegate) { - this.delegate = new InputStreamDelegateEncoder("unknown", delegate); - } - - @Override - public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException { - delegate.encode(object, bodyType, template); - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/ConjureJava8OptionalAwareDecoder.java b/conjure-java-jaxrs-client/src/main/java/feign/ConjureJava8OptionalAwareDecoder.java deleted file mode 100644 index 4084addb6..000000000 --- a/conjure-java-jaxrs-client/src/main/java/feign/ConjureJava8OptionalAwareDecoder.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package feign; - -import com.palantir.conjure.java.client.jaxrs.feignimpl.Java8OptionalAwareDecoder; -import feign.codec.Decoder; -import java.io.IOException; -import java.lang.reflect.Type; - -/** - * Use {@link Java8OptionalAwareDecoder}. - * - * @deprecated Use {@link Java8OptionalAwareDecoder}. - */ -@Deprecated -public final class ConjureJava8OptionalAwareDecoder implements Decoder { - - private final Decoder delegate; - - public ConjureJava8OptionalAwareDecoder(Decoder delegate) { - this.delegate = new Java8OptionalAwareDecoder(delegate); - } - - @Override - public Object decode(Response response, Type type) throws IOException, FeignException { - return delegate.decode(response, type); - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/ConjureNeverReturnNullDecoder.java b/conjure-java-jaxrs-client/src/main/java/feign/ConjureNeverReturnNullDecoder.java deleted file mode 100644 index 90e61fe0b..000000000 --- a/conjure-java-jaxrs-client/src/main/java/feign/ConjureNeverReturnNullDecoder.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * (c) Copyright 2018 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package feign; - -import com.palantir.conjure.java.client.jaxrs.feignimpl.NeverReturnNullDecoder; -import feign.codec.Decoder; -import java.io.IOException; -import java.lang.reflect.Type; - -/** - * Use {@link NeverReturnNullDecoder}. - * - * @deprecated Use {@link NeverReturnNullDecoder}. - */ -@Deprecated -public final class ConjureNeverReturnNullDecoder implements Decoder { - private final Decoder delegate; - - public ConjureNeverReturnNullDecoder(Decoder delegate) { - this.delegate = new NeverReturnNullDecoder(delegate); - } - - @Override - public Object decode(Response response, Type type) throws FeignException, IOException { - return delegate.decode(response, type); - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/ConjureTextDelegateEncoder.java b/conjure-java-jaxrs-client/src/main/java/feign/ConjureTextDelegateEncoder.java deleted file mode 100644 index f9071af9f..000000000 --- a/conjure-java-jaxrs-client/src/main/java/feign/ConjureTextDelegateEncoder.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package feign; - -import com.palantir.conjure.java.client.jaxrs.feignimpl.TextDelegateEncoder; -import feign.codec.EncodeException; -import feign.codec.Encoder; -import java.lang.reflect.Type; - -/** - * Use {@link TextDelegateEncoder}. - * - * @deprecated Use {@link TextDelegateEncoder}. - */ -@Deprecated -public final class ConjureTextDelegateEncoder implements Encoder { - - private final Encoder delegate; - - public ConjureTextDelegateEncoder(Encoder delegate) { - this.delegate = new TextDelegateEncoder(delegate); - } - - @Override - public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException { - delegate.encode(object, bodyType, template); - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Contract.java b/conjure-java-jaxrs-client/src/main/java/feign/Contract.java new file mode 100644 index 000000000..8fd534f4a --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/Contract.java @@ -0,0 +1,327 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign; + +import static feign.Util.checkState; +import static feign.Util.emptyToNull; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.URI; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Defines what annotations and values are valid on interfaces. + */ +public interface Contract { + + /** + * Called to parse the methods in the class that are linked to HTTP requests. + * + * @param targetType {@link Target#type() type} of the Feign interface. + */ + // TODO: break this and correct spelling at some point + List parseAndValidatateMetadata(Class targetType); + + abstract class BaseContract implements Contract { + + @Override + public List parseAndValidatateMetadata(Class targetType) { + checkState( + targetType.getTypeParameters().length == 0, + "Parameterized types unsupported: %s", + targetType.getSimpleName()); + checkState( + targetType.getInterfaces().length <= 1, + "Only single inheritance supported: %s", + targetType.getSimpleName()); + if (targetType.getInterfaces().length == 1) { + checkState( + targetType.getInterfaces()[0].getInterfaces().length == 0, + "Only single-level inheritance supported: %s", + targetType.getSimpleName()); + } + Map result = new LinkedHashMap(); + for (Method method : targetType.getMethods()) { + if (method.getDeclaringClass() == Object.class + || (method.getModifiers() & Modifier.STATIC) != 0 + || Util.isDefault(method)) { + continue; + } + MethodMetadata metadata = parseAndValidateMetadata(targetType, method); + checkState( + !result.containsKey(metadata.configKey()), "Overrides unsupported: %s", metadata.configKey()); + result.put(metadata.configKey(), metadata); + } + return new ArrayList(result.values()); + } + + /** + * @deprecated use {@link #parseAndValidateMetadata(Class, Method)} instead. + */ + @Deprecated + public MethodMetadata parseAndValidatateMetadata(Method method) { + return parseAndValidateMetadata(method.getDeclaringClass(), method); + } + + /** + * Called indirectly by {@link #parseAndValidatateMetadata(Class)}. + */ + protected MethodMetadata parseAndValidateMetadata(Class targetType, Method method) { + MethodMetadata data = new MethodMetadata(); + data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType())); + data.configKey(Feign.configKey(targetType, method)); + + if (targetType.getInterfaces().length == 1) { + processAnnotationOnClass(data, targetType.getInterfaces()[0]); + } + processAnnotationOnClass(data, targetType); + + for (Annotation methodAnnotation : method.getAnnotations()) { + processAnnotationOnMethod(data, methodAnnotation, method); + } + checkState( + data.template().method() != null, + "Method %s not annotated with HTTP method type (ex. GET, POST)", + method.getName()); + Class[] parameterTypes = method.getParameterTypes(); + + Annotation[][] parameterAnnotations = method.getParameterAnnotations(); + int count = parameterAnnotations.length; + for (int i = 0; i < count; i++) { + boolean isHttpAnnotation = false; + if (parameterAnnotations[i] != null) { + isHttpAnnotation = processAnnotationsOnParameter(data, parameterAnnotations[i], i); + } + if (parameterTypes[i] == URI.class) { + data.urlIndex(i); + } else if (!isHttpAnnotation) { + checkState(data.formParams().isEmpty(), "Body parameters cannot be used with form parameters."); + checkState(data.bodyIndex() == null, "Method has too many Body parameters: %s", method); + data.bodyIndex(i); + data.bodyType(Types.resolve(targetType, targetType, method.getGenericParameterTypes()[i])); + } + } + + if (data.headerMapIndex() != null) { + checkState( + Map.class.isAssignableFrom(parameterTypes[data.headerMapIndex()]), + "HeaderMap parameter must be a Map: %s", + parameterTypes[data.headerMapIndex()]); + } + + if (data.queryMapIndex() != null) { + checkState( + Map.class.isAssignableFrom(parameterTypes[data.queryMapIndex()]), + "QueryMap parameter must be a Map: %s", + parameterTypes[data.queryMapIndex()]); + } + + return data; + } + + /** + * Called by parseAndValidateMetadata twice, first on the declaring class, then on the + * target type (unless they are the same). + * + * @param data metadata collected so far relating to the current java method. + * @param clz the class to process + */ + protected void processAnnotationOnClass(MethodMetadata data, Class clz) {} + + /** + * @param data metadata collected so far relating to the current java method. + * @param annotation annotations present on the current method annotation. + * @param method method currently being processed. + */ + protected abstract void processAnnotationOnMethod(MethodMetadata data, Annotation annotation, Method method); + + /** + * @param data metadata collected so far relating to the current java method. + * @param annotations annotations present on the current parameter annotation. + * @param paramIndex if you find a name in {@code annotations}, call {@link + * #nameParam(MethodMetadata, String, int)} with this as the last parameter. + * @return true if you called {@link #nameParam(MethodMetadata, String, int)} after finding an + * http-relevant annotation. + */ + protected abstract boolean processAnnotationsOnParameter( + MethodMetadata data, Annotation[] annotations, int paramIndex); + + protected Collection addTemplatedParam(Collection possiblyNull, String name) { + if (possiblyNull == null) { + possiblyNull = new ArrayList(); + } + possiblyNull.add(String.format("{%s}", name)); + return possiblyNull; + } + + /** + * links a parameter name to its index in the method signature. + */ + protected void nameParam(MethodMetadata data, String name, int i) { + Collection names = + data.indexToName().containsKey(i) ? data.indexToName().get(i) : new ArrayList(); + names.add(name); + data.indexToName().put(i, names); + } + } + + class Default extends BaseContract { + @Override + protected void processAnnotationOnClass(MethodMetadata data, Class targetType) { + if (targetType.isAnnotationPresent(Headers.class)) { + String[] headersOnType = targetType.getAnnotation(Headers.class).value(); + checkState(headersOnType.length > 0, "Headers annotation was empty on type %s.", targetType.getName()); + Map> headers = toMap(headersOnType); + headers.putAll(data.template().headers()); + data.template().headers(null); // to clear + data.template().headers(headers); + } + } + + @Override + protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, Method method) { + Class annotationType = methodAnnotation.annotationType(); + if (annotationType == RequestLine.class) { + String requestLine = RequestLine.class.cast(methodAnnotation).value(); + checkState( + emptyToNull(requestLine) != null, + "RequestLine annotation was empty on method %s.", + method.getName()); + if (requestLine.indexOf(' ') == -1) { + checkState( + requestLine.indexOf('/') == -1, + "RequestLine annotation didn't start with an HTTP verb on method %s.", + method.getName()); + data.template().method(requestLine); + return; + } + data.template().method(requestLine.substring(0, requestLine.indexOf(' '))); + if (requestLine.indexOf(' ') == requestLine.lastIndexOf(' ')) { + // no HTTP version is ok + data.template().append(requestLine.substring(requestLine.indexOf(' ') + 1)); + } else { + // skip HTTP version + data.template() + .append(requestLine.substring(requestLine.indexOf(' ') + 1, requestLine.lastIndexOf(' '))); + } + + data.template() + .decodeSlash(RequestLine.class.cast(methodAnnotation).decodeSlash()); + + } else if (annotationType == Body.class) { + String body = Body.class.cast(methodAnnotation).value(); + checkState(emptyToNull(body) != null, "Body annotation was empty on method %s.", method.getName()); + if (body.indexOf('{') == -1) { + data.template().body(body); + } else { + data.template().bodyTemplate(body); + } + } else if (annotationType == Headers.class) { + String[] headersOnMethod = Headers.class.cast(methodAnnotation).value(); + checkState(headersOnMethod.length > 0, "Headers annotation was empty on method %s.", method.getName()); + data.template().headers(toMap(headersOnMethod)); + } + } + + @Override + protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) { + boolean isHttpAnnotation = false; + for (Annotation annotation : annotations) { + Class annotationType = annotation.annotationType(); + if (annotationType == Param.class) { + String name = ((Param) annotation).value(); + checkState(emptyToNull(name) != null, "Param annotation was empty on param %s.", paramIndex); + nameParam(data, name, paramIndex); + if (annotationType == Param.class) { + Class expander = ((Param) annotation).expander(); + if (expander != Param.ToStringExpander.class) { + data.indexToExpanderClass().put(paramIndex, expander); + } + } + isHttpAnnotation = true; + String varName = '{' + name + '}'; + if (data.template().url().indexOf(varName) == -1 + && !searchMapValuesContainsExact(data.template().queries(), varName) + && !searchMapValuesContainsSubstring(data.template().headers(), varName)) { + data.formParams().add(name); + } + } else if (annotationType == QueryMap.class) { + checkState(data.queryMapIndex() == null, "QueryMap annotation was present on multiple parameters."); + data.queryMapIndex(paramIndex); + data.queryMapEncoded(QueryMap.class.cast(annotation).encoded()); + isHttpAnnotation = true; + } else if (annotationType == HeaderMap.class) { + checkState( + data.headerMapIndex() == null, "HeaderMap annotation was present on multiple parameters."); + data.headerMapIndex(paramIndex); + isHttpAnnotation = true; + } + } + return isHttpAnnotation; + } + + private static boolean searchMapValuesContainsExact(Map> map, V search) { + Collection> values = map.values(); + if (values == null) { + return false; + } + + for (Collection entry : values) { + if (entry.contains(search)) { + return true; + } + } + + return false; + } + + private static boolean searchMapValuesContainsSubstring(Map> map, String search) { + Collection> values = map.values(); + if (values == null) { + return false; + } + + for (Collection entry : values) { + for (String value : entry) { + if (value.indexOf(search) != -1) { + return true; + } + } + } + + return false; + } + + private static Map> toMap(String[] input) { + Map> result = new LinkedHashMap>(input.length); + for (String header : input) { + int colon = header.indexOf(':'); + String name = header.substring(0, colon); + if (!result.containsKey(name)) { + result.put(name, new ArrayList(1)); + } + result.get(name).add(header.substring(colon + 2)); + } + return result; + } + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/DefaultMethodHandler.java b/conjure-java-jaxrs-client/src/main/java/feign/DefaultMethodHandler.java new file mode 100644 index 000000000..e13501ac2 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/DefaultMethodHandler.java @@ -0,0 +1,77 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package feign; + +import feign.InvocationHandlerFactory.MethodHandler; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import org.jvnet.animal_sniffer.IgnoreJRERequirement; + +/** + * Handles default methods by directly invoking the default method code on the interface. + * The bindTo method must be called on the result before invoke is called. + */ +@IgnoreJRERequirement +final class DefaultMethodHandler implements MethodHandler { + // Uses Java 7 MethodHandle based reflection. As default methods will only exist when + // run on a Java 8 JVM this will not affect use on legacy JVMs. + // When Feign upgrades to Java 7, remove the @IgnoreJRERequirement annotation. + private final MethodHandle unboundHandle; + + // handle is effectively final after bindTo has been called. + private MethodHandle handle; + + public DefaultMethodHandler(Method defaultMethod) { + try { + Class declaringClass = defaultMethod.getDeclaringClass(); + Field field = Lookup.class.getDeclaredField("IMPL_LOOKUP"); + field.setAccessible(true); + Lookup lookup = (Lookup) field.get(null); + + this.unboundHandle = lookup.unreflectSpecial(defaultMethod, declaringClass); + } catch (NoSuchFieldException ex) { + throw new IllegalStateException(ex); + } catch (IllegalAccessException ex) { + throw new IllegalStateException(ex); + } + } + + /** + * Bind this handler to a proxy object. After bound, DefaultMethodHandler#invoke will act as if it was called + * on the proxy object. Must be called once and only once for a given instance of DefaultMethodHandler + */ + public void bindTo(Object proxy) { + if (handle != null) { + throw new IllegalStateException("Attempted to rebind a default method handler that was already bound"); + } + handle = unboundHandle.bindTo(proxy); + } + + /** + * Invoke this method. DefaultMethodHandler#bindTo must be called before the first + * time invoke is called. + */ + @Override + public Object invoke(Object[] argv) throws Throwable { + if (handle == null) { + throw new IllegalStateException("Default method handler invoked before proxy has been bound."); + } + return handle.invokeWithArguments(argv); + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Feign.java b/conjure-java-jaxrs-client/src/main/java/feign/Feign.java new file mode 100644 index 000000000..acd9349e5 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/Feign.java @@ -0,0 +1,206 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign; + +import feign.Logger.NoOpLogger; +import feign.ReflectiveFeign.ParseHandlersByName; +import feign.Request.Options; +import feign.Target.HardCodedTarget; +import feign.codec.Decoder; +import feign.codec.Encoder; +import feign.codec.ErrorDecoder; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +/** + * Feign's purpose is to ease development against http apis that feign restfulness.
In + * implementation, Feign is a {@link Feign#newInstance factory} for generating {@link Target + * targeted} http apis. + */ +public abstract class Feign { + + public static Builder builder() { + return new Builder(); + } + + /** + *
Configuration keys are formatted as unresolved see tags.
For example.
  • {@code Route53}: would match a class such as {@code + * denominator.route53.Route53}
  • {@code Route53#list()}: would match a method such as {@code + * denominator.route53.Route53#list()}
  • {@code Route53#listAt(Marker)}: would match a method + * such as {@code denominator.route53.Route53#listAt(denominator.route53.Marker)}
  • {@code + * Route53#listByNameAndType(String, String)}: would match a method such as {@code + * denominator.route53.Route53#listAt(String, String)}

Note that there is no whitespace + * expected in a key! + * + * @param targetType {@link Target#type() type} of the Feign interface. + * @param method invoked method, present on {@code type} or its super. + */ + public static String configKey(Class targetType, Method method) { + StringBuilder builder = new StringBuilder(); + builder.append(targetType.getSimpleName()); + builder.append('#').append(method.getName()).append('('); + for (Type param : method.getGenericParameterTypes()) { + param = Types.resolve(targetType, targetType, param); + builder.append(Types.getRawType(param).getSimpleName()).append(','); + } + if (method.getParameterTypes().length > 0) { + builder.deleteCharAt(builder.length() - 1); + } + return builder.append(')').toString(); + } + + /** + * @deprecated use {@link #configKey(Class, Method)} instead. + */ + @Deprecated + public static String configKey(Method method) { + return configKey(method.getDeclaringClass(), method); + } + + /** + * Returns a new instance of an HTTP API, defined by annotations in the {@link Feign Contract}, + * for the specified {@code target}. You should cache this result. + */ + public abstract T newInstance(Target target); + + public static class Builder { + + private final List requestInterceptors = new ArrayList(); + private Logger.Level logLevel = Logger.Level.NONE; + private Contract contract = new Contract.Default(); + private Client client = new Client.Default(null, null); + private Retryer retryer = new Retryer.Default(); + private Logger logger = new NoOpLogger(); + private Encoder encoder = new Encoder.Default(); + private Decoder decoder = new Decoder.Default(); + private ErrorDecoder errorDecoder = new ErrorDecoder.Default(); + private Options options = new Options(); + private InvocationHandlerFactory invocationHandlerFactory = new InvocationHandlerFactory.Default(); + private boolean decode404; + + public Builder logLevel(Logger.Level logLevel) { + this.logLevel = logLevel; + return this; + } + + public Builder contract(Contract contract) { + this.contract = contract; + return this; + } + + public Builder client(Client client) { + this.client = client; + return this; + } + + public Builder retryer(Retryer retryer) { + this.retryer = retryer; + return this; + } + + public Builder logger(Logger logger) { + this.logger = logger; + return this; + } + + public Builder encoder(Encoder encoder) { + this.encoder = encoder; + return this; + } + + public Builder decoder(Decoder decoder) { + this.decoder = decoder; + return this; + } + + /** + * This flag indicates that the {@link #decoder(Decoder) decoder} should process responses with + * 404 status, specifically returning null or empty instead of throwing {@link FeignException}. + * + *

All first-party (ex gson) decoders return well-known empty values defined by + * {@link Util#emptyValueOf}. To customize further, wrap an existing + * {@link #decoder(Decoder) decoder} or make your own. + * + *

This flag only works with 404, as opposed to all or arbitrary status codes. This was an + * explicit decision: 404 -> empty is safe, common and doesn't complicate redirection, retry or + * fallback policy. If your server returns a different status for not-found, correct via a + * custom {@link #client(Client) client}. + * + * @since 8.12 + */ + public Builder decode404() { + this.decode404 = true; + return this; + } + + public Builder errorDecoder(ErrorDecoder errorDecoder) { + this.errorDecoder = errorDecoder; + return this; + } + + public Builder options(Options options) { + this.options = options; + return this; + } + + /** + * Adds a single request interceptor to the builder. + */ + public Builder requestInterceptor(RequestInterceptor requestInterceptor) { + this.requestInterceptors.add(requestInterceptor); + return this; + } + + /** + * Sets the full set of request interceptors for the builder, overwriting any previous + * interceptors. + */ + public Builder requestInterceptors(Iterable requestInterceptors) { + this.requestInterceptors.clear(); + for (RequestInterceptor requestInterceptor : requestInterceptors) { + this.requestInterceptors.add(requestInterceptor); + } + return this; + } + + /** + * Allows you to override how reflective dispatch works inside of Feign. + */ + public Builder invocationHandlerFactory(InvocationHandlerFactory invocationHandlerFactory) { + this.invocationHandlerFactory = invocationHandlerFactory; + return this; + } + + public T target(Class apiType, String url) { + return target(new HardCodedTarget(apiType, url)); + } + + public T target(Target target) { + return build().newInstance(target); + } + + public Feign build() { + SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory( + client, retryer, requestInterceptors, logger, logLevel, decode404); + ParseHandlersByName handlersByName = new ParseHandlersByName( + contract, options, encoder, decoder, errorDecoder, synchronousMethodHandlerFactory); + return new ReflectiveFeign(handlersByName, invocationHandlerFactory); + } + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/FeignException.java b/conjure-java-jaxrs-client/src/main/java/feign/FeignException.java new file mode 100644 index 000000000..cd03514b8 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/FeignException.java @@ -0,0 +1,68 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign; + +import static java.lang.String.format; + +import java.io.IOException; + +/** + * Origin exception type for all Http Apis. + */ +public class FeignException extends RuntimeException { + + private static final long serialVersionUID = 0; + private int status; + + protected FeignException(String message, Throwable cause) { + super(message, cause); + } + + protected FeignException(String message) { + super(message); + } + + protected FeignException(int status, String message) { + super(message); + this.status = status; + } + + public int status() { + return this.status; + } + + static FeignException errorReading(Request request, Response ignored, IOException cause) { + return new FeignException( + format("%s reading %s %s", cause.getMessage(), request.method(), request.url()), cause); + } + + public static FeignException errorStatus(String methodKey, Response response) { + String message = format("status %s reading %s", response.status(), methodKey); + try { + if (response.body() != null) { + String body = Util.toString(response.body().asReader()); + message += "; content:\n" + body; + } + } catch (IOException ignored) { // NOPMD + } + return new FeignException(response.status(), message); + } + + static FeignException errorExecuting(Request request, IOException cause) { + return new RetryableException( + format("%s executing %s %s", cause.getMessage(), request.method(), request.url()), cause, null); + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/HeaderMap.java b/conjure-java-jaxrs-client/src/main/java/feign/HeaderMap.java new file mode 100644 index 000000000..16ef4b9cc --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/HeaderMap.java @@ -0,0 +1,70 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package feign; + +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.util.List; +import java.util.Map; + +/** + * A template parameter that can be applied to a Map that contains header + * entries, where the keys are Strings that are the header field names and the + * values are the header field values. The headers specified by the map will be + * applied to the request after all other processing, and will take precedence + * over any previously specified header parameters. + *
+ * This parameter is useful in cases where different header fields and values + * need to be set on an API method on a per-request basis in a thread-safe manner + * and independently of Feign client construction. A concrete example of a case + * like this are custom metadata header fields (e.g. as "x-amz-meta-*" or + * "x-goog-meta-*") where the header field names are dynamic and the range of keys + * cannot be determined a priori. The {@link Headers} annotation does not allow this + * because the header fields that it defines are static (it is not possible to add or + * remove fields on a per-request basis), and doing this using a custom {@link Target} + * or {@link RequestInterceptor} can be cumbersome (it requires more code for per-method + * customization, it is difficult to implement in a thread-safe manner and it requires + * customization when the Feign client for the API is built). + *
+ *

+ * ...
+ * @RequestLine("GET /servers/{serverId}")
+ * void get(@Param("serverId") String serverId, @HeaderMap Map);
+ * ...
+ * 
+ * The annotated parameter must be an instance of {@link Map}, and the keys must + * be Strings. The header field value of a key will be the value of its toString + * method, except in the following cases: + *
+ *
+ *
    + *
  • if the value is null, the value will remain null (rather than converting + * to the String "null") + *
  • if the value is an {@link Iterable}, it is converted to a {@link List} of + * String objects where each value in the list is either null if the original + * value was null or the value's toString representation otherwise. + *
+ *
+ * Once this conversion is applied, the query keys and resulting String values + * follow the same contract as if they were set using + * {@link RequestTemplate#header(String, String...)}. + */ +@Retention(RUNTIME) +@java.lang.annotation.Target(PARAMETER) +public @interface HeaderMap {} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Headers.java b/conjure-java-jaxrs-client/src/main/java/feign/Headers.java new file mode 100644 index 000000000..836767a0f --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/Headers.java @@ -0,0 +1,69 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package feign; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Expands headers supplied in the {@code value}. Variables to the the right of the colon are expanded.
+ *
+ * @Headers("Content-Type: application/xml")
+ * interface SoapApi {
+ * ...
+ * @RequestLine("GET /")
+ * @Headers("Cache-Control: max-age=640000")
+ * ...
+ *
+ * @RequestLine("POST /")
+ * @Headers({
+ *   "X-Foo: Bar",
+ *   "X-Ping: {token}"
+ * }) void post(@Param("token") String token);
+ * ...
+ * 
+ *
Notes: + *
    + *
  • If you'd like curly braces literally in the header, urlencode them first.
  • + *
  • Headers do not overwrite each other. All headers with the same name will be included + * in the request.
  • + *
+ *
Relationship to JAXRS

The following two forms are identical.

Feign: + *
+ * @RequestLine("POST /")
+ * @Headers({
+ *   "X-Ping: {token}"
+ * }) void post(@Named("token") String token);
+ * ...
+ * 
+ *
JAX-RS: + *
+ * @POST @Path("/")
+ * void post(@HeaderParam("X-Ping") String token);
+ * ...
+ * 
+ */ +@Target({METHOD, TYPE}) +@Retention(RUNTIME) +public @interface Headers { + + String[] value(); +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/InvocationHandlerFactory.java b/conjure-java-jaxrs-client/src/main/java/feign/InvocationHandlerFactory.java new file mode 100644 index 000000000..62ef9462f --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/InvocationHandlerFactory.java @@ -0,0 +1,45 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.util.Map; + +/** + * Controls reflective method dispatch. + */ +public interface InvocationHandlerFactory { + + InvocationHandler create(Target target, Map dispatch); + + /** + * Like {@link InvocationHandler#invoke(Object, Method, Object[])}, except for a + * single method. + */ + interface MethodHandler { + + Object invoke(Object[] argv) throws Throwable; + } + + static final class Default implements InvocationHandlerFactory { + + @Override + public InvocationHandler create(Target target, Map dispatch) { + return new ReflectiveFeign.FeignInvocationHandler(target, dispatch); + } + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Logger.java b/conjure-java-jaxrs-client/src/main/java/feign/Logger.java new file mode 100644 index 000000000..89ed2db3b --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/Logger.java @@ -0,0 +1,217 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign; + +import static feign.Util.UTF_8; +import static feign.Util.decodeOrDefault; +import static feign.Util.valuesOrEmpty; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.logging.FileHandler; +import java.util.logging.LogRecord; +import java.util.logging.SimpleFormatter; + +/** + * Simple logging abstraction for debug messages. Adapted from {@code retrofit.RestAdapter.Log}. + */ +public abstract class Logger { + + protected static String methodTag(String configKey) { + return new StringBuilder() + .append('[') + .append(configKey.substring(0, configKey.indexOf('('))) + .append("] ") + .toString(); + } + + /** + * Override to log requests and responses using your own implementation. Messages will be http + * request and response text. + * + * @param configKey value of {@link Feign#configKey(Class, java.lang.reflect.Method)} + * @param format {@link java.util.Formatter format string} + * @param args arguments applied to {@code format} + */ + protected abstract void log(String configKey, String format, Object... args); + + protected void logRequest(String configKey, Level logLevel, Request request) { + log(configKey, "---> %s %s HTTP/1.1", request.method(), request.url()); + if (logLevel.ordinal() >= Level.HEADERS.ordinal()) { + + for (String field : request.headers().keySet()) { + for (String value : valuesOrEmpty(request.headers(), field)) { + log(configKey, "%s: %s", field, value); + } + } + + int bodyLength = 0; + if (request.body() != null) { + bodyLength = request.body().length; + if (logLevel.ordinal() >= Level.FULL.ordinal()) { + String bodyText = request.charset() != null ? new String(request.body(), request.charset()) : null; + log(configKey, ""); // CRLF + log(configKey, "%s", bodyText != null ? bodyText : "Binary data"); + } + } + log(configKey, "---> END HTTP (%s-byte body)", bodyLength); + } + } + + void logRetry(String configKey, Level logLevel) { + log(configKey, "---> RETRYING"); + } + + protected Response logAndRebufferResponse(String configKey, Level logLevel, Response response, long elapsedTime) + throws IOException { + String reason = response.reason() != null && logLevel.compareTo(Level.NONE) > 0 ? " " + response.reason() : ""; + log(configKey, "<--- HTTP/1.1 %s%s (%sms)", response.status(), reason, elapsedTime); + if (logLevel.ordinal() >= Level.HEADERS.ordinal()) { + + for (String field : response.headers().keySet()) { + for (String value : valuesOrEmpty(response.headers(), field)) { + log(configKey, "%s: %s", field, value); + } + } + + int bodyLength = 0; + if (response.body() != null) { + if (logLevel.ordinal() >= Level.FULL.ordinal()) { + log(configKey, ""); // CRLF + } + byte[] bodyData = Util.toByteArray(response.body().asInputStream()); + bodyLength = bodyData.length; + if (logLevel.ordinal() >= Level.FULL.ordinal() && bodyLength > 0) { + log(configKey, "%s", decodeOrDefault(bodyData, UTF_8, "Binary data")); + } + log(configKey, "<--- END HTTP (%s-byte body)", bodyLength); + return Response.create(response.status(), response.reason(), response.headers(), bodyData); + } else { + log(configKey, "<--- END HTTP (%s-byte body)", bodyLength); + } + } + return response; + } + + IOException logIOException(String configKey, Level logLevel, IOException ioe, long elapsedTime) { + log(configKey, "<--- ERROR %s: %s (%sms)", ioe.getClass().getSimpleName(), ioe.getMessage(), elapsedTime); + if (logLevel.ordinal() >= Level.FULL.ordinal()) { + StringWriter sw = new StringWriter(); + ioe.printStackTrace(new PrintWriter(sw)); + log(configKey, sw.toString()); + log(configKey, "<--- END ERROR"); + } + return ioe; + } + + /** + * Controls the level of logging. + */ + public enum Level { + /** + * No logging. + */ + NONE, + /** + * Log only the request method and URL and the response status code and execution time. + */ + BASIC, + /** + * Log the basic information along with request and response headers. + */ + HEADERS, + /** + * Log the headers, body, and metadata for both requests and responses. + */ + FULL + } + + /** + * logs to the category {@link Logger} at {@link java.util.logging.Level#FINE}. + */ + public static class ErrorLogger extends Logger { + @Override + protected void log(String configKey, String format, Object... args) { + System.err.printf(methodTag(configKey) + format + "%n", args); + } + } + + /** + * logs to the category {@link Logger} at {@link java.util.logging.Level#FINE}, if loggable. + */ + public static class JavaLogger extends Logger { + + final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(Logger.class.getName()); + + @Override + protected void logRequest(String configKey, Level logLevel, Request request) { + if (logger.isLoggable(java.util.logging.Level.FINE)) { + super.logRequest(configKey, logLevel, request); + } + } + + @Override + protected Response logAndRebufferResponse(String configKey, Level logLevel, Response response, long elapsedTime) + throws IOException { + if (logger.isLoggable(java.util.logging.Level.FINE)) { + return super.logAndRebufferResponse(configKey, logLevel, response, elapsedTime); + } + return response; + } + + @Override + protected void log(String configKey, String format, Object... args) { + logger.fine(String.format(methodTag(configKey) + format, args)); + } + + /** + * helper that configures jul to sanely log messages at FINE level without additional + * formatting. + */ + public JavaLogger appendToFile(String logfile) { + logger.setLevel(java.util.logging.Level.FINE); + try { + FileHandler handler = new FileHandler(logfile, true); + handler.setFormatter(new SimpleFormatter() { + @Override + public String format(LogRecord record) { + return String.format("%s%n", record.getMessage()); // NOPMD + } + }); + logger.addHandler(handler); + } catch (IOException e) { + throw new IllegalStateException("Could not add file handler.", e); + } + return this; + } + } + + public static class NoOpLogger extends Logger { + + @Override + protected void logRequest(String configKey, Level logLevel, Request request) {} + + @Override + protected Response logAndRebufferResponse(String configKey, Level logLevel, Response response, long elapsedTime) + throws IOException { + return response; + } + + @Override + protected void log(String configKey, String format, Object... args) {} + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/MethodMetadata.java b/conjure-java-jaxrs-client/src/main/java/feign/MethodMetadata.java new file mode 100644 index 000000000..fa6427642 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/MethodMetadata.java @@ -0,0 +1,159 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign; + +import feign.Param.Expander; +import java.io.Serializable; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public final class MethodMetadata implements Serializable { + + private static final long serialVersionUID = 1L; + private String configKey; + private transient Type returnType; + private Integer urlIndex; + private Integer bodyIndex; + private Integer headerMapIndex; + private Integer queryMapIndex; + private boolean queryMapEncoded; + private transient Type bodyType; + private RequestTemplate template = new RequestTemplate(); + private List formParams = new ArrayList(); + private Map> indexToName = new LinkedHashMap>(); + private Map> indexToExpanderClass = + new LinkedHashMap>(); + private transient Map indexToExpander; + + MethodMetadata() {} + + /** + * @see Feign#configKey(Class, java.lang.reflect.Method) + */ + public String configKey() { + return configKey; + } + + public MethodMetadata configKey(String configKey) { + this.configKey = configKey; + return this; + } + + public Type returnType() { + return returnType; + } + + public MethodMetadata returnType(Type returnType) { + this.returnType = returnType; + return this; + } + + public Integer urlIndex() { + return urlIndex; + } + + public MethodMetadata urlIndex(Integer urlIndex) { + this.urlIndex = urlIndex; + return this; + } + + public Integer bodyIndex() { + return bodyIndex; + } + + public MethodMetadata bodyIndex(Integer bodyIndex) { + this.bodyIndex = bodyIndex; + return this; + } + + public Integer headerMapIndex() { + return headerMapIndex; + } + + public MethodMetadata headerMapIndex(Integer headerMapIndex) { + this.headerMapIndex = headerMapIndex; + return this; + } + + public Integer queryMapIndex() { + return queryMapIndex; + } + + public MethodMetadata queryMapIndex(Integer queryMapIndex) { + this.queryMapIndex = queryMapIndex; + return this; + } + + public boolean queryMapEncoded() { + return queryMapEncoded; + } + + public MethodMetadata queryMapEncoded(boolean queryMapEncoded) { + this.queryMapEncoded = queryMapEncoded; + return this; + } + + /** + * Type corresponding to {@link #bodyIndex()}. + */ + public Type bodyType() { + return bodyType; + } + + public MethodMetadata bodyType(Type bodyType) { + this.bodyType = bodyType; + return this; + } + + public RequestTemplate template() { + return template; + } + + public List formParams() { + return formParams; + } + + public Map> indexToName() { + return indexToName; + } + + /** + * If {@link #indexToExpander} is null, classes here will be instantiated by newInstance. + */ + public Map> indexToExpanderClass() { + return indexToExpanderClass; + } + + /** + * After {@link #indexToExpanderClass} is populated, this is set by contracts that support + * runtime injection. + */ + public MethodMetadata indexToExpander(Map indexToExpander) { + this.indexToExpander = indexToExpander; + return this; + } + + /** + * When not null, this value will be used instead of {@link #indexToExpander()}. + */ + public Map indexToExpander() { + return indexToExpander; + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Param.java b/conjure-java-jaxrs-client/src/main/java/feign/Param.java new file mode 100644 index 000000000..f78b64c78 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/Param.java @@ -0,0 +1,56 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign; + +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; + +/** + * A named template parameter applied to {@link Headers}, {@linkplain RequestLine} or {@linkplain + * Body} + */ +@Retention(RUNTIME) +@java.lang.annotation.Target(PARAMETER) +public @interface Param { + + /** + * The name of the template parameter. + */ + String value(); + + /** + * How to expand the value of this parameter, if {@link ToStringExpander} isn't adequate. + */ + Class expander() default ToStringExpander.class; + + interface Expander { + + /** + * Expands the value into a string. Does not accept or return null. + */ + String expand(Object value); + } + + final class ToStringExpander implements Expander { + + @Override + public String expand(Object value) { + return value.toString(); + } + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/QueryMap.java b/conjure-java-jaxrs-client/src/main/java/feign/QueryMap.java new file mode 100644 index 000000000..aa6b70176 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/QueryMap.java @@ -0,0 +1,65 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign; + +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.util.List; +import java.util.Map; + +/** + * A template parameter that can be applied to a Map that contains query + * parameters, where the keys are Strings that are the parameter names and the + * values are the parameter values. The queries specified by the map will be + * applied to the request after all other processing, and will take precedence + * over any previously specified query parameters. It is not necessary to + * reference the parameter map as a variable.
+ *
+ *
+ * ...
+ * @RequestLine("POST /servers")
+ * void servers(@QueryMap Map);
+ * ...
+ *
+ * @RequestLine("GET /servers/{serverId}?count={count}")
+ * void get(@Param("serverId") String serverId, @Param("count") int count, @QueryMap Map);
+ * ...
+ * 
+ * The annotated parameter must be an instance of {@link Map}, and the keys must + * be Strings. The query value of a key will be the value of its toString + * method, except in the following cases: + *
+ *
+ *
    + *
  • if the value is null, the value will remain null (rather than converting + * to the String "null") + *
  • if the value is an {@link Iterable}, it is converted to a {@link List} of + * String objects where each value in the list is either null if the original + * value was null or the value's toString representation otherwise. + *
+ *
+ * Once this conversion is applied, the query keys and resulting String values + * follow the same contract as if they were set using + * {@link RequestTemplate#query(String, String...)}. + */ +@Retention(RUNTIME) +@java.lang.annotation.Target(PARAMETER) +public @interface QueryMap { + /** Specifies whether parameter names and values are already encoded. */ + boolean encoded() default false; +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/ReflectiveFeign.java b/conjure-java-jaxrs-client/src/main/java/feign/ReflectiveFeign.java new file mode 100644 index 000000000..e7bb013af --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/ReflectiveFeign.java @@ -0,0 +1,364 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign; + +import static feign.Util.checkArgument; +import static feign.Util.checkNotNull; +import static feign.Util.checkState; + +import feign.InvocationHandlerFactory.MethodHandler; +import feign.Param.Expander; +import feign.Request.Options; +import feign.codec.Decoder; +import feign.codec.EncodeException; +import feign.codec.Encoder; +import feign.codec.ErrorDecoder; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +public class ReflectiveFeign extends Feign { + + private final ParseHandlersByName targetToHandlersByName; + private final InvocationHandlerFactory factory; + + ReflectiveFeign(ParseHandlersByName targetToHandlersByName, InvocationHandlerFactory factory) { + this.targetToHandlersByName = targetToHandlersByName; + this.factory = factory; + } + + /** + * creates an api binding to the {@code target}. As this invokes reflection, care should be taken + * to cache the result. + */ + @SuppressWarnings("unchecked") + @Override + public T newInstance(Target target) { + Map nameToHandler = targetToHandlersByName.apply(target); + Map methodToHandler = new LinkedHashMap(); + List defaultMethodHandlers = new LinkedList(); + + for (Method method : target.type().getMethods()) { + if (method.getDeclaringClass() == Object.class) { + continue; + } else if (Util.isDefault(method)) { + DefaultMethodHandler handler = new DefaultMethodHandler(method); + defaultMethodHandlers.add(handler); + methodToHandler.put(method, handler); + } else { + methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method))); + } + } + InvocationHandler handler = factory.create(target, methodToHandler); + T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class[] {target.type()}, handler); + + for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) { + defaultMethodHandler.bindTo(proxy); + } + return proxy; + } + + static class FeignInvocationHandler implements InvocationHandler { + + private final Target target; + private final Map dispatch; + + FeignInvocationHandler(Target target, Map dispatch) { + this.target = checkNotNull(target, "target"); + this.dispatch = checkNotNull(dispatch, "dispatch for %s", target); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ("equals".equals(method.getName())) { + try { + Object otherHandler = + args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null; + return equals(otherHandler); + } catch (IllegalArgumentException e) { + return false; + } + } else if ("hashCode".equals(method.getName())) { + return hashCode(); + } else if ("toString".equals(method.getName())) { + return toString(); + } + return dispatch.get(method).invoke(args); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof FeignInvocationHandler) { + FeignInvocationHandler other = (FeignInvocationHandler) obj; + return target.equals(other.target); + } + return false; + } + + @Override + public int hashCode() { + return target.hashCode(); + } + + @Override + public String toString() { + return target.toString(); + } + } + + static final class ParseHandlersByName { + + private final Contract contract; + private final Options options; + private final Encoder encoder; + private final Decoder decoder; + private final ErrorDecoder errorDecoder; + private final SynchronousMethodHandler.Factory factory; + + ParseHandlersByName( + Contract contract, + Options options, + Encoder encoder, + Decoder decoder, + ErrorDecoder errorDecoder, + SynchronousMethodHandler.Factory factory) { + this.contract = contract; + this.options = options; + this.factory = factory; + this.errorDecoder = errorDecoder; + this.encoder = checkNotNull(encoder, "encoder"); + this.decoder = checkNotNull(decoder, "decoder"); + } + + public Map apply(Target key) { + List metadata = contract.parseAndValidatateMetadata(key.type()); + Map result = new LinkedHashMap(); + for (MethodMetadata md : metadata) { + BuildTemplateByResolvingArgs buildTemplate; + if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) { + buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder); + } else if (md.bodyIndex() != null) { + buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder); + } else { + buildTemplate = new BuildTemplateByResolvingArgs(md); + } + result.put(md.configKey(), factory.create(key, md, buildTemplate, options, decoder, errorDecoder)); + } + return result; + } + } + + private static class BuildTemplateByResolvingArgs implements RequestTemplate.Factory { + + protected final MethodMetadata metadata; + private final Map indexToExpander = new LinkedHashMap(); + + private BuildTemplateByResolvingArgs(MethodMetadata metadata) { + this.metadata = metadata; + if (metadata.indexToExpander() != null) { + indexToExpander.putAll(metadata.indexToExpander()); + return; + } + if (metadata.indexToExpanderClass().isEmpty()) { + return; + } + for (Entry> indexToExpanderClass : + metadata.indexToExpanderClass().entrySet()) { + try { + indexToExpander.put( + indexToExpanderClass.getKey(), + indexToExpanderClass.getValue().newInstance()); + } catch (InstantiationException e) { + throw new IllegalStateException(e); + } catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + } + + @Override + public RequestTemplate create(Object[] argv) { + RequestTemplate mutable = new RequestTemplate(metadata.template()); + if (metadata.urlIndex() != null) { + int urlIndex = metadata.urlIndex(); + checkArgument(argv[urlIndex] != null, "URI parameter %s was null", urlIndex); + mutable.insert(0, String.valueOf(argv[urlIndex])); + } + Map varBuilder = new LinkedHashMap(); + for (Entry> entry : + metadata.indexToName().entrySet()) { + int i = entry.getKey(); + Object value = argv[entry.getKey()]; + if (value != null) { // Null values are skipped. + if (indexToExpander.containsKey(i)) { + value = expandElements(indexToExpander.get(i), value); + } + for (String name : entry.getValue()) { + varBuilder.put(name, value); + } + } + } + + RequestTemplate template = resolve(argv, mutable, varBuilder); + if (metadata.queryMapIndex() != null) { + // add query map parameters after initial resolve so that they take + // precedence over any predefined values + template = addQueryMapQueryParameters(argv, template); + } + + if (metadata.headerMapIndex() != null) { + template = addHeaderMapHeaders(argv, template); + } + + return template; + } + + private Object expandElements(Expander expander, Object value) { + if (value instanceof Iterable) { + return expandIterable(expander, (Iterable) value); + } + return expander.expand(value); + } + + private List expandIterable(Expander expander, Iterable value) { + List values = new ArrayList(); + for (Object element : (Iterable) value) { + if (element != null) { + values.add(expander.expand(element)); + } + } + return values; + } + + @SuppressWarnings("unchecked") + private RequestTemplate addHeaderMapHeaders(Object[] argv, RequestTemplate mutable) { + Map headerMap = (Map) argv[metadata.headerMapIndex()]; + for (Entry currEntry : headerMap.entrySet()) { + checkState( + currEntry.getKey().getClass() == String.class, + "HeaderMap key must be a String: %s", + currEntry.getKey()); + + Collection values = new ArrayList(); + + Object currValue = currEntry.getValue(); + if (currValue instanceof Iterable) { + Iterator iter = ((Iterable) currValue).iterator(); + while (iter.hasNext()) { + Object nextObject = iter.next(); + values.add(nextObject == null ? null : nextObject.toString()); + } + } else { + values.add(currValue == null ? null : currValue.toString()); + } + + mutable.header((String) currEntry.getKey(), values); + } + return mutable; + } + + @SuppressWarnings("unchecked") + private RequestTemplate addQueryMapQueryParameters(Object[] argv, RequestTemplate mutable) { + Map queryMap = (Map) argv[metadata.queryMapIndex()]; + for (Entry currEntry : queryMap.entrySet()) { + checkState( + currEntry.getKey().getClass() == String.class, + "QueryMap key must be a String: %s", + currEntry.getKey()); + + Collection values = new ArrayList(); + + Object currValue = currEntry.getValue(); + if (currValue instanceof Iterable) { + Iterator iter = ((Iterable) currValue).iterator(); + while (iter.hasNext()) { + Object nextObject = iter.next(); + values.add(nextObject == null ? null : nextObject.toString()); + } + } else { + values.add(currValue == null ? null : currValue.toString()); + } + + mutable.query(metadata.queryMapEncoded(), (String) currEntry.getKey(), values); + } + return mutable; + } + + protected RequestTemplate resolve(Object[] argv, RequestTemplate mutable, Map variables) { + return mutable.resolve(variables); + } + } + + private static class BuildFormEncodedTemplateFromArgs extends BuildTemplateByResolvingArgs { + + private final Encoder encoder; + + private BuildFormEncodedTemplateFromArgs(MethodMetadata metadata, Encoder encoder) { + super(metadata); + this.encoder = encoder; + } + + @Override + protected RequestTemplate resolve(Object[] argv, RequestTemplate mutable, Map variables) { + Map formVariables = new LinkedHashMap(); + for (Entry entry : variables.entrySet()) { + if (metadata.formParams().contains(entry.getKey())) { + formVariables.put(entry.getKey(), entry.getValue()); + } + } + try { + encoder.encode(formVariables, Encoder.MAP_STRING_WILDCARD, mutable); + } catch (EncodeException e) { + throw e; + } catch (RuntimeException e) { + throw new EncodeException(e.getMessage(), e); + } + return super.resolve(argv, mutable, variables); + } + } + + private static class BuildEncodedTemplateFromArgs extends BuildTemplateByResolvingArgs { + + private final Encoder encoder; + + private BuildEncodedTemplateFromArgs(MethodMetadata metadata, Encoder encoder) { + super(metadata); + this.encoder = encoder; + } + + @Override + protected RequestTemplate resolve(Object[] argv, RequestTemplate mutable, Map variables) { + Object body = argv[metadata.bodyIndex()]; + checkArgument(body != null, "Body parameter %s was null", metadata.bodyIndex()); + try { + encoder.encode(body, metadata.bodyType(), mutable); + } catch (EncodeException e) { + throw e; + } catch (RuntimeException e) { + throw new EncodeException(e.getMessage(), e); + } + return super.resolve(argv, mutable, variables); + } + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Request.java b/conjure-java-jaxrs-client/src/main/java/feign/Request.java new file mode 100644 index 000000000..9aaddf51e --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/Request.java @@ -0,0 +1,135 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign; + +import static feign.Util.checkNotNull; +import static feign.Util.valuesOrEmpty; + +import java.nio.charset.Charset; +import java.util.Collection; +import java.util.Map; + +/** + * An immutable request to an http server. + */ +public final class Request { + + /** + * No parameters can be null except {@code body} and {@code charset}. All parameters must be + * effectively immutable, via safe copies, not mutating or otherwise. + */ + public static Request create( + String method, String url, Map> headers, byte[] body, Charset charset) { + return new Request(method, url, headers, body, charset); + } + + private final String method; + private final String url; + private final Map> headers; + private final byte[] body; + private final Charset charset; + + Request(String method, String url, Map> headers, byte[] body, Charset charset) { + this.method = checkNotNull(method, "method of %s", url); + this.url = checkNotNull(url, "url"); + this.headers = checkNotNull(headers, "headers of %s %s", method, url); + this.body = body; // nullable + this.charset = charset; // nullable + } + + /* Method to invoke on the server. */ + public String method() { + return method; + } + + /* Fully resolved URL including query. */ + public String url() { + return url; + } + + /* Ordered list of headers that will be sent to the server. */ + public Map> headers() { + return headers; + } + + /** + * The character set with which the body is encoded, or null if unknown or not applicable. When + * this is present, you can use {@code new String(req.body(), req.charset())} to access the body + * as a String. + */ + public Charset charset() { + return charset; + } + + /** + * If present, this is the replayable body to send to the server. In some cases, this may be + * interpretable as text. + * + * @see #charset() + */ + public byte[] body() { + return body; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(method).append(' ').append(url).append(" HTTP/1.1\n"); + for (String field : headers.keySet()) { + for (String value : valuesOrEmpty(headers, field)) { + builder.append(field).append(": ").append(value).append('\n'); + } + } + if (body != null) { + builder.append('\n').append(charset != null ? new String(body, charset) : "Binary data"); + } + return builder.toString(); + } + + /* Controls the per-request settings currently required to be implemented by all {@link Client clients} */ + public static class Options { + + private final int connectTimeoutMillis; + private final int readTimeoutMillis; + + public Options(int connectTimeoutMillis, int readTimeoutMillis) { + this.connectTimeoutMillis = connectTimeoutMillis; + this.readTimeoutMillis = readTimeoutMillis; + } + + public Options() { + this(10 * 1000, 60 * 1000); + } + + /** + * Defaults to 10 seconds. {@code 0} implies no timeout. + * + * @see java.net.HttpURLConnection#getConnectTimeout() + */ + public int connectTimeoutMillis() { + return connectTimeoutMillis; + } + + /** + * Defaults to 60 seconds. {@code 0} implies no timeout. + * + * @see java.net.HttpURLConnection#getReadTimeout() + */ + public int readTimeoutMillis() { + return readTimeoutMillis; + } + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/RequestInterceptor.java b/conjure-java-jaxrs-client/src/main/java/feign/RequestInterceptor.java new file mode 100644 index 000000000..b74a0dd36 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/RequestInterceptor.java @@ -0,0 +1,44 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign; + +/** + * Zero or more {@code RequestInterceptors} may be configured for purposes such as adding headers to + * all requests. No guarantees are give with regards to the order that interceptors are applied. + * Once interceptors are applied, {@link Target#apply(RequestTemplate)} is called to create the + * immutable http request sent via {@link Client#execute(Request, Request.Options)}.

+ * For example:
+ *
+ * public void apply(RequestTemplate input) {
+ *     input.replaceHeader("X-Auth", currentToken);
+ * }
+ * 
+ *

Configuration

{@code RequestInterceptors} are configured via {@link + * Feign.Builder#requestInterceptors}.

Implementation notes

Do not add + * parameters, such as {@code /path/{foo}/bar } in your implementation of {@link + * #apply(RequestTemplate)}.
Interceptors are applied after the template's parameters are + * {@link RequestTemplate#resolve(java.util.Map) resolved}. This is to ensure that you can + * implement signatures are interceptors.


Relationship to Retrofit 1.x

+ * This class is similar to {@code RequestInterceptor.intercept()}, except that the implementation + * can read, remove, or otherwise mutate any part of the request template. + */ +public interface RequestInterceptor { + + /** + * Called for every request. Add data using methods on the supplied {@link RequestTemplate}. + */ + void apply(RequestTemplate template); +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/RequestLine.java b/conjure-java-jaxrs-client/src/main/java/feign/RequestLine.java new file mode 100644 index 000000000..ddb7b0259 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/RequestLine.java @@ -0,0 +1,68 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package feign; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; + +/** + * Expands the request-line supplied in the {@code value}, permitting path and query variables, or + * just the http method.
+ *
+ * ...
+ * @RequestLine("POST /servers")
+ * ...
+ *
+ * @RequestLine("GET /servers/{serverId}?count={count}")
+ * void get(@Param("serverId") String serverId, @Param("count") int count);
+ * ...
+ *
+ * @RequestLine("GET")
+ * Response getNext(URI nextLink);
+ * ...
+ * 
+ * HTTP version suffix is optional, but permitted. There are no guarantees this version will impact + * that sent by the client.
+ *
+ * @RequestLine("POST /servers HTTP/1.1")
+ * ...
+ * 
+ *
Note: Query params do not overwrite each other. All queries with the same + * name will be included in the request.

Relationship to JAXRS

The following + * two forms are identical.
Feign: + *
+ * @RequestLine("GET /servers/{serverId}?count={count}")
+ * void get(@Param("serverId") String serverId, @Param("count") int count);
+ * ...
+ * 
+ *
JAX-RS: + *
+ * @GET @Path("/servers/{serverId}")
+ * void get(@PathParam("serverId") String serverId, @QueryParam("count") int count);
+ * ...
+ * 
+ */ +@java.lang.annotation.Target(METHOD) +@Retention(RUNTIME) +public @interface RequestLine { + + String value(); + + boolean decodeSlash() default true; +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/RequestTemplate.java b/conjure-java-jaxrs-client/src/main/java/feign/RequestTemplate.java new file mode 100644 index 000000000..2664bfce6 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/RequestTemplate.java @@ -0,0 +1,658 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign; + +import static feign.Util.CONTENT_LENGTH; +import static feign.Util.UTF_8; +import static feign.Util.checkArgument; +import static feign.Util.checkNotNull; +import static feign.Util.emptyToNull; +import static feign.Util.toArray; +import static feign.Util.valuesOrEmpty; + +import feign.codec.Encoder; +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * Builds a request to an http target. Not thread safe.


relationship to JAXRS + * 2.0

A combination of {@code javax.ws.rs.client.WebTarget} and {@code + * javax.ws.rs.client.Invocation.Builder}, ensuring you can modify any part of the request. However, + * this object is mutable, so needs to be guarded with the copy constructor. + */ +public final class RequestTemplate implements Serializable { + + private static final long serialVersionUID = 1L; + private final Map> queries = new LinkedHashMap>(); + private final Map> headers = new LinkedHashMap>(); + private String method; + /* final to encourage mutable use vs replacing the object. */ + private StringBuilder url = new StringBuilder(); + private transient Charset charset; + private byte[] body; + private String bodyTemplate; + private boolean decodeSlash = true; + + public RequestTemplate() {} + + /* Copy constructor. Use this when making templates. */ + public RequestTemplate(RequestTemplate toCopy) { + checkNotNull(toCopy, "toCopy"); + this.method = toCopy.method; + this.url.append(toCopy.url); + this.queries.putAll(toCopy.queries); + this.headers.putAll(toCopy.headers); + this.charset = toCopy.charset; + this.body = toCopy.body; + this.bodyTemplate = toCopy.bodyTemplate; + this.decodeSlash = toCopy.decodeSlash; + } + + private static String urlDecode(String arg) { + try { + return URLDecoder.decode(arg, UTF_8.name()); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + private static String urlEncode(Object arg) { + try { + return URLEncoder.encode(String.valueOf(arg), UTF_8.name()); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + private static boolean isHttpUrl(CharSequence value) { + return value.length() >= 4 && value.subSequence(0, 3).equals("http".substring(0, 3)); + } + + private static CharSequence removeTrailingSlash(CharSequence charSequence) { + if (charSequence != null + && charSequence.length() > 0 + && charSequence.charAt(charSequence.length() - 1) == '/') { + return charSequence.subSequence(0, charSequence.length() - 1); + } else { + return charSequence; + } + } + + /** + * Expands a {@code template}, such as {@code username}, using the {@code variables} supplied. Any + * unresolved parameters will remain.
Note that if you'd like curly braces literally in the + * {@code template}, urlencode them first. + * + * @param template URI template that can be in level 1 RFC6570 + * form. + * @param variables to the URI template + * @return expanded template, leaving any unresolved parameters literal + */ + public static String expand(String template, Map variables) { + // skip expansion if there's no valid variables set. ex. {a} is the + // first valid + if (checkNotNull(template, "template").length() < 3) { + return template.toString(); + } + checkNotNull(variables, "variables for %s", template); + + boolean inVar = false; + StringBuilder var = new StringBuilder(); + StringBuilder builder = new StringBuilder(); + for (char c : template.toCharArray()) { + switch (c) { + case '{': + if (inVar) { + // '{{' is an escape: write the brace and don't interpret as a variable + builder.append("{"); + inVar = false; + break; + } + inVar = true; + break; + case '}': + if (!inVar) { // then write the brace literally + builder.append('}'); + break; + } + inVar = false; + String key = var.toString(); + Object value = variables.get(var.toString()); + if (value != null) { + builder.append(value); + } else { + builder.append('{').append(key).append('}'); + } + var = new StringBuilder(); + break; + default: + if (inVar) { + var.append(c); + } else { + builder.append(c); + } + } + } + return builder.toString(); + } + + private static Map> parseAndDecodeQueries(String queryLine) { + Map> map = new LinkedHashMap>(); + if (emptyToNull(queryLine) == null) { + return map; + } + if (queryLine.indexOf('&') == -1) { + putKV(queryLine, map); + } else { + char[] chars = queryLine.toCharArray(); + int start = 0; + int i = 0; + for (; i < chars.length; i++) { + if (chars[i] == '&') { + putKV(queryLine.substring(start, i), map); + start = i + 1; + } + } + putKV(queryLine.substring(start, i), map); + } + return map; + } + + private static void putKV(String stringToParse, Map> map) { + String key; + String value; + // note that '=' can be a valid part of the value + int firstEq = stringToParse.indexOf('='); + if (firstEq == -1) { + key = urlDecode(stringToParse); + value = null; + } else { + key = urlDecode(stringToParse.substring(0, firstEq)); + value = urlDecode(stringToParse.substring(firstEq + 1)); + } + Collection values = map.containsKey(key) ? map.get(key) : new ArrayList(); + values.add(value); + map.put(key, values); + } + + /** + * Resolves any template parameters in the requests path, query, or headers against the supplied + * unencoded arguments.


relationship to JAXRS 2.0

This call is + * similar to {@code javax.ws.rs.client.WebTarget.resolveTemplates(templateValues, true)} , except + * that the template values apply to any part of the request, not just the URL + */ + public RequestTemplate resolve(Map unencoded) { + replaceQueryValues(unencoded); + Map encoded = new LinkedHashMap(); + for (Entry entry : unencoded.entrySet()) { + encoded.put(entry.getKey(), urlEncode(String.valueOf(entry.getValue()))); + } + String resolvedUrl = expand(url.toString(), encoded).replace("+", "%20"); + if (decodeSlash) { + resolvedUrl = resolvedUrl.replace("%2F", "/"); + } + url = new StringBuilder(resolvedUrl); + + Map> resolvedHeaders = new LinkedHashMap>(); + for (String field : headers.keySet()) { + Collection resolvedValues = new ArrayList(); + for (String value : valuesOrEmpty(headers, field)) { + String resolved = expand(value, unencoded); + resolvedValues.add(resolved); + } + resolvedHeaders.put(field, resolvedValues); + } + headers.clear(); + headers.putAll(resolvedHeaders); + if (bodyTemplate != null) { + body(urlDecode(expand(bodyTemplate, encoded))); + } + return this; + } + + /* roughly analogous to {@code javax.ws.rs.client.Target.request()}. */ + public Request request() { + Map> safeCopy = new LinkedHashMap>(); + safeCopy.putAll(headers); + return Request.create( + method, + new StringBuilder(url).append(queryLine()).toString(), + Collections.unmodifiableMap(safeCopy), + body, + charset); + } + + /* @see Request#method() */ + public RequestTemplate method(String method) { + this.method = checkNotNull(method, "method"); + checkArgument(method.matches("^[A-Z]+$"), "Invalid HTTP Method: %s", method); + return this; + } + + /* @see Request#method() */ + public String method() { + return method; + } + + public RequestTemplate decodeSlash(boolean decodeSlash) { + this.decodeSlash = decodeSlash; + return this; + } + + public boolean decodeSlash() { + return decodeSlash; + } + + /* @see #url() */ + public RequestTemplate append(CharSequence value) { + url.append(value); + url = pullAnyQueriesOutOfUrl(url); + return this; + } + + /* @see #url() */ + public RequestTemplate insert(int pos, CharSequence value) { + if (isHttpUrl(value)) { + value = removeTrailingSlash(value); + if (url.length() > 0 && url.charAt(0) != '/') { + url.insert(0, '/'); + } + } + url.insert(pos, pullAnyQueriesOutOfUrl(new StringBuilder(value))); + return this; + } + + public String url() { + return url.toString(); + } + + /** + * Replaces queries with the specified {@code name} with the {@code values} supplied. + *
Values can be passed in decoded or in url-encoded form depending on the value of the + * {@code encoded} parameter. + *
When the {@code value} is {@code null}, all queries with the {@code configKey} are + * removed.


relationship to JAXRS 2.0

Like {@code WebTarget.query}, + * except the values can be templatized.
ex.
+ *
+     * template.query("Signature", "{signature}");
+     * 
+ *
Note: behavior of RequestTemplate is not consistent if a query parameter with + * unsafe characters is passed as both encoded and unencoded, although no validation is performed. + *
ex.
+ *
+     * template.query(true, "param[]", "value");
+     * template.query(false, "param[]", "value");
+     * 
+ * + * @param encoded whether name and values are already url-encoded + * @param name the name of the query + * @param values can be a single null to imply removing all values. Else no values are expected + * to be null. + * @see #queries() + */ + public RequestTemplate query(boolean encoded, String name, String... values) { + return doQuery(encoded, name, values); + } + + /* @see #query(boolean, String, String...) */ + public RequestTemplate query(boolean encoded, String name, Iterable values) { + return doQuery(encoded, name, values); + } + + /** + * Shortcut for {@code query(false, String, String...)} + * @see #query(boolean, String, String...) + */ + public RequestTemplate query(String name, String... values) { + return doQuery(false, name, values); + } + + /** + * Shortcut for {@code query(false, String, Iterable)} + * @see #query(boolean, String, String...) + */ + public RequestTemplate query(String name, Iterable values) { + return doQuery(false, name, values); + } + + private RequestTemplate doQuery(boolean encoded, String name, String... values) { + checkNotNull(name, "name"); + String paramName = encoded ? name : encodeIfNotVariable(name); + queries.remove(paramName); + if (values != null && values.length > 0 && values[0] != null) { + ArrayList paramValues = new ArrayList(); + for (String value : values) { + paramValues.add(encoded ? value : encodeIfNotVariable(value)); + } + this.queries.put(paramName, paramValues); + } + return this; + } + + private RequestTemplate doQuery(boolean encoded, String name, Iterable values) { + if (values != null) { + return doQuery(encoded, name, toArray(values, String.class)); + } + return doQuery(encoded, name, (String[]) null); + } + + private static String encodeIfNotVariable(String in) { + if (in == null || in.indexOf('{') == 0) { + return in; + } + return urlEncode(in); + } + + /** + * Replaces all existing queries with the newly supplied url decoded queries.
+ *

relationship to JAXRS 2.0

Like {@code WebTarget.queries}, except the + * values can be templatized.
ex.
+ *
+     * template.queries(ImmutableMultimap.of("Signature", "{signature}"));
+     * 
+ * + * @param queries if null, remove all queries. else value to replace all queries with. + * @see #queries() + */ + public RequestTemplate queries(Map> queries) { + if (queries == null || queries.isEmpty()) { + this.queries.clear(); + } else { + for (Entry> entry : queries.entrySet()) { + query(entry.getKey(), toArray(entry.getValue(), String.class)); + } + } + return this; + } + + /** + * Returns an immutable copy of the url decoded queries. + * + * @see Request#url() + */ + public Map> queries() { + Map> decoded = new LinkedHashMap>(); + for (String field : queries.keySet()) { + Collection decodedValues = new ArrayList(); + for (String value : valuesOrEmpty(queries, field)) { + if (value != null) { + decodedValues.add(urlDecode(value)); + } else { + decodedValues.add(null); + } + } + decoded.put(urlDecode(field), decodedValues); + } + return Collections.unmodifiableMap(decoded); + } + + /** + * Replaces headers with the specified {@code configKey} with the {@code values} supplied.
+ * When the {@code value} is {@code null}, all headers with the {@code configKey} are removed. + *


relationship to JAXRS 2.0

Like {@code WebTarget.queries} and + * {@code javax.ws.rs.client.Invocation.Builder.header}, except the values can be templatized. + *
ex.
+ *
+     * template.query("X-Application-Version", "{version}");
+     * 
+ * + * @param name the name of the header + * @param values can be a single null to imply removing all values. Else no values are expected to + * be null. + * @see #headers() + */ + public RequestTemplate header(String name, String... values) { + checkNotNull(name, "header name"); + if (values == null || (values.length == 1 && values[0] == null)) { + headers.remove(name); + } else { + List headers = new ArrayList(); + headers.addAll(Arrays.asList(values)); + this.headers.put(name, headers); + } + return this; + } + + /* @see #header(String, String...) */ + public RequestTemplate header(String name, Iterable values) { + if (values != null) { + return header(name, toArray(values, String.class)); + } + return header(name, (String[]) null); + } + + /** + * Replaces all existing headers with the newly supplied headers.


relationship to + * JAXRS 2.0

Like {@code Invocation.Builder.headers(MultivaluedMap)}, except the + * values can be templatized.
ex.
+ *
+     * template.headers(mapOf("X-Application-Version", asList("{version}")));
+     * 
+ * + * @param headers if null, remove all headers. else value to replace all headers with. + * @see #headers() + */ + public RequestTemplate headers(Map> headers) { + if (headers == null || headers.isEmpty()) { + this.headers.clear(); + } else { + this.headers.putAll(headers); + } + return this; + } + + /** + * Returns an immutable copy of the current headers. + * + * @see Request#headers() + */ + public Map> headers() { + return Collections.unmodifiableMap(headers); + } + + /** + * replaces the {@link Util#CONTENT_LENGTH} header.
Usually populated by an {@link + * Encoder}. + * + * @see Request#body() + */ + public RequestTemplate body(byte[] bodyData, Charset charset) { + this.bodyTemplate = null; + this.charset = charset; + this.body = bodyData; + int bodyLength = bodyData != null ? bodyData.length : 0; + header(CONTENT_LENGTH, String.valueOf(bodyLength)); + return this; + } + + /** + * replaces the {@link Util#CONTENT_LENGTH} header.
Usually populated by an {@link + * Encoder}. + * + * @see Request#body() + */ + public RequestTemplate body(String bodyText) { + byte[] bodyData = bodyText != null ? bodyText.getBytes(UTF_8) : null; + return body(bodyData, UTF_8); + } + + /** + * The character set with which the body is encoded, or null if unknown or not applicable. When + * this is present, you can use {@code new String(req.body(), req.charset())} to access the body + * as a String. + */ + public Charset charset() { + return charset; + } + + /** + * @see Request#body() + */ + public byte[] body() { + return body; + } + + /** + * populated by {@link Body} + * + * @see Request#body() + */ + public RequestTemplate bodyTemplate(String bodyTemplate) { + this.bodyTemplate = bodyTemplate; + this.charset = null; + this.body = null; + return this; + } + + /** + * @see Request#body() + * @see #expand(String, Map) + */ + public String bodyTemplate() { + return bodyTemplate; + } + + /** + * if there are any query params in the URL, this will extract them out. + */ + private StringBuilder pullAnyQueriesOutOfUrl(StringBuilder url) { + // parse out queries + int queryIndex = url.indexOf("?"); + if (queryIndex != -1) { + String queryLine = url.substring(queryIndex + 1); + Map> firstQueries = parseAndDecodeQueries(queryLine); + if (!queries.isEmpty()) { + firstQueries.putAll(queries); + queries.clear(); + } + // Since we decode all queries, we want to use the + // query()-method to re-add them to ensure that all + // logic (such as url-encoding) are executed, giving + // a valid queryLine() + for (String key : firstQueries.keySet()) { + Collection values = firstQueries.get(key); + if (allValuesAreNull(values)) { + // Queries where all values are null will + // be ignored by the query(key, value)-method + // So we manually avoid this case here, to ensure that + // we still fulfill the contract (ex. parameters without values) + queries.put(urlEncode(key), values); + } else { + query(key, values); + } + } + return new StringBuilder(url.substring(0, queryIndex)); + } + return url; + } + + private boolean allValuesAreNull(Collection values) { + if (values == null || values.isEmpty()) { + return true; + } + for (String val : values) { + if (val != null) { + return false; + } + } + return true; + } + + @Override + public String toString() { + return request().toString(); + } + + /** + * Replaces query values which are templated with corresponding values from the {@code unencoded} + * map. Any unresolved queries are removed. + */ + public void replaceQueryValues(Map unencoded) { + Iterator>> iterator = + queries.entrySet().iterator(); + while (iterator.hasNext()) { + Entry> entry = iterator.next(); + if (entry.getValue() == null) { + continue; + } + Collection values = new ArrayList(); + for (String value : entry.getValue()) { + if (value.indexOf('{') == 0 && value.indexOf('}') == value.length() - 1) { + Object variableValue = unencoded.get(value.substring(1, value.length() - 1)); + // only add non-null expressions + if (variableValue == null) { + continue; + } + if (variableValue instanceof Iterable) { + for (Object val : Iterable.class.cast(variableValue)) { + values.add(urlEncode(String.valueOf(val))); + } + } else { + values.add(urlEncode(String.valueOf(variableValue))); + } + } else { + values.add(value); + } + } + if (values.isEmpty()) { + iterator.remove(); + } else { + entry.setValue(values); + } + } + } + + public String queryLine() { + if (queries.isEmpty()) { + return ""; + } + StringBuilder queryBuilder = new StringBuilder(); + for (String field : queries.keySet()) { + for (String value : valuesOrEmpty(queries, field)) { + queryBuilder.append('&'); + queryBuilder.append(field); + if (value != null) { + queryBuilder.append('='); + if (!value.isEmpty()) { + queryBuilder.append(value); + } + } + } + } + queryBuilder.deleteCharAt(0); + return queryBuilder.insert(0, '?').toString(); + } + + interface Factory { + + /** + * create a request template using args passed to a method invocation. + */ + RequestTemplate create(Object[] argv); + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Response.java b/conjure-java-jaxrs-client/src/main/java/feign/Response.java new file mode 100644 index 000000000..9045fe209 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/Response.java @@ -0,0 +1,262 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign; + +import static feign.Util.UTF_8; +import static feign.Util.checkNotNull; +import static feign.Util.checkState; +import static feign.Util.decodeOrDefault; +import static feign.Util.valuesOrEmpty; + +import java.io.ByteArrayInputStream; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.Charset; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.Locale; +import java.util.Map; +import java.util.TreeMap; + +/** + * An immutable response to an http invocation which only returns string content. + */ +public final class Response implements Closeable { + + private final int status; + private final String reason; + private final Map> headers; + private final Body body; + + private Response(int status, String reason, Map> headers, Body body) { + checkState(status >= 200, "Invalid status code: %s", status); + this.status = status; + this.reason = reason; // nullable + this.headers = Collections.unmodifiableMap(caseInsensitiveCopyOf(headers)); + this.body = body; // nullable + } + + public static Response create( + int status, + String reason, + Map> headers, + InputStream inputStream, + Integer length) { + return new Response(status, reason, headers, InputStreamBody.orNull(inputStream, length)); + } + + public static Response create(int status, String reason, Map> headers, byte[] data) { + return new Response(status, reason, headers, ByteArrayBody.orNull(data)); + } + + public static Response create( + int status, String reason, Map> headers, String text, Charset charset) { + return new Response(status, reason, headers, ByteArrayBody.orNull(text, charset)); + } + + public static Response create(int status, String reason, Map> headers, Body body) { + return new Response(status, reason, headers, body); + } + + /** + * status code. ex {@code 200} + * + * See rfc2616 + */ + public int status() { + return status; + } + + /** + * Nullable and not set when using http/2 + * + * See https://github.com/http2/http2-spec/issues/202 + */ + public String reason() { + return reason; + } + + /** + * Returns a case-insensitive mapping of header names to their values. + */ + public Map> headers() { + return headers; + } + + /** + * if present, the response had a body + */ + public Body body() { + return body; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("HTTP/1.1 ").append(status); + if (reason != null) builder.append(' ').append(reason); + builder.append('\n'); + for (String field : headers.keySet()) { + for (String value : valuesOrEmpty(headers, field)) { + builder.append(field).append(": ").append(value).append('\n'); + } + } + if (body != null) builder.append('\n').append(body); + return builder.toString(); + } + + @Override + public void close() { + Util.ensureClosed(body); + } + + public interface Body extends Closeable { + + /** + * length in bytes, if known. Null if unknown or greater than {@link Integer#MAX_VALUE}. + * + *


Note
This is an integer as + * most implementations cannot do bodies greater than 2GB. + */ + Integer length(); + + /** + * True if {@link #asInputStream()} and {@link #asReader()} can be called more than once. + */ + boolean isRepeatable(); + + /** + * It is the responsibility of the caller to close the stream. + */ + InputStream asInputStream() throws IOException; + + /** + * It is the responsibility of the caller to close the stream. + */ + Reader asReader() throws IOException; + } + + private static final class InputStreamBody implements Body { + + private final InputStream inputStream; + private final Integer length; + + private InputStreamBody(InputStream inputStream, Integer length) { + this.inputStream = inputStream; + this.length = length; + } + + private static Body orNull(InputStream inputStream, Integer length) { + if (inputStream == null) { + return null; + } + return new InputStreamBody(inputStream, length); + } + + @Override + public Integer length() { + return length; + } + + @Override + public boolean isRepeatable() { + return false; + } + + @Override + public InputStream asInputStream() throws IOException { + return inputStream; + } + + @Override + public Reader asReader() throws IOException { + return new InputStreamReader(inputStream, UTF_8); + } + + @Override + public void close() throws IOException { + inputStream.close(); + } + } + + private static final class ByteArrayBody implements Body { + + private final byte[] data; + + public ByteArrayBody(byte[] data) { + this.data = data; + } + + private static Body orNull(byte[] data) { + if (data == null) { + return null; + } + return new ByteArrayBody(data); + } + + private static Body orNull(String text, Charset charset) { + if (text == null) { + return null; + } + checkNotNull(charset, "charset"); + return new ByteArrayBody(text.getBytes(charset)); + } + + @Override + public Integer length() { + return data.length; + } + + @Override + public boolean isRepeatable() { + return true; + } + + @Override + public InputStream asInputStream() throws IOException { + return new ByteArrayInputStream(data); + } + + @Override + public Reader asReader() throws IOException { + return new InputStreamReader(asInputStream(), UTF_8); + } + + @Override + public void close() throws IOException {} + + @Override + public String toString() { + return decodeOrDefault(data, UTF_8, "Binary data"); + } + } + + private static Map> caseInsensitiveCopyOf(Map> headers) { + Map> result = new TreeMap>(String.CASE_INSENSITIVE_ORDER); + + for (Map.Entry> entry : headers.entrySet()) { + String headerName = entry.getKey(); + if (!result.containsKey(headerName)) { + result.put(headerName.toLowerCase(Locale.ROOT), new LinkedList()); + } + result.get(headerName).addAll(entry.getValue()); + } + return result; + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/RetryableException.java b/conjure-java-jaxrs-client/src/main/java/feign/RetryableException.java new file mode 100644 index 000000000..a0253d03d --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/RetryableException.java @@ -0,0 +1,54 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign; + +import feign.codec.ErrorDecoder; +import java.util.Date; + +/** + * This exception is raised when the {@link Response} is deemed to be retryable, typically via an + * {@link ErrorDecoder} when the {@link Response#status() status} is 503. + */ +public class RetryableException extends FeignException { + + private static final long serialVersionUID = 1L; + + private final Long retryAfter; + + /** + * @param retryAfter usually corresponds to the {@link Util#RETRY_AFTER} header. + */ + public RetryableException(String message, Throwable cause, Date retryAfter) { + super(message, cause); + this.retryAfter = retryAfter != null ? retryAfter.getTime() : null; + } + + /** + * @param retryAfter usually corresponds to the {@link Util#RETRY_AFTER} header. + */ + public RetryableException(String message, Date retryAfter) { + super(message); + this.retryAfter = retryAfter != null ? retryAfter.getTime() : null; + } + + /** + * Sometimes corresponds to the {@link Util#RETRY_AFTER} header present in {@code 503} + * status. Other times parsed from an application-specific response. Null if unknown. + */ + public Date retryAfter() { + return retryAfter != null ? new Date(retryAfter) : null; + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Retryer.java b/conjure-java-jaxrs-client/src/main/java/feign/Retryer.java new file mode 100644 index 000000000..f5eae7e20 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/Retryer.java @@ -0,0 +1,115 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign; + +import static java.util.concurrent.TimeUnit.SECONDS; + +/** + * Cloned for each invocation to {@link Client#execute(Request, Request.Options)}. + * Implementations may keep state to determine if retry operations should continue or not. + */ +public interface Retryer extends Cloneable { + + /** + * if retry is permitted, return (possibly after sleeping). Otherwise propagate the exception. + */ + void continueOrPropagate(RetryableException e); + + Retryer clone(); + + public static class Default implements Retryer { + + private final int maxAttempts; + private final long period; + private final long maxPeriod; + int attempt; + long sleptForMillis; + + public Default() { + this(100, SECONDS.toMillis(1), 5); + } + + public Default(long period, long maxPeriod, int maxAttempts) { + this.period = period; + this.maxPeriod = maxPeriod; + this.maxAttempts = maxAttempts; + this.attempt = 1; + } + + // visible for testing; + protected long currentTimeMillis() { + return System.currentTimeMillis(); + } + + public void continueOrPropagate(RetryableException e) { + if (attempt++ >= maxAttempts) { + throw e; + } + + long interval; + if (e.retryAfter() != null) { + interval = e.retryAfter().getTime() - currentTimeMillis(); + if (interval > maxPeriod) { + interval = maxPeriod; + } + if (interval < 0) { + return; + } + } else { + interval = nextMaxInterval(); + } + try { + Thread.sleep(interval); + } catch (InterruptedException ignored) { + Thread.currentThread().interrupt(); + } + sleptForMillis += interval; + } + + /** + * Calculates the time interval to a retry attempt.
The interval increases exponentially + * with each attempt, at a rate of nextInterval *= 1.5 (where 1.5 is the backoff factor), to the + * maximum interval. + * + * @return time in nanoseconds from now until the next attempt. + */ + long nextMaxInterval() { + long interval = (long) (period * Math.pow(1.5, attempt - 1)); + return interval > maxPeriod ? maxPeriod : interval; + } + + @Override + public Retryer clone() { + return new Default(period, maxPeriod, maxAttempts); + } + } + + /** + * Implementation that never retries request. It propagates the RetryableException. + */ + Retryer NEVER_RETRY = new Retryer() { + + @Override + public void continueOrPropagate(RetryableException e) { + throw e; + } + + @Override + public Retryer clone() { + return this; + } + }; +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/SynchronousMethodHandler.java b/conjure-java-jaxrs-client/src/main/java/feign/SynchronousMethodHandler.java new file mode 100644 index 000000000..740721325 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/SynchronousMethodHandler.java @@ -0,0 +1,195 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign; + +import static feign.FeignException.errorExecuting; +import static feign.FeignException.errorReading; +import static feign.Util.checkNotNull; +import static feign.Util.ensureClosed; + +import feign.InvocationHandlerFactory.MethodHandler; +import feign.Request.Options; +import feign.codec.DecodeException; +import feign.codec.Decoder; +import feign.codec.ErrorDecoder; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.TimeUnit; + +final class SynchronousMethodHandler implements MethodHandler { + + private static final long MAX_RESPONSE_BUFFER_SIZE = 8192L; + + private final MethodMetadata metadata; + private final Target target; + private final Client client; + private final Retryer retryer; + private final List requestInterceptors; + private final Logger logger; + private final Logger.Level logLevel; + private final RequestTemplate.Factory buildTemplateFromArgs; + private final Options options; + private final Decoder decoder; + private final ErrorDecoder errorDecoder; + private final boolean decode404; + + private SynchronousMethodHandler(Target target, Client client, Retryer retryer, + List requestInterceptors, Logger logger, + Logger.Level logLevel, MethodMetadata metadata, + RequestTemplate.Factory buildTemplateFromArgs, Options options, + Decoder decoder, ErrorDecoder errorDecoder, boolean decode404) { + this.target = checkNotNull(target, "target"); + this.client = checkNotNull(client, "client for %s", target); + this.retryer = checkNotNull(retryer, "retryer for %s", target); + this.requestInterceptors = + checkNotNull(requestInterceptors, "requestInterceptors for %s", target); + this.logger = checkNotNull(logger, "logger for %s", target); + this.logLevel = checkNotNull(logLevel, "logLevel for %s", target); + this.metadata = checkNotNull(metadata, "metadata for %s", target); + this.buildTemplateFromArgs = checkNotNull(buildTemplateFromArgs, "metadata for %s", target); + this.options = checkNotNull(options, "options for %s", target); + this.errorDecoder = checkNotNull(errorDecoder, "errorDecoder for %s", target); + this.decoder = checkNotNull(decoder, "decoder for %s", target); + this.decode404 = decode404; + } + + @Override + public Object invoke(Object[] argv) throws Throwable { + RequestTemplate template = buildTemplateFromArgs.create(argv); + Retryer retryer = this.retryer.clone(); + while (true) { + try { + return executeAndDecode(template); + } catch (RetryableException e) { + retryer.continueOrPropagate(e); + if (logLevel != Logger.Level.NONE) { + logger.logRetry(metadata.configKey(), logLevel); + } + continue; + } + } + } + + Object executeAndDecode(RequestTemplate template) throws Throwable { + Request request = targetRequest(template); + + if (logLevel != Logger.Level.NONE) { + logger.logRequest(metadata.configKey(), logLevel, request); + } + + Response response; + long start = System.nanoTime(); + try { + response = client.execute(request, options); + } catch (IOException e) { + if (logLevel != Logger.Level.NONE) { + logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start)); + } + throw errorExecuting(request, e); + } + long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); + + boolean shouldClose = true; + try { + if (logLevel != Logger.Level.NONE) { + response = + logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime); + } + if (Response.class == metadata.returnType()) { + if (response.body() == null) { + return response; + } + if (response.body().length() == null || + response.body().length() > MAX_RESPONSE_BUFFER_SIZE) { + shouldClose = false; + return response; + } + // Ensure the response body is disconnected + byte[] bodyData = Util.toByteArray(response.body().asInputStream()); + return Response.create(response.status(), response.reason(), response.headers(), bodyData); + } + if (response.status() >= 200 && response.status() < 300) { + if (void.class == metadata.returnType()) { + return null; + } else { + return decode(response); + } + } else if (decode404 && response.status() == 404) { + return decoder.decode(response, metadata.returnType()); + } else { + throw errorDecoder.decode(metadata.configKey(), response); + } + } catch (IOException e) { + if (logLevel != Logger.Level.NONE) { + logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime); + } + throw errorReading(request, response, e); + } finally { + if (shouldClose) { + ensureClosed(response.body()); + } + } + } + + long elapsedTime(long start) { + return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); + } + + Request targetRequest(RequestTemplate template) { + for (RequestInterceptor interceptor : requestInterceptors) { + interceptor.apply(template); + } + return target.apply(new RequestTemplate(template)); + } + + Object decode(Response response) throws Throwable { + try { + return decoder.decode(response, metadata.returnType()); + } catch (FeignException e) { + throw e; + } catch (RuntimeException e) { + throw new DecodeException(e.getMessage(), e); + } + } + + static class Factory { + + private final Client client; + private final Retryer retryer; + private final List requestInterceptors; + private final Logger logger; + private final Logger.Level logLevel; + private final boolean decode404; + + Factory(Client client, Retryer retryer, List requestInterceptors, + Logger logger, Logger.Level logLevel, boolean decode404) { + this.client = checkNotNull(client, "client"); + this.retryer = checkNotNull(retryer, "retryer"); + this.requestInterceptors = checkNotNull(requestInterceptors, "requestInterceptors"); + this.logger = checkNotNull(logger, "logger"); + this.logLevel = checkNotNull(logLevel, "logLevel"); + this.decode404 = decode404; + } + + public MethodHandler create(Target target, MethodMetadata md, + RequestTemplate.Factory buildTemplateFromArgs, + Options options, Decoder decoder, ErrorDecoder errorDecoder) { + return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger, + logLevel, md, buildTemplateFromArgs, options, decoder, + errorDecoder, decode404); + } + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Target.java b/conjure-java-jaxrs-client/src/main/java/feign/Target.java new file mode 100644 index 000000000..2c82067fb --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/Target.java @@ -0,0 +1,193 @@ +/* + * Copyright 2013 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign; + +import static feign.Util.checkNotNull; +import static feign.Util.emptyToNull; + +/** + *

relationship to JAXRS 2.0

Similar to {@code + * javax.ws.rs.client.WebTarget}, as it produces requests. However, {@link RequestTemplate} is a + * closer match to {@code WebTarget}. + * + * @param type of the interface this target applies to. + */ +public interface Target { + + /* The type of the interface this target applies to. ex. {@code Route53}. */ + Class type(); + + /* configuration key associated with this target. For example, {@code route53}. */ + String name(); + + /* base HTTP URL of the target. For example, {@code https://api/v2}. */ + String url(); + + /** + * Targets a template to this target, adding the {@link #url() base url} and any target-specific + * headers or query parameters.

For example:
+ *
+   * public Request apply(RequestTemplate input) {
+   *     input.insert(0, url());
+   *     input.replaceHeader("X-Auth", currentToken);
+   *     return input.asRequest();
+   * }
+   * 
+ *


relationship to JAXRS 2.0

This call is similar to {@code + * javax.ws.rs.client.WebTarget.request()}, except that we expect transient, but necessary + * decoration to be applied on invocation. + */ + public Request apply(RequestTemplate input); + + public static class HardCodedTarget implements Target { + + private final Class type; + private final String name; + private final String url; + + public HardCodedTarget(Class type, String url) { + this(type, url, url); + } + + public HardCodedTarget(Class type, String name, String url) { + this.type = checkNotNull(type, "type"); + this.name = checkNotNull(emptyToNull(name), "name"); + this.url = checkNotNull(emptyToNull(url), "url"); + } + + @Override + public Class type() { + return type; + } + + @Override + public String name() { + return name; + } + + @Override + public String url() { + return url; + } + + /* no authentication or other special activity. just insert the url. */ + @Override + public Request apply(RequestTemplate input) { + if (input.url().indexOf("http") != 0) { + input.insert(0, url()); + } + return input.request(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof HardCodedTarget) { + HardCodedTarget other = (HardCodedTarget) obj; + return type.equals(other.type) + && name.equals(other.name) + && url.equals(other.url); + } + return false; + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + type.hashCode(); + result = 31 * result + name.hashCode(); + result = 31 * result + url.hashCode(); + return result; + } + + @Override + public String toString() { + if (name.equals(url)) { + return "HardCodedTarget(type=" + type.getSimpleName() + ", url=" + url + ")"; + } + return "HardCodedTarget(type=" + type.getSimpleName() + ", name=" + name + ", url=" + url + + ")"; + } + } + + public static final class EmptyTarget implements Target { + + private final Class type; + private final String name; + + EmptyTarget(Class type, String name) { + this.type = checkNotNull(type, "type"); + this.name = checkNotNull(emptyToNull(name), "name"); + } + + public static EmptyTarget create(Class type) { + return new EmptyTarget(type, "empty:" + type.getSimpleName()); + } + + public static EmptyTarget create(Class type, String name) { + return new EmptyTarget(type, name); + } + + @Override + public Class type() { + return type; + } + + @Override + public String name() { + return name; + } + + @Override + public String url() { + throw new UnsupportedOperationException("Empty targets don't have URLs"); + } + + @Override + public Request apply(RequestTemplate input) { + if (input.url().indexOf("http") != 0) { + throw new UnsupportedOperationException( + "Request with non-absolute URL not supported with empty target"); + } + return input.request(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof EmptyTarget) { + EmptyTarget other = (EmptyTarget) obj; + return type.equals(other.type) + && name.equals(other.name); + } + return false; + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + type.hashCode(); + result = 31 * result + name.hashCode(); + return result; + } + + @Override + public String toString() { + if (name.equals("empty:" + type.getSimpleName())) { + return "EmptyTarget(type=" + type.getSimpleName() + ")"; + } + return "EmptyTarget(type=" + type.getSimpleName() + ", name=" + name + ")"; + } + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Types.java b/conjure-java-jaxrs-client/src/main/java/feign/Types.java new file mode 100644 index 000000000..9d9a87bf1 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/Types.java @@ -0,0 +1,459 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign; + +import java.lang.reflect.Array; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.GenericDeclaration; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.Arrays; +import java.util.NoSuchElementException; + +/** + * Static methods for working with types. + * + * @author Bob Lee + * @author Jesse Wilson + */ +final class Types { + + private static final Type[] EMPTY_TYPE_ARRAY = new Type[0]; + + private Types() { + // No instances. + } + + static Class getRawType(Type type) { + if (type instanceof Class) { + // Type is a normal class. + return (Class) type; + + } else if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + + // I'm not exactly sure why getRawType() returns Type instead of Class. Neal isn't either but + // suspects some pathological case related to nested classes exists. + Type rawType = parameterizedType.getRawType(); + if (!(rawType instanceof Class)) { + throw new IllegalArgumentException(); + } + return (Class) rawType; + + } else if (type instanceof GenericArrayType) { + Type componentType = ((GenericArrayType) type).getGenericComponentType(); + return Array.newInstance(getRawType(componentType), 0).getClass(); + + } else if (type instanceof TypeVariable) { + // We could use the variable's bounds, but that won't work if there are multiple. Having a raw + // type that's more general than necessary is okay. + return Object.class; + + } else if (type instanceof WildcardType) { + return getRawType(((WildcardType) type).getUpperBounds()[0]); + + } else { + String className = type == null ? "null" : type.getClass().getName(); + throw new IllegalArgumentException("Expected a Class, ParameterizedType, or " + + "GenericArrayType, but <" + type + "> is of type " + + className); + } + } + + /** + * Returns true if {@code a} and {@code b} are equal. + */ + static boolean equals(Type a, Type b) { + if (a == b) { + return true; // Also handles (a == null && b == null). + + } else if (a instanceof Class) { + return a.equals(b); // Class already specifies equals(). + + } else if (a instanceof ParameterizedType) { + if (!(b instanceof ParameterizedType)) { + return false; + } + ParameterizedType pa = (ParameterizedType) a; + ParameterizedType pb = (ParameterizedType) b; + return equal(pa.getOwnerType(), pb.getOwnerType()) + && pa.getRawType().equals(pb.getRawType()) + && Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments()); + + } else if (a instanceof GenericArrayType) { + if (!(b instanceof GenericArrayType)) { + return false; + } + GenericArrayType ga = (GenericArrayType) a; + GenericArrayType gb = (GenericArrayType) b; + return equals(ga.getGenericComponentType(), gb.getGenericComponentType()); + + } else if (a instanceof WildcardType) { + if (!(b instanceof WildcardType)) { + return false; + } + WildcardType wa = (WildcardType) a; + WildcardType wb = (WildcardType) b; + return Arrays.equals(wa.getUpperBounds(), wb.getUpperBounds()) + && Arrays.equals(wa.getLowerBounds(), wb.getLowerBounds()); + + } else if (a instanceof TypeVariable) { + if (!(b instanceof TypeVariable)) { + return false; + } + TypeVariable va = (TypeVariable) a; + TypeVariable vb = (TypeVariable) b; + return va.getGenericDeclaration() == vb.getGenericDeclaration() + && va.getName().equals(vb.getName()); + + } else { + return false; // This isn't a type we support! + } + } + + /** + * Returns the generic supertype for {@code supertype}. For example, given a class {@code + * IntegerSet}, the result for when supertype is {@code Set.class} is {@code Set} and the + * result when the supertype is {@code Collection.class} is {@code Collection}. + */ + static Type getGenericSupertype(Type context, Class rawType, Class toResolve) { + if (toResolve == rawType) { + return context; + } + + // We skip searching through interfaces if unknown is an interface. + if (toResolve.isInterface()) { + Class[] interfaces = rawType.getInterfaces(); + for (int i = 0, length = interfaces.length; i < length; i++) { + if (interfaces[i] == toResolve) { + return rawType.getGenericInterfaces()[i]; + } else if (toResolve.isAssignableFrom(interfaces[i])) { + return getGenericSupertype(rawType.getGenericInterfaces()[i], interfaces[i], toResolve); + } + } + } + + // Check our supertypes. + if (!rawType.isInterface()) { + while (rawType != Object.class) { + Class rawSupertype = rawType.getSuperclass(); + if (rawSupertype == toResolve) { + return rawType.getGenericSuperclass(); + } else if (toResolve.isAssignableFrom(rawSupertype)) { + return getGenericSupertype(rawType.getGenericSuperclass(), rawSupertype, toResolve); + } + rawType = rawSupertype; + } + } + + // We can't resolve this further. + return toResolve; + } + + private static int indexOf(Object[] array, Object toFind) { + for (int i = 0; i < array.length; i++) { + if (toFind.equals(array[i])) { + return i; + } + } + throw new NoSuchElementException(); + } + + private static boolean equal(Object a, Object b) { + return a == b || (a != null && a.equals(b)); + } + + private static int hashCodeOrZero(Object o) { + return o != null ? o.hashCode() : 0; + } + + static String typeToString(Type type) { + return type instanceof Class ? ((Class) type).getName() : type.toString(); + } + + /** + * Returns the generic form of {@code supertype}. For example, if this is {@code + * ArrayList}, this returns {@code Iterable} given the input {@code + * Iterable.class}. + * + * @param supertype a superclass of, or interface implemented by, this. + */ + static Type getSupertype(Type context, Class contextRawType, Class supertype) { + if (!supertype.isAssignableFrom(contextRawType)) { + throw new IllegalArgumentException(); + } + return resolve(context, contextRawType, getGenericSupertype(context, contextRawType, supertype)); + } + + static Type resolve(Type context, Class contextRawType, Type toResolve) { + // This implementation is made a little more complicated in an attempt to avoid object-creation. + while (true) { + if (toResolve instanceof TypeVariable) { + TypeVariable typeVariable = (TypeVariable) toResolve; + toResolve = resolveTypeVariable(context, contextRawType, typeVariable); + if (toResolve == typeVariable) { + return toResolve; + } + + } else if (toResolve instanceof Class && ((Class) toResolve).isArray()) { + Class original = (Class) toResolve; + Type componentType = original.getComponentType(); + Type newComponentType = resolve(context, contextRawType, componentType); + return componentType == newComponentType ? original : new GenericArrayTypeImpl(newComponentType); + + } else if (toResolve instanceof GenericArrayType) { + GenericArrayType original = (GenericArrayType) toResolve; + Type componentType = original.getGenericComponentType(); + Type newComponentType = resolve(context, contextRawType, componentType); + return componentType == newComponentType ? original : new GenericArrayTypeImpl(newComponentType); + + } else if (toResolve instanceof ParameterizedType) { + ParameterizedType original = (ParameterizedType) toResolve; + Type ownerType = original.getOwnerType(); + Type newOwnerType = resolve(context, contextRawType, ownerType); + boolean changed = newOwnerType != ownerType; + + Type[] args = original.getActualTypeArguments(); + for (int t = 0, length = args.length; t < length; t++) { + Type resolvedTypeArgument = resolve(context, contextRawType, args[t]); + if (resolvedTypeArgument != args[t]) { + if (!changed) { + args = args.clone(); + changed = true; + } + args[t] = resolvedTypeArgument; + } + } + + return changed ? new ParameterizedTypeImpl(newOwnerType, original.getRawType(), args) : original; + + } else if (toResolve instanceof WildcardType) { + WildcardType original = (WildcardType) toResolve; + Type[] originalLowerBound = original.getLowerBounds(); + Type[] originalUpperBound = original.getUpperBounds(); + + if (originalLowerBound.length == 1) { + Type lowerBound = resolve(context, contextRawType, originalLowerBound[0]); + if (lowerBound != originalLowerBound[0]) { + return new WildcardTypeImpl(new Type[] {Object.class}, new Type[] {lowerBound}); + } + } else if (originalUpperBound.length == 1) { + Type upperBound = resolve(context, contextRawType, originalUpperBound[0]); + if (upperBound != originalUpperBound[0]) { + return new WildcardTypeImpl(new Type[] {upperBound}, EMPTY_TYPE_ARRAY); + } + } + return original; + + } else { + return toResolve; + } + } + } + + private static Type resolveTypeVariable(Type context, Class contextRawType, TypeVariable unknown) { + Class declaredByRaw = declaringClassOf(unknown); + + // We can't reduce this further. + if (declaredByRaw == null) { + return unknown; + } + + Type declaredBy = getGenericSupertype(context, contextRawType, declaredByRaw); + if (declaredBy instanceof ParameterizedType) { + int index = indexOf(declaredByRaw.getTypeParameters(), unknown); + return ((ParameterizedType) declaredBy).getActualTypeArguments()[index]; + } + + return unknown; + } + + /** + * Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by + * a class. + */ + private static Class declaringClassOf(TypeVariable typeVariable) { + GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration(); + return genericDeclaration instanceof Class ? (Class) genericDeclaration : null; + } + + private static void checkNotPrimitive(Type type) { + if (type instanceof Class && ((Class) type).isPrimitive()) { + throw new IllegalArgumentException(); + } + } + + static final class ParameterizedTypeImpl implements ParameterizedType { + + private final Type ownerType; + private final Type rawType; + private final Type[] typeArguments; + + ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) { + // Require an owner type if the raw type needs it. + if (rawType instanceof Class + && (ownerType == null) != (((Class) rawType).getEnclosingClass() == null)) { + throw new IllegalArgumentException(); + } + + this.ownerType = ownerType; + this.rawType = rawType; + this.typeArguments = typeArguments.clone(); + + for (Type typeArgument : this.typeArguments) { + if (typeArgument == null) { + throw new NullPointerException(); + } + checkNotPrimitive(typeArgument); + } + } + + public Type[] getActualTypeArguments() { + return typeArguments.clone(); + } + + public Type getRawType() { + return rawType; + } + + public Type getOwnerType() { + return ownerType; + } + + @Override + public boolean equals(Object other) { + return other instanceof ParameterizedType && Types.equals(this, (ParameterizedType) other); + } + + @Override + public int hashCode() { + return Arrays.hashCode(typeArguments) ^ rawType.hashCode() ^ hashCodeOrZero(ownerType); + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(30 * (typeArguments.length + 1)); + result.append(typeToString(rawType)); + if (typeArguments.length == 0) { + return result.toString(); + } + result.append("<").append(typeToString(typeArguments[0])); + for (int i = 1; i < typeArguments.length; i++) { + result.append(", ").append(typeToString(typeArguments[i])); + } + return result.append(">").toString(); + } + } + + private static final class GenericArrayTypeImpl implements GenericArrayType { + + private final Type componentType; + + GenericArrayTypeImpl(Type componentType) { + this.componentType = componentType; + } + + public Type getGenericComponentType() { + return componentType; + } + + @Override + public boolean equals(Object o) { + return o instanceof GenericArrayType && Types.equals(this, (GenericArrayType) o); + } + + @Override + public int hashCode() { + return componentType.hashCode(); + } + + @Override + public String toString() { + return typeToString(componentType) + "[]"; + } + } + + /** + * The WildcardType interface supports multiple upper bounds and multiple lower bounds. We only + * support what the Java 6 language needs - at most one bound. If a lower bound is set, the upper + * bound must be Object.class. + */ + static final class WildcardTypeImpl implements WildcardType { + + private final Type upperBound; + private final Type lowerBound; + + WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) { + if (lowerBounds.length > 1) { + throw new IllegalArgumentException(); + } + if (upperBounds.length != 1) { + throw new IllegalArgumentException(); + } + + if (lowerBounds.length == 1) { + if (lowerBounds[0] == null) { + throw new NullPointerException(); + } + checkNotPrimitive(lowerBounds[0]); + if (upperBounds[0] != Object.class) { + throw new IllegalArgumentException(); + } + this.lowerBound = lowerBounds[0]; + this.upperBound = Object.class; + } else { + if (upperBounds[0] == null) { + throw new NullPointerException(); + } + checkNotPrimitive(upperBounds[0]); + this.lowerBound = null; + this.upperBound = upperBounds[0]; + } + } + + public Type[] getUpperBounds() { + return new Type[] {upperBound}; + } + + public Type[] getLowerBounds() { + return lowerBound != null ? new Type[] {lowerBound} : EMPTY_TYPE_ARRAY; + } + + @Override + public boolean equals(Object other) { + return other instanceof WildcardType && Types.equals(this, (WildcardType) other); + } + + @Override + public int hashCode() { + // This equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds()). + return (lowerBound != null ? 31 + lowerBound.hashCode() : 1) ^ (31 + upperBound.hashCode()); + } + + @Override + public String toString() { + if (lowerBound != null) { + return "? super " + typeToString(lowerBound); + } + if (upperBound == Object.class) { + return "?"; + } + return "? extends " + typeToString(upperBound); + } + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Util.java b/conjure-java-jaxrs-client/src/main/java/feign/Util.java new file mode 100644 index 000000000..dc0541752 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/Util.java @@ -0,0 +1,321 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign; + +import static java.lang.String.format; + +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.WildcardType; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +/** + * Utilities, typically copied in from guava, so as to avoid dependency conflicts. + */ +public class Util { + + /** + * The HTTP Content-Length header field name. + */ + public static final String CONTENT_LENGTH = "Content-Length"; + /** + * The HTTP Content-Encoding header field name. + */ + public static final String CONTENT_ENCODING = "Content-Encoding"; + /** + * The HTTP Retry-After header field name. + */ + public static final String RETRY_AFTER = "Retry-After"; + /** + * Value for the Content-Encoding header that indicates that GZIP encoding is in use. + */ + public static final String ENCODING_GZIP = "gzip"; + /** + * Value for the Content-Encoding header that indicates that DEFLATE encoding is in use. + */ + public static final String ENCODING_DEFLATE = "deflate"; + /** + * UTF-8: eight-bit UCS Transformation Format. + */ + public static final Charset UTF_8 = Charset.forName("UTF-8"); + + // com.google.common.base.Charsets + /** + * ISO-8859-1: ISO Latin Alphabet Number 1 (ISO-LATIN-1). + */ + public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); + + private static final int BUF_SIZE = 0x800; // 2K chars (4K bytes) + + /** + * Type literal for {@code Map}. + */ + public static final Type MAP_STRING_WILDCARD = new Types.ParameterizedTypeImpl( + null, Map.class, String.class, new Types.WildcardTypeImpl(new Type[] {Object.class}, new Type[0])); + + private Util() { // no instances + } + + /** + * Copy of {@code com.google.common.base.Preconditions#checkArgument}. + */ + public static void checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { + if (!expression) { + throw new IllegalArgumentException(format(errorMessageTemplate, errorMessageArgs)); + } + } + + /** + * Copy of {@code com.google.common.base.Preconditions#checkNotNull}. + */ + public static T checkNotNull(T reference, String errorMessageTemplate, Object... errorMessageArgs) { + if (reference == null) { + // If either of these parameters is null, the right thing happens anyway + throw new NullPointerException(format(errorMessageTemplate, errorMessageArgs)); + } + return reference; + } + + /** + * Copy of {@code com.google.common.base.Preconditions#checkState}. + */ + public static void checkState(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { + if (!expression) { + throw new IllegalStateException(format(errorMessageTemplate, errorMessageArgs)); + } + } + + /** + * Identifies a method as a default instance method. + */ + public static boolean isDefault(Method method) { + // Default methods are public non-abstract, non-synthetic, and non-static instance methods + // declared in an interface. + // method.isDefault() is not sufficient for our usage as it does not check + // for synthetic methods. As a result, it picks up overridden methods as well as actual default methods. + final int SYNTHETIC = 0x00001000; + return ((method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC | SYNTHETIC)) + == Modifier.PUBLIC) + && method.getDeclaringClass().isInterface(); + } + + /** + * Adapted from {@code com.google.common.base.Strings#emptyToNull}. + */ + public static String emptyToNull(String string) { + return string == null || string.isEmpty() ? null : string; + } + + /** + * Adapted from {@code com.google.common.base.Strings#emptyToNull}. + */ + @SuppressWarnings("unchecked") + public static T[] toArray(Iterable iterable, Class type) { + Collection collection; + if (iterable instanceof Collection) { + collection = (Collection) iterable; + } else { + collection = new ArrayList(); + for (T element : iterable) { + collection.add(element); + } + } + T[] array = (T[]) Array.newInstance(type, collection.size()); + return collection.toArray(array); + } + + /** + * Returns an unmodifiable collection which may be empty, but is never null. + */ + public static Collection valuesOrEmpty(Map> map, String key) { + return map.containsKey(key) && map.get(key) != null ? map.get(key) : Collections.emptyList(); + } + + public static void ensureClosed(Closeable closeable) { + if (closeable != null) { + try { + closeable.close(); + } catch (IOException ignored) { // NOPMD + } + } + } + + /** + * Resolves the last type parameter of the parameterized {@code supertype}, based on the {@code + * genericContext}, into its upper bounds.

Implementation copied from {@code + * retrofit.RestMethodInfo}. + * + * @param genericContext Ex. {@link java.lang.reflect.Field#getGenericType()} + * @param supertype Ex. {@code Decoder.class} + * @return in the example above, the type parameter of {@code Decoder}. + * @throws IllegalStateException if {@code supertype} cannot be resolved into a parameterized type + * using {@code context}. + */ + public static Type resolveLastTypeParameter(Type genericContext, Class supertype) throws IllegalStateException { + Type resolvedSuperType = Types.getSupertype(genericContext, Types.getRawType(genericContext), supertype); + checkState( + resolvedSuperType instanceof ParameterizedType, + "could not resolve %s into a parameterized type %s", + genericContext, + supertype); + Type[] types = ParameterizedType.class.cast(resolvedSuperType).getActualTypeArguments(); + for (int i = 0; i < types.length; i++) { + Type type = types[i]; + if (type instanceof WildcardType) { + types[i] = ((WildcardType) type).getUpperBounds()[0]; + } + } + return types[types.length - 1]; + } + + /** + * This returns well known empty values for well-known java types. This returns null for types not + * in the following list. + * + *

    + *
  • {@code [Bb]oolean}
  • + *
  • {@code byte[]}
  • + *
  • {@code Collection}
  • + *
  • {@code Iterator}
  • + *
  • {@code List}
  • + *
  • {@code Map}
  • + *
  • {@code Set}
  • + *
+ * + *

When {@link Feign.Builder#decode404() decoding HTTP 404 status}, you'll need to teach + * decoders a default empty value for a type. This method cheaply supports typical types by only + * looking at the raw type (vs type hierarchy). Decorate for sophistication. + */ + public static Object emptyValueOf(Type type) { + return EMPTIES.get(Types.getRawType(type)); + } + + private static final Map, Object> EMPTIES; + + static { + Map, Object> empties = new LinkedHashMap, Object>(); + empties.put(boolean.class, false); + empties.put(Boolean.class, false); + empties.put(byte[].class, new byte[0]); + empties.put(Collection.class, Collections.emptyList()); + empties.put( + Iterator.class, + new Iterator() { // Collections.emptyIterator is a 1.7 api + public boolean hasNext() { + return false; + } + + public Object next() { + throw new NoSuchElementException(); + } + + public void remove() { + throw new IllegalStateException(); + } + }); + empties.put(List.class, Collections.emptyList()); + empties.put(Map.class, Collections.emptyMap()); + empties.put(Set.class, Collections.emptySet()); + EMPTIES = Collections.unmodifiableMap(empties); + } + + /** + * Adapted from {@code com.google.common.io.CharStreams.toString()}. + */ + public static String toString(Reader reader) throws IOException { + if (reader == null) { + return null; + } + try { + StringBuilder to = new StringBuilder(); + CharBuffer buf = CharBuffer.allocate(BUF_SIZE); + while (reader.read(buf) != -1) { + buf.flip(); + to.append(buf); + buf.clear(); + } + return to.toString(); + } finally { + ensureClosed(reader); + } + } + + /** + * Adapted from {@code com.google.common.io.ByteStreams.toByteArray()}. + */ + public static byte[] toByteArray(InputStream in) throws IOException { + checkNotNull(in, "in"); + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + copy(in, out); + return out.toByteArray(); + } finally { + ensureClosed(in); + } + } + + /** + * Adapted from {@code com.google.common.io.ByteStreams.copy()}. + */ + private static long copy(InputStream from, OutputStream to) throws IOException { + checkNotNull(from, "from"); + checkNotNull(to, "to"); + byte[] buf = new byte[BUF_SIZE]; + long total = 0; + while (true) { + int r = from.read(buf); + if (r == -1) { + break; + } + to.write(buf, 0, r); + total += r; + } + return total; + } + + static String decodeOrDefault(byte[] data, Charset charset, String defaultValue) { + if (data == null) { + return defaultValue; + } + checkNotNull(charset, "charset"); + try { + return charset.newDecoder().decode(ByteBuffer.wrap(data)).toString(); + } catch (CharacterCodingException ex) { + return defaultValue; + } + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/auth/Base64.java b/conjure-java-jaxrs-client/src/main/java/feign/auth/Base64.java new file mode 100644 index 000000000..c565bc7c8 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/auth/Base64.java @@ -0,0 +1,160 @@ +/* + * Copyright 2013 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign.auth; + +import java.io.UnsupportedEncodingException; + +/** + * copied from okhttp + * + * @author Alexander Y. Kleymenov + */ +final class Base64 { + + public static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; + private static final byte[] MAP = new byte[]{ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', + 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', + '5', '6', '7', '8', '9', '+', '/' + }; + + private Base64() { + } + + public static byte[] decode(byte[] in) { + return decode(in, in.length); + } + + public static byte[] decode(byte[] in, int len) { + // approximate output length + int length = len / 4 * 3; + // return an empty array on empty or short input without padding + if (length == 0) { + return EMPTY_BYTE_ARRAY; + } + // temporary array + byte[] out = new byte[length]; + // number of padding characters ('=') + int pad = 0; + byte chr; + // compute the number of the padding characters + // and adjust the length of the input + for (; ; len--) { + chr = in[len - 1]; + // skip the neutral characters + if ((chr == '\n') || (chr == '\r') || (chr == ' ') || (chr == '\t')) { + continue; + } + if (chr == '=') { + pad++; + } else { + break; + } + } + // index in the output array + int outIndex = 0; + // index in the input array + int inIndex = 0; + // holds the value of the input character + int bits = 0; + // holds the value of the input quantum + int quantum = 0; + for (int i = 0; i < len; i++) { + chr = in[i]; + // skip the neutral characters + if ((chr == '\n') || (chr == '\r') || (chr == ' ') || (chr == '\t')) { + continue; + } + if ((chr >= 'A') && (chr <= 'Z')) { + // char ASCII value + // A 65 0 + // Z 90 25 (ASCII - 65) + bits = chr - 65; + } else if ((chr >= 'a') && (chr <= 'z')) { + // char ASCII value + // a 97 26 + // z 122 51 (ASCII - 71) + bits = chr - 71; + } else if ((chr >= '0') && (chr <= '9')) { + // char ASCII value + // 0 48 52 + // 9 57 61 (ASCII + 4) + bits = chr + 4; + } else if (chr == '+') { + bits = 62; + } else if (chr == '/') { + bits = 63; + } else { + return null; + } + // append the value to the quantum + quantum = (quantum << 6) | (byte) bits; + if (inIndex % 4 == 3) { + // 4 characters were read, so make the output: + out[outIndex++] = (byte) (quantum >> 16); + out[outIndex++] = (byte) (quantum >> 8); + out[outIndex++] = (byte) quantum; + } + inIndex++; + } + if (pad > 0) { + // adjust the quantum value according to the padding + quantum = quantum << (6 * pad); + // make output + out[outIndex++] = (byte) (quantum >> 16); + if (pad == 1) { + out[outIndex++] = (byte) (quantum >> 8); + } + } + // create the resulting array + byte[] result = new byte[outIndex]; + System.arraycopy(out, 0, result, 0, outIndex); + return result; + } + + public static String encode(byte[] in) { + int length = (in.length + 2) * 4 / 3; + byte[] out = new byte[length]; + int index = 0, end = in.length - in.length % 3; + for (int i = 0; i < end; i += 3) { + out[index++] = MAP[(in[i] & 0xff) >> 2]; + out[index++] = MAP[((in[i] & 0x03) << 4) | ((in[i + 1] & 0xff) >> 4)]; + out[index++] = MAP[((in[i + 1] & 0x0f) << 2) | ((in[i + 2] & 0xff) >> 6)]; + out[index++] = MAP[(in[i + 2] & 0x3f)]; + } + switch (in.length % 3) { + case 1: + out[index++] = MAP[(in[end] & 0xff) >> 2]; + out[index++] = MAP[(in[end] & 0x03) << 4]; + out[index++] = '='; + out[index++] = '='; + break; + case 2: + out[index++] = MAP[(in[end] & 0xff) >> 2]; + out[index++] = MAP[((in[end] & 0x03) << 4) | ((in[end + 1] & 0xff) >> 4)]; + out[index++] = MAP[((in[end + 1] & 0x0f) << 2)]; + out[index++] = '='; + break; + } + try { + return new String(out, 0, index, "US-ASCII"); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); + } + } +} + diff --git a/conjure-java-jaxrs-client/src/main/java/feign/auth/BasicAuthRequestInterceptor.java b/conjure-java-jaxrs-client/src/main/java/feign/auth/BasicAuthRequestInterceptor.java new file mode 100644 index 000000000..713c08b94 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/auth/BasicAuthRequestInterceptor.java @@ -0,0 +1,69 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign.auth; + +import static feign.Util.ISO_8859_1; +import static feign.Util.checkNotNull; + +import feign.RequestInterceptor; +import feign.RequestTemplate; +import java.nio.charset.Charset; + +/** + * An interceptor that adds the request header needed to use HTTP basic authentication. + */ +public class BasicAuthRequestInterceptor implements RequestInterceptor { + + private final String headerValue; + + /** + * Creates an interceptor that authenticates all requests with the specified username and password + * encoded using ISO-8859-1. + * + * @param username the username to use for authentication + * @param password the password to use for authentication + */ + public BasicAuthRequestInterceptor(String username, String password) { + this(username, password, ISO_8859_1); + } + + /** + * Creates an interceptor that authenticates all requests with the specified username and password + * encoded using the specified charset. + * + * @param username the username to use for authentication + * @param password the password to use for authentication + * @param charset the charset to use when encoding the credentials + */ + public BasicAuthRequestInterceptor(String username, String password, Charset charset) { + checkNotNull(username, "username"); + checkNotNull(password, "password"); + this.headerValue = "Basic " + base64Encode((username + ":" + password).getBytes(charset)); + } + + /* + * This uses a Sun internal method; if we ever encounter a case where this method is not available, the appropriate + * response would be to pull the necessary portions of Guava's BaseEncoding class into Util. + */ + private static String base64Encode(byte[] bytes) { + return Base64.encode(bytes); + } + + @Override + public void apply(RequestTemplate template) { + template.header("Authorization", headerValue); + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/codec/DecodeException.java b/conjure-java-jaxrs-client/src/main/java/feign/codec/DecodeException.java new file mode 100644 index 000000000..e9fcff579 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/codec/DecodeException.java @@ -0,0 +1,45 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign.codec; + +import static feign.Util.checkNotNull; + +import feign.FeignException; + +/** + * Similar to {@code javax.websocket.DecodeException}, raised when a problem occurs decoding a + * message. Note that {@code DecodeException} is not an {@code IOException}, nor does it have one + * set as its cause. + */ +public class DecodeException extends FeignException { + + private static final long serialVersionUID = 1L; + + /** + * @param message the reason for the failure. + */ + public DecodeException(String message) { + super(checkNotNull(message, "message")); + } + + /** + * @param message possibly null reason for the failure. + * @param cause the cause of the error. + */ + public DecodeException(String message, Throwable cause) { + super(message, checkNotNull(cause, "cause")); + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/codec/Decoder.java b/conjure-java-jaxrs-client/src/main/java/feign/codec/Decoder.java new file mode 100644 index 000000000..cb64890c4 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/codec/Decoder.java @@ -0,0 +1,87 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign.codec; + +import feign.Feign; +import feign.FeignException; +import feign.Response; +import feign.Target; +import feign.Util; +import java.io.IOException; +import java.lang.reflect.Type; + +/** + * Decodes an HTTP response into a single object of the given {@code type}. Invoked when {@link + * Response#status()} is in the 2xx range and the return type is neither {@code void} nor {@code + * Response}.

Example Implementation:

+ *

+ * public class GsonDecoder implements Decoder {
+ *   private final Gson gson = new Gson();
+ *
+ *   @Override
+ *   public Object decode(Response response, Type type) throws IOException {
+ *     try {
+ *       return gson.fromJson(response.body().asReader(), type);
+ *     } catch (JsonIOException e) {
+ *       if (e.getCause() != null &&
+ *           e.getCause() instanceof IOException) {
+ *         throw IOException.class.cast(e.getCause());
+ *       }
+ *       throw e;
+ *     }
+ *   }
+ * }
+ * 
+ *

Implementation Note

The {@code type} parameter will correspond to the {@link + * java.lang.reflect.Method#getGenericReturnType() generic return type} of an {@link + * Target#type() interface} processed by {@link Feign#newInstance(Target)}. When + * writing your implementation of Decoder, ensure you also test parameterized types such as {@code + * List}. + *

Note on exception propagation

Exceptions thrown by {@link Decoder}s get wrapped in + * a {@link DecodeException} unless they are a subclass of {@link FeignException} already, and unless + * the client was configured with {@link Feign.Builder#decode404()}. + */ +public interface Decoder { + + /** + * Decodes an http response into an object corresponding to its {@link + * java.lang.reflect.Method#getGenericReturnType() generic return type}. If you need to wrap + * exceptions, please do so via {@link DecodeException}. + * + * @param response the response to decode + * @param type {@link java.lang.reflect.Method#getGenericReturnType() generic return type} of + * the method corresponding to this {@code response}. + * @return instance of {@code type} + * @throws IOException will be propagated safely to the caller. + * @throws DecodeException when decoding failed due to a checked exception besides IOException. + * @throws FeignException when decoding succeeds, but conveys the operation failed. + */ + Object decode(Response response, Type type) throws IOException, DecodeException, FeignException; + + /** Default implementation of {@code Decoder}. */ + public class Default extends StringDecoder { + + @Override + public Object decode(Response response, Type type) throws IOException { + if (response.status() == 404) return Util.emptyValueOf(type); + if (response.body() == null) return null; + if (byte[].class.equals(type)) { + return Util.toByteArray(response.body().asInputStream()); + } + return super.decode(response, type); + } + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/codec/EncodeException.java b/conjure-java-jaxrs-client/src/main/java/feign/codec/EncodeException.java new file mode 100644 index 000000000..4e82c15ca --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/codec/EncodeException.java @@ -0,0 +1,45 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign.codec; + +import static feign.Util.checkNotNull; + +import feign.FeignException; + +/** + * Similar to {@code javax.websocket.EncodeException}, raised when a problem occurs encoding a + * message. Note that {@code EncodeException} is not an {@code IOException}, nor does it have one + * set as its cause. + */ +public class EncodeException extends FeignException { + + private static final long serialVersionUID = 1L; + + /** + * @param message the reason for the failure. + */ + public EncodeException(String message) { + super(checkNotNull(message, "message")); + } + + /** + * @param message possibly null reason for the failure. + * @param cause the cause of the error. + */ + public EncodeException(String message, Throwable cause) { + super(message, checkNotNull(cause, "cause")); + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/codec/Encoder.java b/conjure-java-jaxrs-client/src/main/java/feign/codec/Encoder.java new file mode 100644 index 000000000..c03dd5e96 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/codec/Encoder.java @@ -0,0 +1,93 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign.codec; + +import static java.lang.String.format; + +import feign.MethodMetadata; +import feign.RequestTemplate; +import feign.Util; +import java.lang.reflect.Type; + +/** + * Encodes an object into an HTTP request body. Like {@code javax.websocket.Encoder}. {@code + * Encoder} is used when a method parameter has no {@code @Param} annotation. For example:
+ *

+ *

+ * @POST
+ * @Path("/")
+ * void create(User user);
+ * 
+ * Example implementation:

+ *

+ * public class GsonEncoder implements Encoder {
+ *   private final Gson gson;
+ *
+ *   public GsonEncoder(Gson gson) {
+ *     this.gson = gson;
+ *   }
+ *
+ *   @Override
+ *   public void encode(Object object, Type bodyType, RequestTemplate template) {
+ *     template.body(gson.toJson(object, bodyType));
+ *   }
+ * }
+ * 
+ * + *

Form encoding

If any parameters are found in {@link + * MethodMetadata#formParams()}, they will be collected and passed to the Encoder as a map. + * + *

Ex. The following is a form. Notice the parameters aren't consumed in the request line. A map + * including "username" and "password" keys will passed to the encoder, and the body type will be + * {@link #MAP_STRING_WILDCARD}. + *

+ * @RequestLine("POST /")
+ * Session login(@Param("username") String username, @Param("password") String
+ * password);
+ * 
+ */ +public interface Encoder { + /** Type literal for {@code Map}, indicating the object to encode is a form. */ + Type MAP_STRING_WILDCARD = Util.MAP_STRING_WILDCARD; + + /** + * Converts objects to an appropriate representation in the template. + * + * @param object what to encode as the request body. + * @param bodyType the type the object should be encoded as. {@link #MAP_STRING_WILDCARD} + * indicates form encoding. + * @param template the request template to populate. + * @throws EncodeException when encoding failed due to a checked exception. + */ + void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException; + + /** + * Default implementation of {@code Encoder}. + */ + class Default implements Encoder { + + @Override + public void encode(Object object, Type bodyType, RequestTemplate template) { + if (bodyType == String.class) { + template.body(object.toString()); + } else if (bodyType == byte[].class) { + template.body((byte[]) object, null); + } else if (object != null) { + throw new EncodeException(format("%s is not a type supported by this encoder.", object.getClass())); + } + } + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/codec/ErrorDecoder.java b/conjure-java-jaxrs-client/src/main/java/feign/codec/ErrorDecoder.java new file mode 100644 index 000000000..3c1cda799 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/codec/ErrorDecoder.java @@ -0,0 +1,151 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign.codec; + +import static feign.FeignException.errorStatus; +import static feign.Util.RETRY_AFTER; +import static feign.Util.checkNotNull; +import static java.util.Locale.US; +import static java.util.concurrent.TimeUnit.SECONDS; + +import feign.Feign; +import feign.FeignException; +import feign.Response; +import feign.RetryableException; +import feign.Util; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Date; +import java.util.Map; + +/** + * Allows you to massage an exception into a application-specific one. Converting out to a throttle + * exception are examples of this in use. + * + *

Ex: + *

+ * class IllegalArgumentExceptionOn404Decoder extends ErrorDecoder {
+ *
+ *   @Override
+ *   public Exception decode(String methodKey, Response response) {
+ *    if (response.status() == 400)
+ *        throw new IllegalArgumentException("bad zone name");
+ *    return new ErrorDecoder.Default().decode(methodKey, request, response);
+ *   }
+ *
+ * }
+ * 
+ * + *

Error handling + * + *

Responses where {@link Response#status()} is not in the 2xx + * range are classified as errors, addressed by the {@link ErrorDecoder}. That said, certain RPC + * apis return errors defined in the {@link Response#body()} even on a 200 status. For example, in + * the DynECT api, a job still running condition is returned with a 200 status, encoded in json. + * When scenarios like this occur, you should raise an application-specific exception (which may be + * {@link RetryableException retryable}). + * + *

Not Found Semantics + *

It is commonly the case that 404 (Not Found) status has semantic value in HTTP apis. While + * the default behavior is to raise exeception, users can alternatively enable 404 processing via + * {@link Feign.Builder#decode404()}. + */ +public interface ErrorDecoder { + + /** + * Implement this method in order to decode an HTTP {@link Response} when {@link + * Response#status()} is not in the 2xx range. Please raise application-specific exceptions where + * possible. If your exception is retryable, wrap or subclass {@link RetryableException} + * + * @param methodKey {@link Feign#configKey} of the java method that invoked the request. + * ex. {@code IAM#getUser()} + * @param response HTTP response where {@link Response#status() status} is greater than or equal + * to {@code 300}. + * @return Exception IOException, if there was a network error reading the response or an + * application-specific exception decoded by the implementation. If the throwable is retryable, it + * should be wrapped, or a subtype of {@link RetryableException} + */ + public Exception decode(String methodKey, Response response); + + public static class Default implements ErrorDecoder { + + private final RetryAfterDecoder retryAfterDecoder = new RetryAfterDecoder(); + + @Override + public Exception decode(String methodKey, Response response) { + FeignException exception = errorStatus(methodKey, response); + Date retryAfter = retryAfterDecoder.apply(firstOrNull(response.headers(), RETRY_AFTER)); + if (retryAfter != null) { + return new RetryableException(exception.getMessage(), exception, retryAfter); + } + return exception; + } + + private T firstOrNull(Map> map, String key) { + if (map.containsKey(key) && !map.get(key).isEmpty()) { + return map.get(key).iterator().next(); + } + return null; + } + } + + /** + * Decodes a {@link Util#RETRY_AFTER} header into an absolute date, if possible.
See Retry-After format + */ + static class RetryAfterDecoder { + + static final DateFormat RFC822_FORMAT = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", US); + private final DateFormat rfc822Format; + + RetryAfterDecoder() { + this(RFC822_FORMAT); + } + + RetryAfterDecoder(DateFormat rfc822Format) { + this.rfc822Format = checkNotNull(rfc822Format, "rfc822Format"); + } + + protected long currentTimeMillis() { + return System.currentTimeMillis(); + } + + /** + * returns a date that corresponds to the first time a request can be retried. + * + * @param retryAfter String in Retry-After format + */ + public Date apply(String retryAfter) { + if (retryAfter == null) { + return null; + } + if (retryAfter.matches("^[0-9]+$")) { + long deltaMillis = SECONDS.toMillis(Long.parseLong(retryAfter)); + return new Date(currentTimeMillis() + deltaMillis); + } + synchronized (rfc822Format) { + try { + return rfc822Format.parse(retryAfter); + } catch (ParseException ignored) { + return null; + } + } + } + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/ConjureTextDelegateDecoder.java b/conjure-java-jaxrs-client/src/main/java/feign/codec/StringDecoder.java similarity index 54% rename from conjure-java-jaxrs-client/src/main/java/feign/ConjureTextDelegateDecoder.java rename to conjure-java-jaxrs-client/src/main/java/feign/codec/StringDecoder.java index 91150ee99..653d7179f 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/ConjureTextDelegateDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/feign/codec/StringDecoder.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,30 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package feign.codec; -package feign; +import static java.lang.String.format; -import com.palantir.conjure.java.client.jaxrs.feignimpl.TextDelegateDecoder; -import feign.codec.Decoder; +import feign.Response; +import feign.Util; import java.io.IOException; import java.lang.reflect.Type; -/** - * Use {@link TextDelegateDecoder}. - * - * @deprecated Use {@link TextDelegateDecoder}. - */ -@Deprecated -public final class ConjureTextDelegateDecoder implements Decoder { - - private final Decoder delegate; - - public ConjureTextDelegateDecoder(Decoder delegate) { - this.delegate = new TextDelegateDecoder(delegate); - } +public class StringDecoder implements Decoder { @Override - public Object decode(Response response, Type type) throws IOException, FeignException { - return delegate.decode(response, type); + public Object decode(Response response, Type type) throws IOException { + Response.Body body = response.body(); + if (body == null) { + return null; + } + if (String.class.equals(type)) { + return Util.toString(body.asReader()); + } + throw new DecodeException(format("%s is not a type supported by this decoder.", type)); } } diff --git a/conjure-java-jaxrs-client/src/main/java/feign/jackson/JacksonDecoder.java b/conjure-java-jaxrs-client/src/main/java/feign/jackson/JacksonDecoder.java new file mode 100644 index 000000000..3278d5cea --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/jackson/JacksonDecoder.java @@ -0,0 +1,72 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign.jackson; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.RuntimeJsonMappingException; +import feign.Response; +import feign.Util; +import feign.codec.Decoder; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.lang.reflect.Type; +import java.util.Collections; + +public class JacksonDecoder implements Decoder { + + private final ObjectMapper mapper; + + public JacksonDecoder() { + this(Collections.emptyList()); + } + + public JacksonDecoder(Iterable modules) { + this(new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .registerModules(modules)); + } + + public JacksonDecoder(ObjectMapper mapper) { + this.mapper = mapper; + } + + @Override + public Object decode(Response response, Type type) throws IOException { + if (response.status() == 404) return Util.emptyValueOf(type); + if (response.body() == null) return null; + Reader reader = response.body().asReader(); + if (!reader.markSupported()) { + reader = new BufferedReader(reader, 1); + } + try { + // Read the first byte to see if we have any data + reader.mark(1); + if (reader.read() == -1) { + return null; // Eagerly returning null avoids "No content to map due to end-of-input" + } + reader.reset(); + return mapper.readValue(reader, mapper.constructType(type)); + } catch (RuntimeJsonMappingException e) { + if (e.getCause() != null && e.getCause() instanceof IOException) { + throw IOException.class.cast(e.getCause()); + } + throw e; + } + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/jackson/JacksonEncoder.java b/conjure-java-jaxrs-client/src/main/java/feign/jackson/JacksonEncoder.java new file mode 100644 index 000000000..125ac510e --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/feign/jackson/JacksonEncoder.java @@ -0,0 +1,58 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feign.jackson; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import feign.RequestTemplate; +import feign.codec.EncodeException; +import feign.codec.Encoder; +import java.lang.reflect.Type; +import java.util.Collections; + +public class JacksonEncoder implements Encoder { + + private final ObjectMapper mapper; + + public JacksonEncoder() { + this(Collections.emptyList()); + } + + public JacksonEncoder(Iterable modules) { + this(new ObjectMapper() + .setSerializationInclusion(JsonInclude.Include.NON_NULL) + .configure(SerializationFeature.INDENT_OUTPUT, true) + .registerModules(modules)); + } + + public JacksonEncoder(ObjectMapper mapper) { + this.mapper = mapper; + } + + @Override + public void encode(Object object, Type bodyType, RequestTemplate template) { + try { + JavaType javaType = mapper.getTypeFactory().constructType(bodyType); + template.body(mapper.writerFor(javaType).writeValueAsString(object)); + } catch (JsonProcessingException e) { + throw new EncodeException(e.getMessage(), e); + } + } +} diff --git a/versions.lock b/versions.lock index d4f93b8b6..3635219ae 100644 --- a/versions.lock +++ b/versions.lock @@ -6,7 +6,7 @@ com.fasterxml.jackson.core:jackson-annotations:2.21 (12 constraints: 03de1b79) com.fasterxml.jackson.core:jackson-core:2.21.1 (14 constraints: a723ccfe) -com.fasterxml.jackson.core:jackson-databind:2.21.1 (18 constraints: 98637c49) +com.fasterxml.jackson.core:jackson-databind:2.21.1 (17 constraints: fe5524e3) com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.21.1 (2 constraints: 29206505) @@ -44,10 +44,6 @@ com.google.j2objc:j2objc-annotations:3.1 (1 constraints: b809f1a0) com.netflix.concurrency-limits:concurrency-limits-core:0.2.2 (1 constraints: 0605f335) -com.netflix.feign:feign-core:8.18.0 (2 constraints: 1213df52) - -com.netflix.feign:feign-jackson:8.18.0 (1 constraints: 43056b3b) - com.palantir.conjure.java:conjure-lib:8.75.0 (3 constraints: 7625f153) com.palantir.conjure.java.api:errors:2.70.0 (5 constraints: e144f1a9) @@ -206,8 +202,6 @@ org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 (3 constraints: eb27efa7) org.jspecify:jspecify:1.0.0 (20 constraints: 9d42438d) -org.jvnet:animal-sniffer-annotation:1.0 (1 constraints: f20b95eb) - org.mpierce.metrics.reservoir:hdrhistogram-metrics-reservoir:1.1.3 (1 constraints: 0d10f991) org.scala-lang:scala-library:2.12.21 (1 constraints: 3716742c) diff --git a/versions.props b/versions.props index c63d9f43b..39052153a 100644 --- a/versions.props +++ b/versions.props @@ -4,7 +4,6 @@ com.github.ben-manes.caffeine:caffeine = 3.2.3 com.google.code.findbugs:jsr305 = 3.0.2 com.google.guava:guava = 33.5.0-jre com.netflix.concurrency-limits:* = 0.2.2 -com.netflix.feign:feign-*= 8.18.0 com.palantir.conjure.java:* = 8.75.0 com.palantir.conjure.java:conjure-lib = 8.75.0 com.palantir.conjure.java.api:* = 2.70.0 From d5e099e38422e9634790060b9b1df5f1a0330c23 Mon Sep 17 00:00:00 2001 From: Patrick Koenig Date: Thu, 26 Mar 2026 15:40:38 -0400 Subject: [PATCH 2/6] Move to com.palantir.conjure.java.client.jaxrs.feign --- .../AbstractFeignJaxRsClientBuilder.java | 20 +- .../client/jaxrs/CompatibleJaxRsContract.java | 4 +- .../jaxrs/ConjureFeignJacksonEncoder.java | 9 +- .../client/jaxrs/DialogueFeignClient.java | 30 +-- .../java/client/jaxrs}/feign/Body.java | 2 +- .../java/client/jaxrs}/feign/Client.java | 12 +- .../java/client/jaxrs}/feign/Contract.java | 6 +- .../jaxrs}/feign/DefaultMethodHandler.java | 4 +- .../java/client/jaxrs}/feign/Feign.java | 18 +- .../client/jaxrs}/feign/FeignException.java | 2 +- .../java/client/jaxrs}/feign/HeaderMap.java | 2 +- .../java/client/jaxrs}/feign/Headers.java | 2 +- .../feign/InvocationHandlerFactory.java | 2 +- .../java/client/jaxrs}/feign/Logger.java | 8 +- .../client/jaxrs}/feign/MethodMetadata.java | 4 +- .../java/client/jaxrs}/feign/Param.java | 2 +- .../java/client/jaxrs}/feign/QueryMap.java | 2 +- .../client/jaxrs}/feign/ReflectiveFeign.java | 26 +-- .../java/client/jaxrs}/feign/Request.java | 6 +- .../jaxrs}/feign/RequestInterceptor.java | 2 +- .../java/client/jaxrs}/feign/RequestLine.java | 2 +- .../client/jaxrs}/feign/RequestTemplate.java | 18 +- .../java/client/jaxrs}/feign/Response.java | 12 +- .../jaxrs}/feign/RetryableException.java | 4 +- .../java/client/jaxrs}/feign/Retryer.java | 2 +- .../feign/SynchronousMethodHandler.java | 24 +-- .../java/client/jaxrs/feign/Target.java | 189 +++++++++++++++++ .../java/client/jaxrs}/feign/Types.java | 2 +- .../java/client/jaxrs}/feign/Util.java | 2 +- .../java/client/jaxrs/feign/auth/Base64.java | 158 ++++++++++++++ .../auth/BasicAuthRequestInterceptor.java | 10 +- .../jaxrs}/feign/codec/DecodeException.java | 6 +- .../client/jaxrs}/feign/codec/Decoder.java | 12 +- .../jaxrs}/feign/codec/EncodeException.java | 6 +- .../client/jaxrs}/feign/codec/Encoder.java | 8 +- .../jaxrs}/feign/codec/ErrorDecoder.java | 18 +- .../jaxrs}/feign/codec/StringDecoder.java | 6 +- .../jaxrs}/feign/jackson/JacksonDecoder.java | 8 +- .../jaxrs}/feign/jackson/JacksonEncoder.java | 8 +- .../feignimpl/AbstractDelegatingContract.java | 6 +- .../jaxrs/feignimpl/CborDelegateDecoder.java | 6 +- .../jaxrs/feignimpl/CborDelegateEncoder.java | 6 +- .../feignimpl/EmptyContainerDecoder.java | 4 +- .../EndpointNameHeaderEnrichmentContract.java | 4 +- .../feignimpl/GuavaEmptyOptionalExpander.java | 2 +- .../feignimpl/GuavaNullOptionalExpander.java | 2 +- .../feignimpl/GuavaOptionalAwareContract.java | 4 +- .../feignimpl/GuavaOptionalAwareDecoder.java | 6 +- .../jaxrs/feignimpl/HeaderAccessUtils.java | 6 +- .../feignimpl/InputStreamDelegateDecoder.java | 8 +- .../feignimpl/InputStreamDelegateEncoder.java | 8 +- .../Java8EmptyOptionalDoubleExpander.java | 2 +- .../feignimpl/Java8EmptyOptionalExpander.java | 2 +- .../Java8EmptyOptionalIntExpander.java | 2 +- .../Java8EmptyOptionalLongExpander.java | 2 +- .../Java8NullOptionalDoubleExpander.java | 2 +- .../feignimpl/Java8NullOptionalExpander.java | 2 +- .../Java8NullOptionalIntExpander.java | 2 +- .../Java8NullOptionalLongExpander.java | 2 +- .../feignimpl/Java8OptionalAwareContract.java | 6 +- .../feignimpl/Java8OptionalAwareDecoder.java | 6 +- .../MethodHeaderEnrichmentContract.java | 4 +- .../feignimpl/NeverReturnNullDecoder.java | 6 +- .../PathTemplateHeaderEnrichmentContract.java | 4 +- .../feignimpl/PathTemplateHeaderRewriter.java | 4 +- .../jaxrs/feignimpl/QosErrorDecoder.java | 4 +- .../feignimpl/SlashEncodingContract.java | 4 +- .../jaxrs/feignimpl/TextDelegateDecoder.java | 8 +- .../jaxrs/feignimpl/TextDelegateEncoder.java | 8 +- .../src/main/java/feign/Target.java | 193 ------------------ .../src/main/java/feign/auth/Base64.java | 160 --------------- .../feignimpl/EmptyContainerDecoderTest.java | 4 +- .../InputStreamDelegateDecoderTest.java | 4 +- .../InputStreamDelegateEncoderTest.java | 4 +- .../feignimpl/NeverReturnNullDecoderTest.java | 6 +- .../jaxrs/feignimpl/QosErrorDecoderTest.java | 4 +- .../feignimpl/TextDelegateDecoderTest.java | 8 +- .../feignimpl/TextDelegateEncoderTest.java | 4 +- 78 files changed, 588 insertions(+), 589 deletions(-) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/Body.java (96%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/Client.java (93%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/Contract.java (98%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/DefaultMethodHandler.java (95%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/Feign.java (93%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/FeignException.java (97%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/HeaderMap.java (98%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/Headers.java (97%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/InvocationHandlerFactory.java (96%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/Logger.java (96%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/MethodMetadata.java (97%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/Param.java (96%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/QueryMap.java (97%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/ReflectiveFeign.java (94%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/Request.java (95%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/RequestInterceptor.java (97%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/RequestLine.java (97%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/RequestTemplate.java (97%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/Response.java (94%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/RetryableException.java (93%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/Retryer.java (98%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/SynchronousMethodHandler.java (90%) create mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Target.java rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/Types.java (99%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/Util.java (99%) create mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/auth/Base64.java rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/auth/BasicAuthRequestInterceptor.java (87%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/codec/DecodeException.java (87%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/codec/Decoder.java (90%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/codec/EncodeException.java (87%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/codec/Encoder.java (92%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/codec/ErrorDecoder.java (90%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/codec/StringDecoder.java (86%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/jackson/JacksonDecoder.java (90%) rename conjure-java-jaxrs-client/src/main/java/{ => com/palantir/conjure/java/client/jaxrs}/feign/jackson/JacksonEncoder.java (87%) delete mode 100644 conjure-java-jaxrs-client/src/main/java/feign/Target.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/feign/auth/Base64.java diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/AbstractFeignJaxRsClientBuilder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/AbstractFeignJaxRsClientBuilder.java index f757faa83..ffe129301 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/AbstractFeignJaxRsClientBuilder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/AbstractFeignJaxRsClientBuilder.java @@ -21,6 +21,16 @@ import com.palantir.conjure.java.annotations.JaxRsServer; import com.palantir.conjure.java.api.config.service.UserAgent; import com.palantir.conjure.java.client.config.ClientConfiguration; +import com.palantir.conjure.java.client.jaxrs.feign.Contract; +import com.palantir.conjure.java.client.jaxrs.feign.Feign; +import com.palantir.conjure.java.client.jaxrs.feign.Logger; +import com.palantir.conjure.java.client.jaxrs.feign.Request; +import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; +import com.palantir.conjure.java.client.jaxrs.feign.Retryer; +import com.palantir.conjure.java.client.jaxrs.feign.Target; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; +import com.palantir.conjure.java.client.jaxrs.feign.jackson.JacksonDecoder; import com.palantir.conjure.java.client.jaxrs.feignimpl.CborDelegateDecoder; import com.palantir.conjure.java.client.jaxrs.feignimpl.CborDelegateEncoder; import com.palantir.conjure.java.client.jaxrs.feignimpl.EmptyContainerDecoder; @@ -46,16 +56,6 @@ import com.palantir.logsafe.Safe; import com.palantir.logsafe.SafeArg; import com.palantir.logsafe.exceptions.SafeIllegalArgumentException; -import feign.Contract; -import feign.Feign; -import feign.Logger; -import feign.Request; -import feign.RequestTemplate; -import feign.Retryer; -import feign.Target; -import feign.codec.Decoder; -import feign.codec.Encoder; -import feign.jackson.JacksonDecoder; import java.util.Objects; /** Not meant to be implemented outside of this library. */ diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/CompatibleJaxRsContract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/CompatibleJaxRsContract.java index 42361492a..742fe877d 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/CompatibleJaxRsContract.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/CompatibleJaxRsContract.java @@ -18,11 +18,11 @@ import com.google.common.base.Strings; import com.google.common.net.HttpHeaders; import com.palantir.conjure.java.client.jaxrs.JaxRsJakartaCompatibility.Annotations; +import com.palantir.conjure.java.client.jaxrs.feign.Contract; +import com.palantir.conjure.java.client.jaxrs.feign.MethodMetadata; import com.palantir.logsafe.Preconditions; import com.palantir.logsafe.SafeArg; import com.palantir.logsafe.exceptions.SafeIllegalStateException; -import feign.Contract; -import feign.MethodMetadata; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Collection; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/ConjureFeignJacksonEncoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/ConjureFeignJacksonEncoder.java index 600a45f21..b4e9ac6fa 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/ConjureFeignJacksonEncoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/ConjureFeignJacksonEncoder.java @@ -19,14 +19,15 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; -import feign.RequestTemplate; -import feign.codec.EncodeException; -import feign.codec.Encoder; +import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; +import com.palantir.conjure.java.client.jaxrs.feign.codec.EncodeException; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; +import com.palantir.conjure.java.client.jaxrs.feign.jackson.JacksonEncoder; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; /** - * Similar to {@link feign.jackson.JacksonEncoder}, but optimized to avoid intermediate String representation of request + * Similar to {@link JacksonEncoder}, but optimized to avoid intermediate String representation of request * body. See upstream PR https://github.com/OpenFeign/feign/pull/989 . */ final class ConjureFeignJacksonEncoder implements Encoder { diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/DialogueFeignClient.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/DialogueFeignClient.java index de98b3118..c8ebeb995 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/DialogueFeignClient.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/DialogueFeignClient.java @@ -26,6 +26,9 @@ import com.google.common.primitives.Ints; import com.google.common.util.concurrent.UncheckedExecutionException; import com.palantir.conjure.java.api.errors.UnknownRemoteException; +import com.palantir.conjure.java.client.jaxrs.feign.Client; +import com.palantir.conjure.java.client.jaxrs.feign.Request; +import com.palantir.conjure.java.client.jaxrs.feign.codec.ErrorDecoder; import com.palantir.conjure.java.client.jaxrs.feignimpl.EndpointNameHeaderEnrichmentContract; import com.palantir.conjure.java.client.jaxrs.feignimpl.MethodHeaderEnrichmentContract; import com.palantir.dialogue.Channel; @@ -43,7 +46,6 @@ import com.palantir.logsafe.UnsafeArg; import com.palantir.logsafe.exceptions.SafeIllegalStateException; import com.palantir.logsafe.exceptions.SafeUncheckedIoException; -import feign.Request; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -64,10 +66,10 @@ import org.immutables.value.Value; /** - * {@link DialogueFeignClient} is an adapter from {@link feign.Client} to {@link Channel Dialogue Channel} + * {@link DialogueFeignClient} is an adapter from {@link Client} to {@link Channel Dialogue Channel} * taking advantage of the superior observability and stability provided by Dialogue. */ -final class DialogueFeignClient implements feign.Client { +final class DialogueFeignClient implements Client { private static final String REQUEST_URL_PATH_PARAM = "request-url"; private static final Splitter PATH_SPLITTER = Splitter.on('/'); @@ -93,7 +95,7 @@ final class DialogueFeignClient implements feign.Client { } @Override - public feign.Response execute(Request request, Request.Options _options) throws IOException { + public com.palantir.conjure.java.client.jaxrs.feign.Response execute(Request request, Request.Options _options) throws IOException { com.palantir.dialogue.Request.Builder builder = com.palantir.dialogue.Request.builder(); builder.putPathParams(REQUEST_URL_PATH_PARAM, request.url()); @@ -209,7 +211,7 @@ public void close() { } } - private static final class DialogueResponseBody implements feign.Response.Body { + private static final class DialogueResponseBody implements com.palantir.conjure.java.client.jaxrs.feign.Response.Body { private final Response response; @@ -275,12 +277,12 @@ public String toString() { } } - enum FeignResponseDeserializer implements Deserializer { + enum FeignResponseDeserializer implements Deserializer { INSTANCE; @Override - public feign.Response deserialize(Response response) { - return feign.Response.create( + public com.palantir.conjure.java.client.jaxrs.feign.Response deserialize(Response response) { + return com.palantir.conjure.java.client.jaxrs.feign.Response.create( response.code(), null, Multimaps.asMap((Multimap) response.headers()), @@ -297,17 +299,17 @@ public Optional accepts() { /** Converts back from a feign response into a dialogue response for exception mapping. */ private static final class FeignDialogueResponse implements Response { - private final feign.Response delegate; + private final com.palantir.conjure.java.client.jaxrs.feign.Response delegate; private final ResponseAttachments attachments; - FeignDialogueResponse(feign.Response delegate) { + FeignDialogueResponse(com.palantir.conjure.java.client.jaxrs.feign.Response delegate) { this.delegate = delegate; this.attachments = ResponseAttachments.create(); } @Override public InputStream body() { - feign.Response.Body body = delegate.body(); + com.palantir.conjure.java.client.jaxrs.feign.Response.Body body = delegate.body(); if (body != null) { try { return body.asInputStream(); @@ -355,7 +357,7 @@ public String toString() { } /** Implements exception handling equivalent dialogue decoders. */ - static final class RemoteExceptionDecoder implements feign.codec.ErrorDecoder { + static final class RemoteExceptionDecoder implements ErrorDecoder { private final ConjureRuntime runtime; @@ -364,7 +366,7 @@ static final class RemoteExceptionDecoder implements feign.codec.ErrorDecoder { } @Override - public Exception decode(String _methodKey, feign.Response response) { + public Exception decode(String _methodKey, com.palantir.conjure.java.client.jaxrs.feign.Response response) { try { // The dialogue empty body deserializer properly handles exception mapping runtime.bodySerDe().emptyBodyDeserializer().deserialize(new FeignDialogueResponse(response)); @@ -468,7 +470,7 @@ private static FeignEndpointKey of(Request request) { getFirstHeader(request, MethodHeaderEnrichmentContract.METHOD_HEADER) .orElse(""), getFirstHeader(request, EndpointNameHeaderEnrichmentContract.ENDPOINT_NAME_HEADER) - .orElse("feign")); + .orElse("com/palantir/conjure/java/client/jaxrs")); } } } diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Body.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Body.java similarity index 96% rename from conjure-java-jaxrs-client/src/main/java/feign/Body.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Body.java index 67703222c..f1974bb85 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/Body.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Body.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package feign; +package com.palantir.conjure.java.client.jaxrs.feign; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Client.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Client.java similarity index 93% rename from conjure-java-jaxrs-client/src/main/java/feign/Client.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Client.java index 9a3a66c0a..c360d4437 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/Client.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Client.java @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign; +package com.palantir.conjure.java.client.jaxrs.feign; -import static feign.Util.CONTENT_ENCODING; -import static feign.Util.CONTENT_LENGTH; -import static feign.Util.ENCODING_DEFLATE; -import static feign.Util.ENCODING_GZIP; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.CONTENT_ENCODING; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.CONTENT_LENGTH; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.ENCODING_DEFLATE; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.ENCODING_GZIP; import static java.lang.String.format; -import feign.Request.Options; +import com.palantir.conjure.java.client.jaxrs.feign.Request.Options; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Contract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Contract.java similarity index 98% rename from conjure-java-jaxrs-client/src/main/java/feign/Contract.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Contract.java index 8fd534f4a..bb863aca7 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/Contract.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Contract.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign; +package com.palantir.conjure.java.client.jaxrs.feign; -import static feign.Util.checkState; -import static feign.Util.emptyToNull; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkState; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.emptyToNull; import java.lang.annotation.Annotation; import java.lang.reflect.Method; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/DefaultMethodHandler.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/DefaultMethodHandler.java similarity index 95% rename from conjure-java-jaxrs-client/src/main/java/feign/DefaultMethodHandler.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/DefaultMethodHandler.java index e13501ac2..d3c1aaafc 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/DefaultMethodHandler.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/DefaultMethodHandler.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package feign; +package com.palantir.conjure.java.client.jaxrs.feign; -import feign.InvocationHandlerFactory.MethodHandler; +import com.palantir.conjure.java.client.jaxrs.feign.InvocationHandlerFactory.MethodHandler; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles.Lookup; import java.lang.reflect.Field; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Feign.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Feign.java similarity index 93% rename from conjure-java-jaxrs-client/src/main/java/feign/Feign.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Feign.java index acd9349e5..c3396c05b 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/Feign.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Feign.java @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign; - -import feign.Logger.NoOpLogger; -import feign.ReflectiveFeign.ParseHandlersByName; -import feign.Request.Options; -import feign.Target.HardCodedTarget; -import feign.codec.Decoder; -import feign.codec.Encoder; -import feign.codec.ErrorDecoder; +package com.palantir.conjure.java.client.jaxrs.feign; + +import com.palantir.conjure.java.client.jaxrs.feign.Logger.NoOpLogger; +import com.palantir.conjure.java.client.jaxrs.feign.ReflectiveFeign.ParseHandlersByName; +import com.palantir.conjure.java.client.jaxrs.feign.Request.Options; +import com.palantir.conjure.java.client.jaxrs.feign.Target.HardCodedTarget; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; +import com.palantir.conjure.java.client.jaxrs.feign.codec.ErrorDecoder; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/FeignException.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/FeignException.java similarity index 97% rename from conjure-java-jaxrs-client/src/main/java/feign/FeignException.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/FeignException.java index cd03514b8..2eb6c93bf 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/FeignException.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/FeignException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign; +package com.palantir.conjure.java.client.jaxrs.feign; import static java.lang.String.format; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/HeaderMap.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/HeaderMap.java similarity index 98% rename from conjure-java-jaxrs-client/src/main/java/feign/HeaderMap.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/HeaderMap.java index 16ef4b9cc..2100cd766 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/HeaderMap.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/HeaderMap.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package feign; +package com.palantir.conjure.java.client.jaxrs.feign; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Headers.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Headers.java similarity index 97% rename from conjure-java-jaxrs-client/src/main/java/feign/Headers.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Headers.java index 836767a0f..ef78fcb30 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/Headers.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Headers.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package feign; +package com.palantir.conjure.java.client.jaxrs.feign; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.TYPE; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/InvocationHandlerFactory.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/InvocationHandlerFactory.java similarity index 96% rename from conjure-java-jaxrs-client/src/main/java/feign/InvocationHandlerFactory.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/InvocationHandlerFactory.java index 62ef9462f..13b2d704c 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/InvocationHandlerFactory.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/InvocationHandlerFactory.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign; +package com.palantir.conjure.java.client.jaxrs.feign; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Logger.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Logger.java similarity index 96% rename from conjure-java-jaxrs-client/src/main/java/feign/Logger.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Logger.java index 89ed2db3b..35468f5d2 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/Logger.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Logger.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign; +package com.palantir.conjure.java.client.jaxrs.feign; -import static feign.Util.UTF_8; -import static feign.Util.decodeOrDefault; -import static feign.Util.valuesOrEmpty; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.UTF_8; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.decodeOrDefault; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.valuesOrEmpty; import java.io.IOException; import java.io.PrintWriter; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/MethodMetadata.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/MethodMetadata.java similarity index 97% rename from conjure-java-jaxrs-client/src/main/java/feign/MethodMetadata.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/MethodMetadata.java index fa6427642..c833cf252 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/MethodMetadata.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/MethodMetadata.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign; +package com.palantir.conjure.java.client.jaxrs.feign; -import feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; import java.io.Serializable; import java.lang.reflect.Type; import java.util.ArrayList; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Param.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Param.java similarity index 96% rename from conjure-java-jaxrs-client/src/main/java/feign/Param.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Param.java index f78b64c78..ba504a1db 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/Param.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Param.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign; +package com.palantir.conjure.java.client.jaxrs.feign; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/QueryMap.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/QueryMap.java similarity index 97% rename from conjure-java-jaxrs-client/src/main/java/feign/QueryMap.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/QueryMap.java index aa6b70176..baea5645e 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/QueryMap.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/QueryMap.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign; +package com.palantir.conjure.java.client.jaxrs.feign; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/ReflectiveFeign.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/ReflectiveFeign.java similarity index 94% rename from conjure-java-jaxrs-client/src/main/java/feign/ReflectiveFeign.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/ReflectiveFeign.java index e7bb013af..4f9b3ff37 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/ReflectiveFeign.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/ReflectiveFeign.java @@ -13,19 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign; - -import static feign.Util.checkArgument; -import static feign.Util.checkNotNull; -import static feign.Util.checkState; - -import feign.InvocationHandlerFactory.MethodHandler; -import feign.Param.Expander; -import feign.Request.Options; -import feign.codec.Decoder; -import feign.codec.EncodeException; -import feign.codec.Encoder; -import feign.codec.ErrorDecoder; +package com.palantir.conjure.java.client.jaxrs.feign; + +import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkArgument; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkState; + +import com.palantir.conjure.java.client.jaxrs.feign.InvocationHandlerFactory.MethodHandler; +import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Request.Options; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; +import com.palantir.conjure.java.client.jaxrs.feign.codec.EncodeException; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; +import com.palantir.conjure.java.client.jaxrs.feign.codec.ErrorDecoder; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Request.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Request.java similarity index 95% rename from conjure-java-jaxrs-client/src/main/java/feign/Request.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Request.java index 9aaddf51e..8088886f6 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/Request.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Request.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign; +package com.palantir.conjure.java.client.jaxrs.feign; -import static feign.Util.checkNotNull; -import static feign.Util.valuesOrEmpty; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.valuesOrEmpty; import java.nio.charset.Charset; import java.util.Collection; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/RequestInterceptor.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestInterceptor.java similarity index 97% rename from conjure-java-jaxrs-client/src/main/java/feign/RequestInterceptor.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestInterceptor.java index b74a0dd36..5c00ba6fb 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/RequestInterceptor.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestInterceptor.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign; +package com.palantir.conjure.java.client.jaxrs.feign; /** * Zero or more {@code RequestInterceptors} may be configured for purposes such as adding headers to diff --git a/conjure-java-jaxrs-client/src/main/java/feign/RequestLine.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestLine.java similarity index 97% rename from conjure-java-jaxrs-client/src/main/java/feign/RequestLine.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestLine.java index ddb7b0259..07fde1598 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/RequestLine.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestLine.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package feign; +package com.palantir.conjure.java.client.jaxrs.feign; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/RequestTemplate.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestTemplate.java similarity index 97% rename from conjure-java-jaxrs-client/src/main/java/feign/RequestTemplate.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestTemplate.java index 2664bfce6..c5be7b04d 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/RequestTemplate.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestTemplate.java @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign; +package com.palantir.conjure.java.client.jaxrs.feign; -import static feign.Util.CONTENT_LENGTH; -import static feign.Util.UTF_8; -import static feign.Util.checkArgument; -import static feign.Util.checkNotNull; -import static feign.Util.emptyToNull; -import static feign.Util.toArray; -import static feign.Util.valuesOrEmpty; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.CONTENT_LENGTH; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.UTF_8; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkArgument; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.emptyToNull; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.toArray; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.valuesOrEmpty; -import feign.codec.Encoder; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Response.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Response.java similarity index 94% rename from conjure-java-jaxrs-client/src/main/java/feign/Response.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Response.java index 9045fe209..2f331e4c8 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/Response.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Response.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign; +package com.palantir.conjure.java.client.jaxrs.feign; -import static feign.Util.UTF_8; -import static feign.Util.checkNotNull; -import static feign.Util.checkState; -import static feign.Util.decodeOrDefault; -import static feign.Util.valuesOrEmpty; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.UTF_8; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkState; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.decodeOrDefault; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.valuesOrEmpty; import java.io.ByteArrayInputStream; import java.io.Closeable; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/RetryableException.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RetryableException.java similarity index 93% rename from conjure-java-jaxrs-client/src/main/java/feign/RetryableException.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RetryableException.java index a0253d03d..de4bb4156 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/RetryableException.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RetryableException.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign; +package com.palantir.conjure.java.client.jaxrs.feign; -import feign.codec.ErrorDecoder; +import com.palantir.conjure.java.client.jaxrs.feign.codec.ErrorDecoder; import java.util.Date; /** diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Retryer.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Retryer.java similarity index 98% rename from conjure-java-jaxrs-client/src/main/java/feign/Retryer.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Retryer.java index f5eae7e20..ea5e05957 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/Retryer.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Retryer.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign; +package com.palantir.conjure.java.client.jaxrs.feign; import static java.util.concurrent.TimeUnit.SECONDS; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/SynchronousMethodHandler.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/SynchronousMethodHandler.java similarity index 90% rename from conjure-java-jaxrs-client/src/main/java/feign/SynchronousMethodHandler.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/SynchronousMethodHandler.java index 740721325..ed7e1fe68 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/SynchronousMethodHandler.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/SynchronousMethodHandler.java @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign; - -import static feign.FeignException.errorExecuting; -import static feign.FeignException.errorReading; -import static feign.Util.checkNotNull; -import static feign.Util.ensureClosed; - -import feign.InvocationHandlerFactory.MethodHandler; -import feign.Request.Options; -import feign.codec.DecodeException; -import feign.codec.Decoder; -import feign.codec.ErrorDecoder; +package com.palantir.conjure.java.client.jaxrs.feign; + +import static com.palantir.conjure.java.client.jaxrs.feign.FeignException.errorExecuting; +import static com.palantir.conjure.java.client.jaxrs.feign.FeignException.errorReading; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.ensureClosed; + +import com.palantir.conjure.java.client.jaxrs.feign.InvocationHandlerFactory.MethodHandler; +import com.palantir.conjure.java.client.jaxrs.feign.Request.Options; +import com.palantir.conjure.java.client.jaxrs.feign.codec.DecodeException; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; +import com.palantir.conjure.java.client.jaxrs.feign.codec.ErrorDecoder; import java.io.IOException; import java.util.List; import java.util.concurrent.TimeUnit; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Target.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Target.java new file mode 100644 index 000000000..348ae1112 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Target.java @@ -0,0 +1,189 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.palantir.conjure.java.client.jaxrs.feign; + +import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.emptyToNull; + +/** + *

relationship to JAXRS 2.0

Similar to {@code + * javax.ws.rs.client.WebTarget}, as it produces requests. However, {@link RequestTemplate} is a + * closer match to {@code WebTarget}. + * + * @param type of the interface this target applies to. + */ +public interface Target { + + /* The type of the interface this target applies to. ex. {@code Route53}. */ + Class type(); + + /* configuration key associated with this target. For example, {@code route53}. */ + String name(); + + /* base HTTP URL of the target. For example, {@code https://api/v2}. */ + String url(); + + /** + * Targets a template to this target, adding the {@link #url() base url} and any target-specific + * headers or query parameters.

For example:
+ *

+     * public Request apply(RequestTemplate input) {
+     *     input.insert(0, url());
+     *     input.replaceHeader("X-Auth", currentToken);
+     *     return input.asRequest();
+     * }
+     * 
+ *


relationship to JAXRS 2.0

This call is similar to {@code + * javax.ws.rs.client.WebTarget.request()}, except that we expect transient, but necessary + * decoration to be applied on invocation. + */ + public Request apply(RequestTemplate input); + + public static class HardCodedTarget implements Target { + + private final Class type; + private final String name; + private final String url; + + public HardCodedTarget(Class type, String url) { + this(type, url, url); + } + + public HardCodedTarget(Class type, String name, String url) { + this.type = checkNotNull(type, "type"); + this.name = checkNotNull(emptyToNull(name), "name"); + this.url = checkNotNull(emptyToNull(url), "url"); + } + + @Override + public Class type() { + return type; + } + + @Override + public String name() { + return name; + } + + @Override + public String url() { + return url; + } + + /* no authentication or other special activity. just insert the url. */ + @Override + public Request apply(RequestTemplate input) { + if (input.url().indexOf("http") != 0) { + input.insert(0, url()); + } + return input.request(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof HardCodedTarget) { + HardCodedTarget other = (HardCodedTarget) obj; + return type.equals(other.type) && name.equals(other.name) && url.equals(other.url); + } + return false; + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + type.hashCode(); + result = 31 * result + name.hashCode(); + result = 31 * result + url.hashCode(); + return result; + } + + @Override + public String toString() { + if (name.equals(url)) { + return "HardCodedTarget(type=" + type.getSimpleName() + ", url=" + url + ")"; + } + return "HardCodedTarget(type=" + type.getSimpleName() + ", name=" + name + ", url=" + url + ")"; + } + } + + public static final class EmptyTarget implements Target { + + private final Class type; + private final String name; + + EmptyTarget(Class type, String name) { + this.type = checkNotNull(type, "type"); + this.name = checkNotNull(emptyToNull(name), "name"); + } + + public static EmptyTarget create(Class type) { + return new EmptyTarget(type, "empty:" + type.getSimpleName()); + } + + public static EmptyTarget create(Class type, String name) { + return new EmptyTarget(type, name); + } + + @Override + public Class type() { + return type; + } + + @Override + public String name() { + return name; + } + + @Override + public String url() { + throw new UnsupportedOperationException("Empty targets don't have URLs"); + } + + @Override + public Request apply(RequestTemplate input) { + if (input.url().indexOf("http") != 0) { + throw new UnsupportedOperationException( + "Request with non-absolute URL not supported with empty target"); + } + return input.request(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof EmptyTarget) { + EmptyTarget other = (EmptyTarget) obj; + return type.equals(other.type) && name.equals(other.name); + } + return false; + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + type.hashCode(); + result = 31 * result + name.hashCode(); + return result; + } + + @Override + public String toString() { + if (name.equals("empty:" + type.getSimpleName())) { + return "EmptyTarget(type=" + type.getSimpleName() + ")"; + } + return "EmptyTarget(type=" + type.getSimpleName() + ", name=" + name + ")"; + } + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Types.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Types.java similarity index 99% rename from conjure-java-jaxrs-client/src/main/java/feign/Types.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Types.java index 9d9a87bf1..b4d6525f8 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/Types.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Types.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign; +package com.palantir.conjure.java.client.jaxrs.feign; import java.lang.reflect.Array; import java.lang.reflect.GenericArrayType; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Util.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Util.java similarity index 99% rename from conjure-java-jaxrs-client/src/main/java/feign/Util.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Util.java index dc0541752..eb324964e 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/Util.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Util.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign; +package com.palantir.conjure.java.client.jaxrs.feign; import static java.lang.String.format; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/auth/Base64.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/auth/Base64.java new file mode 100644 index 000000000..4c6791a12 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/auth/Base64.java @@ -0,0 +1,158 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.palantir.conjure.java.client.jaxrs.feign.auth; + +import java.io.UnsupportedEncodingException; + +/** + * copied from okhttp + * + * @author Alexander Y. Kleymenov + */ +final class Base64 { + + public static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; + private static final byte[] MAP = new byte[] { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', + 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', + '5', '6', '7', '8', '9', '+', '/' + }; + + private Base64() {} + + public static byte[] decode(byte[] in) { + return decode(in, in.length); + } + + public static byte[] decode(byte[] in, int len) { + // approximate output length + int length = len / 4 * 3; + // return an empty array on empty or short input without padding + if (length == 0) { + return EMPTY_BYTE_ARRAY; + } + // temporary array + byte[] out = new byte[length]; + // number of padding characters ('=') + int pad = 0; + byte chr; + // compute the number of the padding characters + // and adjust the length of the input + for (; ; len--) { + chr = in[len - 1]; + // skip the neutral characters + if ((chr == '\n') || (chr == '\r') || (chr == ' ') || (chr == '\t')) { + continue; + } + if (chr == '=') { + pad++; + } else { + break; + } + } + // index in the output array + int outIndex = 0; + // index in the input array + int inIndex = 0; + // holds the value of the input character + int bits = 0; + // holds the value of the input quantum + int quantum = 0; + for (int i = 0; i < len; i++) { + chr = in[i]; + // skip the neutral characters + if ((chr == '\n') || (chr == '\r') || (chr == ' ') || (chr == '\t')) { + continue; + } + if ((chr >= 'A') && (chr <= 'Z')) { + // char ASCII value + // A 65 0 + // Z 90 25 (ASCII - 65) + bits = chr - 65; + } else if ((chr >= 'a') && (chr <= 'z')) { + // char ASCII value + // a 97 26 + // z 122 51 (ASCII - 71) + bits = chr - 71; + } else if ((chr >= '0') && (chr <= '9')) { + // char ASCII value + // 0 48 52 + // 9 57 61 (ASCII + 4) + bits = chr + 4; + } else if (chr == '+') { + bits = 62; + } else if (chr == '/') { + bits = 63; + } else { + return null; + } + // append the value to the quantum + quantum = (quantum << 6) | (byte) bits; + if (inIndex % 4 == 3) { + // 4 characters were read, so make the output: + out[outIndex++] = (byte) (quantum >> 16); + out[outIndex++] = (byte) (quantum >> 8); + out[outIndex++] = (byte) quantum; + } + inIndex++; + } + if (pad > 0) { + // adjust the quantum value according to the padding + quantum = quantum << (6 * pad); + // make output + out[outIndex++] = (byte) (quantum >> 16); + if (pad == 1) { + out[outIndex++] = (byte) (quantum >> 8); + } + } + // create the resulting array + byte[] result = new byte[outIndex]; + System.arraycopy(out, 0, result, 0, outIndex); + return result; + } + + public static String encode(byte[] in) { + int length = (in.length + 2) * 4 / 3; + byte[] out = new byte[length]; + int index = 0, end = in.length - in.length % 3; + for (int i = 0; i < end; i += 3) { + out[index++] = MAP[(in[i] & 0xff) >> 2]; + out[index++] = MAP[((in[i] & 0x03) << 4) | ((in[i + 1] & 0xff) >> 4)]; + out[index++] = MAP[((in[i + 1] & 0x0f) << 2) | ((in[i + 2] & 0xff) >> 6)]; + out[index++] = MAP[(in[i + 2] & 0x3f)]; + } + switch (in.length % 3) { + case 1: + out[index++] = MAP[(in[end] & 0xff) >> 2]; + out[index++] = MAP[(in[end] & 0x03) << 4]; + out[index++] = '='; + out[index++] = '='; + break; + case 2: + out[index++] = MAP[(in[end] & 0xff) >> 2]; + out[index++] = MAP[((in[end] & 0x03) << 4) | ((in[end + 1] & 0xff) >> 4)]; + out[index++] = MAP[((in[end + 1] & 0x0f) << 2)]; + out[index++] = '='; + break; + } + try { + return new String(out, 0, index, "US-ASCII"); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); + } + } +} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/auth/BasicAuthRequestInterceptor.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/auth/BasicAuthRequestInterceptor.java similarity index 87% rename from conjure-java-jaxrs-client/src/main/java/feign/auth/BasicAuthRequestInterceptor.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/auth/BasicAuthRequestInterceptor.java index 713c08b94..2fa8c25ca 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/auth/BasicAuthRequestInterceptor.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/auth/BasicAuthRequestInterceptor.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign.auth; +package com.palantir.conjure.java.client.jaxrs.feign.auth; -import static feign.Util.ISO_8859_1; -import static feign.Util.checkNotNull; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.ISO_8859_1; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; -import feign.RequestInterceptor; -import feign.RequestTemplate; +import com.palantir.conjure.java.client.jaxrs.feign.RequestInterceptor; +import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; import java.nio.charset.Charset; /** diff --git a/conjure-java-jaxrs-client/src/main/java/feign/codec/DecodeException.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/DecodeException.java similarity index 87% rename from conjure-java-jaxrs-client/src/main/java/feign/codec/DecodeException.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/DecodeException.java index e9fcff579..a2addcdd0 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/codec/DecodeException.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/DecodeException.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign.codec; +package com.palantir.conjure.java.client.jaxrs.feign.codec; -import static feign.Util.checkNotNull; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; -import feign.FeignException; +import com.palantir.conjure.java.client.jaxrs.feign.FeignException; /** * Similar to {@code javax.websocket.DecodeException}, raised when a problem occurs decoding a diff --git a/conjure-java-jaxrs-client/src/main/java/feign/codec/Decoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/Decoder.java similarity index 90% rename from conjure-java-jaxrs-client/src/main/java/feign/codec/Decoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/Decoder.java index cb64890c4..ca7480931 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/codec/Decoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/Decoder.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign.codec; +package com.palantir.conjure.java.client.jaxrs.feign.codec; -import feign.Feign; -import feign.FeignException; -import feign.Response; -import feign.Target; -import feign.Util; +import com.palantir.conjure.java.client.jaxrs.feign.Feign; +import com.palantir.conjure.java.client.jaxrs.feign.FeignException; +import com.palantir.conjure.java.client.jaxrs.feign.Response; +import com.palantir.conjure.java.client.jaxrs.feign.Target; +import com.palantir.conjure.java.client.jaxrs.feign.Util; import java.io.IOException; import java.lang.reflect.Type; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/codec/EncodeException.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/EncodeException.java similarity index 87% rename from conjure-java-jaxrs-client/src/main/java/feign/codec/EncodeException.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/EncodeException.java index 4e82c15ca..9f8f6e395 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/codec/EncodeException.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/EncodeException.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign.codec; +package com.palantir.conjure.java.client.jaxrs.feign.codec; -import static feign.Util.checkNotNull; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; -import feign.FeignException; +import com.palantir.conjure.java.client.jaxrs.feign.FeignException; /** * Similar to {@code javax.websocket.EncodeException}, raised when a problem occurs encoding a diff --git a/conjure-java-jaxrs-client/src/main/java/feign/codec/Encoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/Encoder.java similarity index 92% rename from conjure-java-jaxrs-client/src/main/java/feign/codec/Encoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/Encoder.java index c03dd5e96..a75a23646 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/codec/Encoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/Encoder.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign.codec; +package com.palantir.conjure.java.client.jaxrs.feign.codec; import static java.lang.String.format; -import feign.MethodMetadata; -import feign.RequestTemplate; -import feign.Util; +import com.palantir.conjure.java.client.jaxrs.feign.MethodMetadata; +import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; +import com.palantir.conjure.java.client.jaxrs.feign.Util; import java.lang.reflect.Type; /** diff --git a/conjure-java-jaxrs-client/src/main/java/feign/codec/ErrorDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/ErrorDecoder.java similarity index 90% rename from conjure-java-jaxrs-client/src/main/java/feign/codec/ErrorDecoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/ErrorDecoder.java index 3c1cda799..123502544 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/codec/ErrorDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/ErrorDecoder.java @@ -13,19 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign.codec; +package com.palantir.conjure.java.client.jaxrs.feign.codec; -import static feign.FeignException.errorStatus; -import static feign.Util.RETRY_AFTER; -import static feign.Util.checkNotNull; +import static com.palantir.conjure.java.client.jaxrs.feign.FeignException.errorStatus; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.RETRY_AFTER; +import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; import static java.util.Locale.US; import static java.util.concurrent.TimeUnit.SECONDS; -import feign.Feign; -import feign.FeignException; -import feign.Response; -import feign.RetryableException; -import feign.Util; +import com.palantir.conjure.java.client.jaxrs.feign.Feign; +import com.palantir.conjure.java.client.jaxrs.feign.FeignException; +import com.palantir.conjure.java.client.jaxrs.feign.Response; +import com.palantir.conjure.java.client.jaxrs.feign.RetryableException; +import com.palantir.conjure.java.client.jaxrs.feign.Util; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/codec/StringDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/StringDecoder.java similarity index 86% rename from conjure-java-jaxrs-client/src/main/java/feign/codec/StringDecoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/StringDecoder.java index 653d7179f..e2c936564 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/codec/StringDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/StringDecoder.java @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign.codec; +package com.palantir.conjure.java.client.jaxrs.feign.codec; import static java.lang.String.format; -import feign.Response; -import feign.Util; +import com.palantir.conjure.java.client.jaxrs.feign.Response; +import com.palantir.conjure.java.client.jaxrs.feign.Util; import java.io.IOException; import java.lang.reflect.Type; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/jackson/JacksonDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/jackson/JacksonDecoder.java similarity index 90% rename from conjure-java-jaxrs-client/src/main/java/feign/jackson/JacksonDecoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/jackson/JacksonDecoder.java index 3278d5cea..4c3a1e91c 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/jackson/JacksonDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/jackson/JacksonDecoder.java @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign.jackson; +package com.palantir.conjure.java.client.jaxrs.feign.jackson; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.RuntimeJsonMappingException; -import feign.Response; -import feign.Util; -import feign.codec.Decoder; +import com.palantir.conjure.java.client.jaxrs.feign.Response; +import com.palantir.conjure.java.client.jaxrs.feign.Util; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; diff --git a/conjure-java-jaxrs-client/src/main/java/feign/jackson/JacksonEncoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/jackson/JacksonEncoder.java similarity index 87% rename from conjure-java-jaxrs-client/src/main/java/feign/jackson/JacksonEncoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/jackson/JacksonEncoder.java index 125ac510e..1ee3529c6 100644 --- a/conjure-java-jaxrs-client/src/main/java/feign/jackson/JacksonEncoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/jackson/JacksonEncoder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package feign.jackson; +package com.palantir.conjure.java.client.jaxrs.feign.jackson; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; @@ -21,9 +21,9 @@ import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; -import feign.RequestTemplate; -import feign.codec.EncodeException; -import feign.codec.Encoder; +import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; +import com.palantir.conjure.java.client.jaxrs.feign.codec.EncodeException; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; import java.lang.reflect.Type; import java.util.Collections; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/AbstractDelegatingContract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/AbstractDelegatingContract.java index 4dcabca29..91066fad6 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/AbstractDelegatingContract.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/AbstractDelegatingContract.java @@ -16,9 +16,9 @@ package com.palantir.conjure.java.client.jaxrs.feignimpl; -import feign.Contract; -import feign.Feign; -import feign.MethodMetadata; +import com.palantir.conjure.java.client.jaxrs.feign.Contract; +import com.palantir.conjure.java.client.jaxrs.feign.Feign; +import com.palantir.conjure.java.client.jaxrs.feign.MethodMetadata; import java.lang.reflect.Method; import java.util.LinkedHashMap; import java.util.List; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CborDelegateDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CborDelegateDecoder.java index 830ef1d76..6afd493d1 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CborDelegateDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CborDelegateDecoder.java @@ -20,9 +20,9 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.net.HttpHeaders; -import feign.FeignException; -import feign.Response; -import feign.codec.Decoder; +import com.palantir.conjure.java.client.jaxrs.feign.FeignException; +import com.palantir.conjure.java.client.jaxrs.feign.Response; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import java.io.IOException; import java.io.PushbackInputStream; import java.lang.reflect.Type; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CborDelegateEncoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CborDelegateEncoder.java index 2f62e6c11..bbcf7f3bb 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CborDelegateEncoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CborDelegateEncoder.java @@ -21,9 +21,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableSet; import com.google.common.net.HttpHeaders; -import feign.RequestTemplate; -import feign.codec.EncodeException; -import feign.codec.Encoder; +import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; +import com.palantir.conjure.java.client.jaxrs.feign.codec.EncodeException; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; import java.io.UncheckedIOException; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/EmptyContainerDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/EmptyContainerDecoder.java index a1c2dc169..9ed48ebba 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/EmptyContainerDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/EmptyContainerDecoder.java @@ -21,12 +21,12 @@ import com.github.benmanes.caffeine.cache.CacheLoader; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; +import com.palantir.conjure.java.client.jaxrs.feign.Response; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import com.palantir.logsafe.Preconditions; import com.palantir.logsafe.SafeArg; import com.palantir.logsafe.logger.SafeLogger; import com.palantir.logsafe.logger.SafeLoggerFactory; -import feign.Response; -import feign.codec.Decoder; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/EndpointNameHeaderEnrichmentContract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/EndpointNameHeaderEnrichmentContract.java index 208969303..00f21ce86 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/EndpointNameHeaderEnrichmentContract.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/EndpointNameHeaderEnrichmentContract.java @@ -16,12 +16,12 @@ package com.palantir.conjure.java.client.jaxrs.feignimpl; +import com.palantir.conjure.java.client.jaxrs.feign.Contract; +import com.palantir.conjure.java.client.jaxrs.feign.MethodMetadata; import com.palantir.dialogue.HttpMethod; import com.palantir.logsafe.SafeArg; import com.palantir.logsafe.exceptions.SafeIllegalArgumentException; import com.palantir.logsafe.exceptions.SafeNullPointerException; -import feign.Contract; -import feign.MethodMetadata; import java.lang.reflect.Method; import java.util.Locale; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaEmptyOptionalExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaEmptyOptionalExpander.java index a562bef98..f305f0482 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaEmptyOptionalExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaEmptyOptionalExpander.java @@ -17,7 +17,7 @@ package com.palantir.conjure.java.client.jaxrs.feignimpl; import com.google.common.base.Preconditions; -import feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; import java.util.Objects; /** diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaNullOptionalExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaNullOptionalExpander.java index 7d84b08b9..c7219bfeb 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaNullOptionalExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaNullOptionalExpander.java @@ -17,7 +17,7 @@ package com.palantir.conjure.java.client.jaxrs.feignimpl; import com.google.common.base.Preconditions; -import feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; import java.util.Objects; /** diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalAwareContract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalAwareContract.java index 8bb69a344..a4eab1253 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalAwareContract.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalAwareContract.java @@ -17,8 +17,8 @@ package com.palantir.conjure.java.client.jaxrs.feignimpl; import com.palantir.conjure.java.client.jaxrs.JaxRsJakartaCompatibility.Annotations; -import feign.Contract; -import feign.MethodMetadata; +import com.palantir.conjure.java.client.jaxrs.feign.Contract; +import com.palantir.conjure.java.client.jaxrs.feign.MethodMetadata; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Arrays; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalAwareDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalAwareDecoder.java index a0036021e..d6116c2f3 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalAwareDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalAwareDecoder.java @@ -18,9 +18,9 @@ import static com.google.common.base.Preconditions.checkNotNull; -import feign.FeignException; -import feign.Response; -import feign.codec.Decoder; +import com.palantir.conjure.java.client.jaxrs.feign.FeignException; +import com.palantir.conjure.java.client.jaxrs.feign.Response; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import java.io.IOException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/HeaderAccessUtils.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/HeaderAccessUtils.java index c043ded03..f310c600f 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/HeaderAccessUtils.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/HeaderAccessUtils.java @@ -16,6 +16,8 @@ package com.palantir.conjure.java.client.jaxrs.feignimpl; +import com.palantir.conjure.java.client.jaxrs.feign.Request; +import com.palantir.conjure.java.client.jaxrs.feign.Response; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -24,9 +26,9 @@ /** * Used to access headers in a case-insensitive manner. This is necessary for compatibility with OkHttp 3.3.0+ as it * lower-cases header names whereas we use constants from {@link com.google.common.net.HttpHeaders} where header names - * are in Train-Case. This can be removed once {@link feign.Request} and {@link feign.Response} expose the headers as a + * are in Train-Case. This can be removed once {@link Request} and {@link Response} expose the headers as a * map which is case-insensitive with respect to the key. com.netflix.feign:feign-core:8.18.0 will have it for the - * {@link feign.Response} headers due to https://github.com/Netflix/feign/pull/418. + * {@link Response} headers due to https://github.com/Netflix/feign/pull/418. */ public final class HeaderAccessUtils { private HeaderAccessUtils() {} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateDecoder.java index f139dc50a..d93daa51b 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateDecoder.java @@ -17,13 +17,13 @@ package com.palantir.conjure.java.client.jaxrs.feignimpl; import com.codahale.metrics.Meter; +import com.palantir.conjure.java.client.jaxrs.feign.FeignException; +import com.palantir.conjure.java.client.jaxrs.feign.Response; +import com.palantir.conjure.java.client.jaxrs.feign.Util; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import com.palantir.conjure.java.client.jaxrs.feignimpl.FeignClientMetrics.DangerousBuffering_Direction; import com.palantir.logsafe.Safe; import com.palantir.tritium.metrics.registry.SharedTaggedMetricRegistries; -import feign.FeignException; -import feign.Response; -import feign.Util; -import feign.codec.Decoder; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateEncoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateEncoder.java index e75c16022..60d21adca 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateEncoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateEncoder.java @@ -17,13 +17,13 @@ package com.palantir.conjure.java.client.jaxrs.feignimpl; import com.codahale.metrics.Meter; +import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; +import com.palantir.conjure.java.client.jaxrs.feign.Util; +import com.palantir.conjure.java.client.jaxrs.feign.codec.EncodeException; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; import com.palantir.conjure.java.client.jaxrs.feignimpl.FeignClientMetrics.DangerousBuffering_Direction; import com.palantir.logsafe.Safe; import com.palantir.tritium.metrics.registry.SharedTaggedMetricRegistries; -import feign.RequestTemplate; -import feign.Util; -import feign.codec.EncodeException; -import feign.codec.Encoder; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalDoubleExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalDoubleExpander.java index 3d1b9a459..df2bce964 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalDoubleExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalDoubleExpander.java @@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument; -import feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; import java.util.OptionalDouble; /** diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalExpander.java index d90624438..c845cd272 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalExpander.java @@ -17,7 +17,7 @@ package com.palantir.conjure.java.client.jaxrs.feignimpl; import com.google.common.base.Preconditions; -import feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; import java.util.Objects; import java.util.Optional; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalIntExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalIntExpander.java index 63e5043c8..9a1b239b9 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalIntExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalIntExpander.java @@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument; -import feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; import java.util.OptionalInt; /** diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalLongExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalLongExpander.java index 1ca30f647..b0fafc1dc 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalLongExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalLongExpander.java @@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument; -import feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; import java.util.OptionalLong; /** diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalDoubleExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalDoubleExpander.java index 880736632..5a8bdf576 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalDoubleExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalDoubleExpander.java @@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument; -import feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; import java.util.OptionalDouble; /** diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalExpander.java index 56bde28bd..149709008 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalExpander.java @@ -17,7 +17,7 @@ package com.palantir.conjure.java.client.jaxrs.feignimpl; import com.google.common.base.Preconditions; -import feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; import java.util.Objects; import java.util.Optional; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalIntExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalIntExpander.java index 51e20a8b8..bd40f6c86 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalIntExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalIntExpander.java @@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument; -import feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; import java.util.OptionalInt; /** diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalLongExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalLongExpander.java index fb1d3cd76..e2fff40c5 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalLongExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalLongExpander.java @@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument; -import feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; import java.util.OptionalLong; /** diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareContract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareContract.java index 4444a36b4..247fe3dfc 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareContract.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareContract.java @@ -18,9 +18,9 @@ import com.google.common.collect.ImmutableList; import com.palantir.conjure.java.client.jaxrs.JaxRsJakartaCompatibility.Annotations; -import feign.Contract; -import feign.MethodMetadata; -import feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Contract; +import com.palantir.conjure.java.client.jaxrs.feign.MethodMetadata; +import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Arrays; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareDecoder.java index 105f1061b..38bbabd16 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareDecoder.java @@ -18,9 +18,9 @@ import static com.google.common.base.Preconditions.checkNotNull; -import feign.FeignException; -import feign.Response; -import feign.codec.Decoder; +import com.palantir.conjure.java.client.jaxrs.feign.FeignException; +import com.palantir.conjure.java.client.jaxrs.feign.Response; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import java.io.IOException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/MethodHeaderEnrichmentContract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/MethodHeaderEnrichmentContract.java index 2068934b5..b03794a09 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/MethodHeaderEnrichmentContract.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/MethodHeaderEnrichmentContract.java @@ -16,8 +16,8 @@ package com.palantir.conjure.java.client.jaxrs.feignimpl; -import feign.Contract; -import feign.MethodMetadata; +import com.palantir.conjure.java.client.jaxrs.feign.Contract; +import com.palantir.conjure.java.client.jaxrs.feign.MethodMetadata; import java.lang.reflect.Method; /** diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverReturnNullDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverReturnNullDecoder.java index 8876178ba..2d6dd7abc 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverReturnNullDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverReturnNullDecoder.java @@ -16,11 +16,11 @@ package com.palantir.conjure.java.client.jaxrs.feignimpl; +import com.palantir.conjure.java.client.jaxrs.feign.FeignException; +import com.palantir.conjure.java.client.jaxrs.feign.Response; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import com.palantir.logsafe.Preconditions; import com.palantir.logsafe.SafeArg; -import feign.FeignException; -import feign.Response; -import feign.codec.Decoder; import java.io.IOException; import java.lang.reflect.Type; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/PathTemplateHeaderEnrichmentContract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/PathTemplateHeaderEnrichmentContract.java index c6acafa94..963d57f8e 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/PathTemplateHeaderEnrichmentContract.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/PathTemplateHeaderEnrichmentContract.java @@ -16,8 +16,8 @@ package com.palantir.conjure.java.client.jaxrs.feignimpl; -import feign.Contract; -import feign.MethodMetadata; +import com.palantir.conjure.java.client.jaxrs.feign.Contract; +import com.palantir.conjure.java.client.jaxrs.feign.MethodMetadata; import java.lang.reflect.Method; public final class PathTemplateHeaderEnrichmentContract extends AbstractDelegatingContract { diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/PathTemplateHeaderRewriter.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/PathTemplateHeaderRewriter.java index 16b398537..ce4b5ed6d 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/PathTemplateHeaderRewriter.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/PathTemplateHeaderRewriter.java @@ -16,8 +16,8 @@ package com.palantir.conjure.java.client.jaxrs.feignimpl; -import feign.RequestInterceptor; -import feign.RequestTemplate; +import com.palantir.conjure.java.client.jaxrs.feign.RequestInterceptor; +import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; /** * No longer used. diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/QosErrorDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/QosErrorDecoder.java index 3f0cb3b7c..2552cfc81 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/QosErrorDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/QosErrorDecoder.java @@ -17,8 +17,8 @@ package com.palantir.conjure.java.client.jaxrs.feignimpl; import com.palantir.conjure.java.QosExceptionResponseMapper; -import feign.Response; -import feign.codec.ErrorDecoder; +import com.palantir.conjure.java.client.jaxrs.feign.Response; +import com.palantir.conjure.java.client.jaxrs.feign.codec.ErrorDecoder; import java.util.Collection; import java.util.Optional; import java.util.function.Function; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/SlashEncodingContract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/SlashEncodingContract.java index 94c0451c1..254c387f1 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/SlashEncodingContract.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/SlashEncodingContract.java @@ -16,8 +16,8 @@ package com.palantir.conjure.java.client.jaxrs.feignimpl; -import feign.Contract; -import feign.MethodMetadata; +import com.palantir.conjure.java.client.jaxrs.feign.Contract; +import com.palantir.conjure.java.client.jaxrs.feign.MethodMetadata; import java.lang.reflect.Method; /** Decorates a {@link Contract} and forces slashes to be encoded when they are part of a URL. */ diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateDecoder.java index c688a12b1..aefdc05dc 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateDecoder.java @@ -19,10 +19,10 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.net.HttpHeaders; -import feign.FeignException; -import feign.Response; -import feign.codec.Decoder; -import feign.codec.StringDecoder; +import com.palantir.conjure.java.client.jaxrs.feign.FeignException; +import com.palantir.conjure.java.client.jaxrs.feign.Response; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; +import com.palantir.conjure.java.client.jaxrs.feign.codec.StringDecoder; import java.io.IOException; import java.lang.reflect.Type; import java.util.Collection; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateEncoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateEncoder.java index 8a60f8709..80db73d80 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateEncoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateEncoder.java @@ -19,14 +19,14 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.net.HttpHeaders; -import feign.RequestTemplate; -import feign.codec.EncodeException; -import feign.codec.Encoder; +import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; +import com.palantir.conjure.java.client.jaxrs.feign.codec.EncodeException; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; import java.lang.reflect.Type; import java.util.Collection; /** - * Delegates to a {@link feign.codec.Encoder.Default} if the response has a Content-Type of text/plain, or falls back to + * Delegates to a {@link Encoder.Default} if the response has a Content-Type of text/plain, or falls back to * the given delegate otherwise. */ public final class TextDelegateEncoder implements Encoder { diff --git a/conjure-java-jaxrs-client/src/main/java/feign/Target.java b/conjure-java-jaxrs-client/src/main/java/feign/Target.java deleted file mode 100644 index 2c82067fb..000000000 --- a/conjure-java-jaxrs-client/src/main/java/feign/Target.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright 2013 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package feign; - -import static feign.Util.checkNotNull; -import static feign.Util.emptyToNull; - -/** - *

relationship to JAXRS 2.0

Similar to {@code - * javax.ws.rs.client.WebTarget}, as it produces requests. However, {@link RequestTemplate} is a - * closer match to {@code WebTarget}. - * - * @param type of the interface this target applies to. - */ -public interface Target { - - /* The type of the interface this target applies to. ex. {@code Route53}. */ - Class type(); - - /* configuration key associated with this target. For example, {@code route53}. */ - String name(); - - /* base HTTP URL of the target. For example, {@code https://api/v2}. */ - String url(); - - /** - * Targets a template to this target, adding the {@link #url() base url} and any target-specific - * headers or query parameters.

For example:
- *
-   * public Request apply(RequestTemplate input) {
-   *     input.insert(0, url());
-   *     input.replaceHeader("X-Auth", currentToken);
-   *     return input.asRequest();
-   * }
-   * 
- *


relationship to JAXRS 2.0

This call is similar to {@code - * javax.ws.rs.client.WebTarget.request()}, except that we expect transient, but necessary - * decoration to be applied on invocation. - */ - public Request apply(RequestTemplate input); - - public static class HardCodedTarget implements Target { - - private final Class type; - private final String name; - private final String url; - - public HardCodedTarget(Class type, String url) { - this(type, url, url); - } - - public HardCodedTarget(Class type, String name, String url) { - this.type = checkNotNull(type, "type"); - this.name = checkNotNull(emptyToNull(name), "name"); - this.url = checkNotNull(emptyToNull(url), "url"); - } - - @Override - public Class type() { - return type; - } - - @Override - public String name() { - return name; - } - - @Override - public String url() { - return url; - } - - /* no authentication or other special activity. just insert the url. */ - @Override - public Request apply(RequestTemplate input) { - if (input.url().indexOf("http") != 0) { - input.insert(0, url()); - } - return input.request(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof HardCodedTarget) { - HardCodedTarget other = (HardCodedTarget) obj; - return type.equals(other.type) - && name.equals(other.name) - && url.equals(other.url); - } - return false; - } - - @Override - public int hashCode() { - int result = 17; - result = 31 * result + type.hashCode(); - result = 31 * result + name.hashCode(); - result = 31 * result + url.hashCode(); - return result; - } - - @Override - public String toString() { - if (name.equals(url)) { - return "HardCodedTarget(type=" + type.getSimpleName() + ", url=" + url + ")"; - } - return "HardCodedTarget(type=" + type.getSimpleName() + ", name=" + name + ", url=" + url - + ")"; - } - } - - public static final class EmptyTarget implements Target { - - private final Class type; - private final String name; - - EmptyTarget(Class type, String name) { - this.type = checkNotNull(type, "type"); - this.name = checkNotNull(emptyToNull(name), "name"); - } - - public static EmptyTarget create(Class type) { - return new EmptyTarget(type, "empty:" + type.getSimpleName()); - } - - public static EmptyTarget create(Class type, String name) { - return new EmptyTarget(type, name); - } - - @Override - public Class type() { - return type; - } - - @Override - public String name() { - return name; - } - - @Override - public String url() { - throw new UnsupportedOperationException("Empty targets don't have URLs"); - } - - @Override - public Request apply(RequestTemplate input) { - if (input.url().indexOf("http") != 0) { - throw new UnsupportedOperationException( - "Request with non-absolute URL not supported with empty target"); - } - return input.request(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof EmptyTarget) { - EmptyTarget other = (EmptyTarget) obj; - return type.equals(other.type) - && name.equals(other.name); - } - return false; - } - - @Override - public int hashCode() { - int result = 17; - result = 31 * result + type.hashCode(); - result = 31 * result + name.hashCode(); - return result; - } - - @Override - public String toString() { - if (name.equals("empty:" + type.getSimpleName())) { - return "EmptyTarget(type=" + type.getSimpleName() + ")"; - } - return "EmptyTarget(type=" + type.getSimpleName() + ", name=" + name + ")"; - } - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/feign/auth/Base64.java b/conjure-java-jaxrs-client/src/main/java/feign/auth/Base64.java deleted file mode 100644 index c565bc7c8..000000000 --- a/conjure-java-jaxrs-client/src/main/java/feign/auth/Base64.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2013 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package feign.auth; - -import java.io.UnsupportedEncodingException; - -/** - * copied from okhttp - * - * @author Alexander Y. Kleymenov - */ -final class Base64 { - - public static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; - private static final byte[] MAP = new byte[]{ - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', - 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', - 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', - '5', '6', '7', '8', '9', '+', '/' - }; - - private Base64() { - } - - public static byte[] decode(byte[] in) { - return decode(in, in.length); - } - - public static byte[] decode(byte[] in, int len) { - // approximate output length - int length = len / 4 * 3; - // return an empty array on empty or short input without padding - if (length == 0) { - return EMPTY_BYTE_ARRAY; - } - // temporary array - byte[] out = new byte[length]; - // number of padding characters ('=') - int pad = 0; - byte chr; - // compute the number of the padding characters - // and adjust the length of the input - for (; ; len--) { - chr = in[len - 1]; - // skip the neutral characters - if ((chr == '\n') || (chr == '\r') || (chr == ' ') || (chr == '\t')) { - continue; - } - if (chr == '=') { - pad++; - } else { - break; - } - } - // index in the output array - int outIndex = 0; - // index in the input array - int inIndex = 0; - // holds the value of the input character - int bits = 0; - // holds the value of the input quantum - int quantum = 0; - for (int i = 0; i < len; i++) { - chr = in[i]; - // skip the neutral characters - if ((chr == '\n') || (chr == '\r') || (chr == ' ') || (chr == '\t')) { - continue; - } - if ((chr >= 'A') && (chr <= 'Z')) { - // char ASCII value - // A 65 0 - // Z 90 25 (ASCII - 65) - bits = chr - 65; - } else if ((chr >= 'a') && (chr <= 'z')) { - // char ASCII value - // a 97 26 - // z 122 51 (ASCII - 71) - bits = chr - 71; - } else if ((chr >= '0') && (chr <= '9')) { - // char ASCII value - // 0 48 52 - // 9 57 61 (ASCII + 4) - bits = chr + 4; - } else if (chr == '+') { - bits = 62; - } else if (chr == '/') { - bits = 63; - } else { - return null; - } - // append the value to the quantum - quantum = (quantum << 6) | (byte) bits; - if (inIndex % 4 == 3) { - // 4 characters were read, so make the output: - out[outIndex++] = (byte) (quantum >> 16); - out[outIndex++] = (byte) (quantum >> 8); - out[outIndex++] = (byte) quantum; - } - inIndex++; - } - if (pad > 0) { - // adjust the quantum value according to the padding - quantum = quantum << (6 * pad); - // make output - out[outIndex++] = (byte) (quantum >> 16); - if (pad == 1) { - out[outIndex++] = (byte) (quantum >> 8); - } - } - // create the resulting array - byte[] result = new byte[outIndex]; - System.arraycopy(out, 0, result, 0, outIndex); - return result; - } - - public static String encode(byte[] in) { - int length = (in.length + 2) * 4 / 3; - byte[] out = new byte[length]; - int index = 0, end = in.length - in.length % 3; - for (int i = 0; i < end; i += 3) { - out[index++] = MAP[(in[i] & 0xff) >> 2]; - out[index++] = MAP[((in[i] & 0x03) << 4) | ((in[i + 1] & 0xff) >> 4)]; - out[index++] = MAP[((in[i + 1] & 0x0f) << 2) | ((in[i + 2] & 0xff) >> 6)]; - out[index++] = MAP[(in[i + 2] & 0x3f)]; - } - switch (in.length % 3) { - case 1: - out[index++] = MAP[(in[end] & 0xff) >> 2]; - out[index++] = MAP[(in[end] & 0x03) << 4]; - out[index++] = '='; - out[index++] = '='; - break; - case 2: - out[index++] = MAP[(in[end] & 0xff) >> 2]; - out[index++] = MAP[((in[end] & 0x03) << 4) | ((in[end + 1] & 0xff) >> 4)]; - out[index++] = MAP[((in[end + 1] & 0x0f) << 2)]; - out[index++] = '='; - break; - } - try { - return new String(out, 0, index, "US-ASCII"); - } catch (UnsupportedEncodingException e) { - throw new AssertionError(e); - } - } -} - diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/EmptyContainerDecoderTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/EmptyContainerDecoderTest.java index a9a31bf8a..e1fa9b9d4 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/EmptyContainerDecoderTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/EmptyContainerDecoderTest.java @@ -30,9 +30,9 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.net.HttpHeaders; +import com.palantir.conjure.java.client.jaxrs.feign.Response; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import com.palantir.conjure.java.serialization.ObjectMappers; -import feign.Response; -import feign.codec.Decoder; import jakarta.ws.rs.core.MediaType; import java.io.IOException; import java.nio.charset.StandardCharsets; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateDecoderTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateDecoderTest.java index 317b41649..f7992f6a5 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateDecoderTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateDecoderTest.java @@ -23,10 +23,10 @@ import com.google.common.collect.ImmutableMap; import com.palantir.conjure.java.client.jaxrs.JaxRsClient; import com.palantir.conjure.java.client.jaxrs.TestBase; +import com.palantir.conjure.java.client.jaxrs.feign.Response; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import com.palantir.conjure.java.okhttp.HostMetricsRegistry; import com.palantir.undertest.UndertowServerExtension; -import feign.Response; -import feign.codec.Decoder; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateEncoderTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateEncoderTest.java index 00fdc1bbe..6078df73c 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateEncoderTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateEncoderTest.java @@ -21,10 +21,10 @@ import com.palantir.conjure.java.client.jaxrs.JaxRsClient; import com.palantir.conjure.java.client.jaxrs.TestBase; +import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; import com.palantir.conjure.java.okhttp.HostMetricsRegistry; import com.palantir.undertest.UndertowServerExtension; -import feign.RequestTemplate; -import feign.codec.Encoder; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverReturnNullDecoderTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverReturnNullDecoderTest.java index 68081a751..b8f76c5a2 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverReturnNullDecoderTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverReturnNullDecoderTest.java @@ -20,12 +20,12 @@ import com.google.common.collect.ImmutableList; import com.palantir.conjure.java.client.jaxrs.TestBase; +import com.palantir.conjure.java.client.jaxrs.feign.Response; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; +import com.palantir.conjure.java.client.jaxrs.feign.jackson.JacksonDecoder; import com.palantir.conjure.java.serialization.ObjectMappers; import com.palantir.logsafe.SafeArg; import com.palantir.logsafe.testing.Assertions; -import feign.Response; -import feign.codec.Decoder; -import feign.jackson.JacksonDecoder; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.HashMap; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/QosErrorDecoderTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/QosErrorDecoderTest.java index f84363f7c..234f49bd6 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/QosErrorDecoderTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/QosErrorDecoderTest.java @@ -28,8 +28,8 @@ import com.palantir.conjure.java.api.errors.QosReason.DueTo; import com.palantir.conjure.java.api.errors.QosReason.RetryHint; import com.palantir.conjure.java.api.errors.QosReasons; -import feign.Response; -import feign.codec.ErrorDecoder; +import com.palantir.conjure.java.client.jaxrs.feign.Response; +import com.palantir.conjure.java.client.jaxrs.feign.codec.ErrorDecoder; import jakarta.ws.rs.core.HttpHeaders; import java.time.Duration; import java.util.Collection; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateDecoderTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateDecoderTest.java index 2fe8b0b9a..a381506da 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateDecoderTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateDecoderTest.java @@ -28,12 +28,12 @@ import com.google.common.net.HttpHeaders; import com.palantir.conjure.java.client.jaxrs.JaxRsClient; import com.palantir.conjure.java.client.jaxrs.TestBase; +import com.palantir.conjure.java.client.jaxrs.feign.FeignException; +import com.palantir.conjure.java.client.jaxrs.feign.Response; +import com.palantir.conjure.java.client.jaxrs.feign.codec.DecodeException; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import com.palantir.conjure.java.okhttp.HostMetricsRegistry; import com.palantir.undertest.UndertowServerExtension; -import feign.FeignException; -import feign.Response; -import feign.codec.DecodeException; -import feign.codec.Decoder; import jakarta.ws.rs.core.MediaType; import java.nio.charset.StandardCharsets; import java.util.Collection; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateEncoderTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateEncoderTest.java index f99b5911a..87f78d800 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateEncoderTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateEncoderTest.java @@ -21,8 +21,8 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import com.google.common.net.HttpHeaders; -import feign.RequestTemplate; -import feign.codec.Encoder; +import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; +import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; import jakarta.ws.rs.core.MediaType; import java.util.Arrays; import java.util.Collection; From 19fb10e03311aa7105f3aa682e1684232b5c796c Mon Sep 17 00:00:00 2001 From: Patrick Koenig Date: Fri, 27 Mar 2026 00:42:17 -0400 Subject: [PATCH 3/6] Remove unused code, suppress warnings, and clean up --- .../AbstractFeignJaxRsClientBuilder.java | 52 +--- .../client/jaxrs/DialogueFeignClient.java | 5 +- .../jaxrs/JaxRsJakartaCompatibility.java | 1 - .../conjure/java/client/jaxrs/feign/Body.java | 42 ---- .../java/client/jaxrs/feign/Client.java | 144 +---------- .../java/client/jaxrs/feign/Contract.java | 160 +----------- .../jaxrs/feign/DefaultMethodHandler.java | 8 +- .../java/client/jaxrs/feign/Expander.java | 25 ++ .../java/client/jaxrs/feign/Feign.java | 120 +-------- .../client/jaxrs/feign/FeignException.java | 68 ------ .../java/client/jaxrs/feign/HeaderMap.java | 70 ------ .../java/client/jaxrs/feign/Headers.java | 69 ------ .../jaxrs/feign/InvocationHandlerFactory.java | 45 ---- .../java/client/jaxrs/feign/Logger.java | 217 ----------------- .../MethodHandler.java} | 22 +- .../client/jaxrs/feign/MethodMetadata.java | 7 +- .../java/client/jaxrs/feign/Param.java | 56 ----- .../java/client/jaxrs/feign/QueryMap.java | 65 ----- .../client/jaxrs/feign/ReflectiveFeign.java | 93 +++---- .../java/client/jaxrs/feign/Request.java | 64 +---- .../jaxrs/feign/RequestInterceptor.java | 44 ---- .../java/client/jaxrs/feign/RequestLine.java | 68 ------ .../client/jaxrs/feign/RequestTemplate.java | 130 +++------- .../java/client/jaxrs/feign/Response.java | 3 +- .../jaxrs/feign/RetryableException.java | 54 ----- .../java/client/jaxrs/feign/Retryer.java | 115 --------- .../jaxrs/feign/SynchronousMethodHandler.java | 227 ++++++------------ .../java/client/jaxrs/feign/Target.java | 147 +----------- .../java/client/jaxrs/feign/Types.java | 58 +++-- .../conjure/java/client/jaxrs/feign/Util.java | 62 +---- .../java/client/jaxrs/feign/auth/Base64.java | 158 ------------ .../auth/BasicAuthRequestInterceptor.java | 69 ------ .../jaxrs/feign/codec/DecodeException.java | 45 ---- .../client/jaxrs/feign/codec/Decoder.java | 23 +- .../jaxrs/feign/codec/EncodeException.java | 45 ---- .../client/jaxrs/feign/codec/Encoder.java | 22 +- .../jaxrs/feign/codec/ErrorDecoder.java | 97 +------- .../jaxrs/feign/codec/StringDecoder.java | 38 --- .../jaxrs/feign/jackson/JacksonEncoder.java | 58 ----- .../feignimpl/AbstractDelegatingContract.java | 6 +- .../jaxrs/feignimpl/CborDelegateDecoder.java | 3 +- .../jaxrs/feignimpl/CborDelegateEncoder.java | 6 +- .../feignimpl/GuavaEmptyOptionalExpander.java | 2 +- .../feignimpl/GuavaNullOptionalExpander.java | 2 +- .../feignimpl/GuavaOptionalAwareDecoder.java | 3 +- .../feignimpl/InputStreamDelegateDecoder.java | 3 +- .../feignimpl/InputStreamDelegateEncoder.java | 6 +- .../jackson => feignimpl}/JacksonDecoder.java | 46 ++-- .../JacksonEncoder.java} | 20 +- .../Java8EmptyOptionalDoubleExpander.java | 2 +- .../feignimpl/Java8EmptyOptionalExpander.java | 2 +- .../Java8EmptyOptionalIntExpander.java | 2 +- .../Java8EmptyOptionalLongExpander.java | 2 +- .../Java8NullOptionalDoubleExpander.java | 2 +- .../feignimpl/Java8NullOptionalExpander.java | 2 +- .../Java8NullOptionalIntExpander.java | 2 +- .../Java8NullOptionalLongExpander.java | 2 +- .../feignimpl/Java8OptionalAwareContract.java | 2 +- .../feignimpl/Java8OptionalAwareDecoder.java | 3 +- .../feignimpl/NeverReturnNullDecoder.java | 3 +- .../jaxrs/feignimpl/TextDelegateDecoder.java | 19 +- .../jaxrs/feignimpl/TextDelegateEncoder.java | 20 +- .../feignimpl/NeverReturnNullDecoderTest.java | 1 - .../jaxrs/feignimpl/QosErrorDecoderTest.java | 3 +- .../feignimpl/TextDelegateDecoderTest.java | 9 +- 65 files changed, 335 insertions(+), 2634 deletions(-) delete mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Body.java create mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Expander.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/FeignException.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/HeaderMap.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Headers.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/InvocationHandlerFactory.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Logger.java rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl/PathTemplateHeaderRewriter.java => feign/MethodHandler.java} (54%) delete mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Param.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/QueryMap.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestInterceptor.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestLine.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RetryableException.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Retryer.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/auth/Base64.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/auth/BasicAuthRequestInterceptor.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/DecodeException.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/EncodeException.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/StringDecoder.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/jackson/JacksonEncoder.java rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feign/jackson => feignimpl}/JacksonDecoder.java (50%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{ConjureFeignJacksonEncoder.java => feignimpl/JacksonEncoder.java} (64%) diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/AbstractFeignJaxRsClientBuilder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/AbstractFeignJaxRsClientBuilder.java index ffe129301..96c87d085 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/AbstractFeignJaxRsClientBuilder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/AbstractFeignJaxRsClientBuilder.java @@ -23,14 +23,11 @@ import com.palantir.conjure.java.client.config.ClientConfiguration; import com.palantir.conjure.java.client.jaxrs.feign.Contract; import com.palantir.conjure.java.client.jaxrs.feign.Feign; -import com.palantir.conjure.java.client.jaxrs.feign.Logger; import com.palantir.conjure.java.client.jaxrs.feign.Request; import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; -import com.palantir.conjure.java.client.jaxrs.feign.Retryer; import com.palantir.conjure.java.client.jaxrs.feign.Target; import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; -import com.palantir.conjure.java.client.jaxrs.feign.jackson.JacksonDecoder; import com.palantir.conjure.java.client.jaxrs.feignimpl.CborDelegateDecoder; import com.palantir.conjure.java.client.jaxrs.feignimpl.CborDelegateEncoder; import com.palantir.conjure.java.client.jaxrs.feignimpl.EmptyContainerDecoder; @@ -39,6 +36,8 @@ import com.palantir.conjure.java.client.jaxrs.feignimpl.GuavaOptionalAwareDecoder; import com.palantir.conjure.java.client.jaxrs.feignimpl.InputStreamDelegateDecoder; import com.palantir.conjure.java.client.jaxrs.feignimpl.InputStreamDelegateEncoder; +import com.palantir.conjure.java.client.jaxrs.feignimpl.JacksonDecoder; +import com.palantir.conjure.java.client.jaxrs.feignimpl.JacksonEncoder; import com.palantir.conjure.java.client.jaxrs.feignimpl.Java8OptionalAwareContract; import com.palantir.conjure.java.client.jaxrs.feignimpl.Java8OptionalAwareDecoder; import com.palantir.conjure.java.client.jaxrs.feignimpl.MethodHeaderEnrichmentContract; @@ -56,7 +55,6 @@ import com.palantir.logsafe.Safe; import com.palantir.logsafe.SafeArg; import com.palantir.logsafe.exceptions.SafeIllegalArgumentException; -import java.util.Objects; /** Not meant to be implemented outside of this library. */ abstract class AbstractFeignJaxRsClientBuilder { @@ -116,8 +114,6 @@ static T create( .decoder(createDecoder(clientNameForLogging, jsonMapper, cborMapper)) .errorDecoder(new DialogueFeignClient.RemoteExceptionDecoder(runtime)) .client(new DialogueFeignClient(serviceClass, channel, runtime, FeignDialogueTarget.BASE_URL)) - .logLevel(Logger.Level.NONE) // we use Dialogue for logging. (note that NONE is the default) - .retryer(new Retryer.Default(0, 0, 1)) // use dialogue retry mechanism only .target(new FeignDialogueTarget<>(serviceClass, channel)); } @@ -126,57 +122,25 @@ static T create( * target. However, there's a great deal of other configuration, and we handle failover/retries in Dialogue * which makes every client appear to use the same URL. */ - private static final class FeignDialogueTarget implements Target { + private record FeignDialogueTarget(Class serviceClass, Channel channel) implements Target { private static final String BASE_URL = "dialogue://feign"; - private final Class serviceClass; - private final Target delegate; - // For equality checks - private final Channel channel; - - FeignDialogueTarget(Class serviceClass, Channel channel) { - this.serviceClass = serviceClass; - this.channel = channel; - this.delegate = new HardCodedTarget<>(serviceClass, BASE_URL); - } - @Override public Class type() { return serviceClass; } - @Override - public String name() { - return delegate.name(); - } - @Override public String url() { - return delegate.url(); + return BASE_URL; } @Override public Request apply(RequestTemplate input) { - return delegate.apply(input); - } - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } - if (other == null || getClass() != other.getClass()) { - return false; + if (input.url().indexOf("http") != 0) { + input.insert(0, url()); } - FeignDialogueTarget that = (FeignDialogueTarget) other; - return serviceClass.equals(that.serviceClass) - && delegate.equals(that.delegate) - && channel.equals(that.channel); - } - - @Override - public int hashCode() { - return Objects.hash(serviceClass, delegate, channel); + return input.request(); } } @@ -205,7 +169,7 @@ private static Decoder createDecoder( private static Encoder createEncoder( @Safe String clientNameForLogging, ObjectMapper jsonMapper, ObjectMapper cborMapper) { - Encoder encoder = new ConjureFeignJacksonEncoder(jsonMapper); + Encoder encoder = new JacksonEncoder(jsonMapper); encoder = new CborDelegateEncoder(cborMapper, encoder); encoder = new TextDelegateEncoder(encoder); encoder = new InputStreamDelegateEncoder(clientNameForLogging, encoder); diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/DialogueFeignClient.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/DialogueFeignClient.java index c8ebeb995..2054d09b0 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/DialogueFeignClient.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/DialogueFeignClient.java @@ -95,7 +95,7 @@ final class DialogueFeignClient implements Client { } @Override - public com.palantir.conjure.java.client.jaxrs.feign.Response execute(Request request, Request.Options _options) throws IOException { + public com.palantir.conjure.java.client.jaxrs.feign.Response execute(Request request) throws IOException { com.palantir.dialogue.Request.Builder builder = com.palantir.dialogue.Request.builder(); builder.putPathParams(REQUEST_URL_PATH_PARAM, request.url()); @@ -211,7 +211,8 @@ public void close() { } } - private static final class DialogueResponseBody implements com.palantir.conjure.java.client.jaxrs.feign.Response.Body { + private static final class DialogueResponseBody + implements com.palantir.conjure.java.client.jaxrs.feign.Response.Body { private final Response response; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/JaxRsJakartaCompatibility.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/JaxRsJakartaCompatibility.java index e14df14a3..f645f907b 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/JaxRsJakartaCompatibility.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/JaxRsJakartaCompatibility.java @@ -120,7 +120,6 @@ public Annotation getAnnotation(AnnotatedElement element) { } @Nullable - @SuppressWarnings("unchecked") private static Class resolve(String fqcn) { try { return (Class) Class.forName(fqcn); diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Body.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Body.java deleted file mode 100644 index f1974bb85..000000000 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Body.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.palantir.conjure.java.client.jaxrs.feign; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import java.util.Map; - -/** - * A possibly templated body of a PUT or POST command. variables wrapped in curly braces are - * expanded before the request is submitted.
ex.
- *
- * @Body("<v01:getResourceRecordsOfZone><zoneName>{zoneName}</zoneName><rrType>0</rrType></v01:getResourceRecordsOfZone>")
- * List<Record> listByZone(@Param("zoneName") String zoneName);
- * 
- *
Note that if you'd like curly braces literally in the body, urlencode them first. - * - * @see RequestTemplate#expand(String, Map) - */ -@Target(METHOD) -@Retention(RUNTIME) -public @interface Body { - - String value(); -} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Client.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Client.java index c360d4437..72c890cb5 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Client.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Client.java @@ -15,27 +15,7 @@ */ package com.palantir.conjure.java.client.jaxrs.feign; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.CONTENT_ENCODING; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.CONTENT_LENGTH; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.ENCODING_DEFLATE; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.ENCODING_GZIP; -import static java.lang.String.format; - -import com.palantir.conjure.java.client.jaxrs.feign.Request.Options; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.zip.DeflaterOutputStream; -import java.util.zip.GZIPOutputStream; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLSocketFactory; /** * Submits HTTP {@link Request requests}. Implementations are expected to be thread-safe. @@ -46,130 +26,8 @@ public interface Client { * Executes a request against its {@link Request#url() url} and returns a response. * * @param request safe to replay. - * @param options options to apply to this request. * @return connected response, {@link Response.Body} is absent or unread. * @throws IOException on a network error connecting to {@link Request#url()}. */ - Response execute(Request request, Options options) throws IOException; - - public static class Default implements Client { - - private final SSLSocketFactory sslContextFactory; - private final HostnameVerifier hostnameVerifier; - - /** - * Null parameters imply platform defaults. - */ - public Default(SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier) { - this.sslContextFactory = sslContextFactory; - this.hostnameVerifier = hostnameVerifier; - } - - @Override - public Response execute(Request request, Options options) throws IOException { - HttpURLConnection connection = convertAndSend(request, options); - return convertResponse(connection); - } - - HttpURLConnection convertAndSend(Request request, Options options) throws IOException { - final HttpURLConnection connection = (HttpURLConnection) new URL(request.url()).openConnection(); - if (connection instanceof HttpsURLConnection) { - HttpsURLConnection sslCon = (HttpsURLConnection) connection; - if (sslContextFactory != null) { - sslCon.setSSLSocketFactory(sslContextFactory); - } - if (hostnameVerifier != null) { - sslCon.setHostnameVerifier(hostnameVerifier); - } - } - connection.setConnectTimeout(options.connectTimeoutMillis()); - connection.setReadTimeout(options.readTimeoutMillis()); - connection.setAllowUserInteraction(false); - connection.setInstanceFollowRedirects(true); - connection.setRequestMethod(request.method()); - - Collection contentEncodingValues = request.headers().get(CONTENT_ENCODING); - boolean gzipEncodedRequest = contentEncodingValues != null && contentEncodingValues.contains(ENCODING_GZIP); - boolean deflateEncodedRequest = - contentEncodingValues != null && contentEncodingValues.contains(ENCODING_DEFLATE); - - boolean hasAcceptHeader = false; - Integer contentLength = null; - for (String field : request.headers().keySet()) { - if (field.equalsIgnoreCase("Accept")) { - hasAcceptHeader = true; - } - for (String value : request.headers().get(field)) { - if (field.equals(CONTENT_LENGTH)) { - if (!gzipEncodedRequest && !deflateEncodedRequest) { - contentLength = Integer.valueOf(value); - connection.addRequestProperty(field, value); - } - } else { - connection.addRequestProperty(field, value); - } - } - } - // Some servers choke on the default accept string. - if (!hasAcceptHeader) { - connection.addRequestProperty("Accept", "*/*"); - } - - if (request.body() != null) { - if (contentLength != null) { - connection.setFixedLengthStreamingMode(contentLength); - } else { - connection.setChunkedStreamingMode(8196); - } - connection.setDoOutput(true); - OutputStream out = connection.getOutputStream(); - if (gzipEncodedRequest) { - out = new GZIPOutputStream(out); - } else if (deflateEncodedRequest) { - out = new DeflaterOutputStream(out); - } - try { - out.write(request.body()); - } finally { - try { - out.close(); - } catch (IOException suppressed) { // NOPMD - } - } - } - return connection; - } - - Response convertResponse(HttpURLConnection connection) throws IOException { - int status = connection.getResponseCode(); - String reason = connection.getResponseMessage(); - - if (status < 0) { - throw new IOException(format( - "Invalid status(%s) executing %s %s", - status, connection.getRequestMethod(), connection.getURL())); - } - - Map> headers = new LinkedHashMap>(); - for (Map.Entry> field : - connection.getHeaderFields().entrySet()) { - // response message - if (field.getKey() != null) { - headers.put(field.getKey(), field.getValue()); - } - } - - Integer length = connection.getContentLength(); - if (length == -1) { - length = null; - } - InputStream stream; - if (status >= 400) { - stream = connection.getErrorStream(); - } else { - stream = connection.getInputStream(); - } - return Response.create(status, reason, headers, stream, length); - } - } + Response execute(Request request) throws IOException; } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Contract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Contract.java index bb863aca7..bf89e059a 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Contract.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Contract.java @@ -16,7 +16,6 @@ package com.palantir.conjure.java.client.jaxrs.feign; import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkState; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.emptyToNull; import java.lang.annotation.Annotation; import java.lang.reflect.Method; @@ -31,6 +30,7 @@ /** * Defines what annotations and values are valid on interfaces. */ +@SuppressWarnings("MissingSummary") public interface Contract { /** @@ -38,13 +38,12 @@ public interface Contract { * * @param targetType {@link Target#type() type} of the Feign interface. */ - // TODO: break this and correct spelling at some point - List parseAndValidatateMetadata(Class targetType); + List parseAndValidateMetadata(Class targetType); abstract class BaseContract implements Contract { @Override - public List parseAndValidatateMetadata(Class targetType) { + public List parseAndValidateMetadata(Class targetType) { checkState( targetType.getTypeParameters().length == 0, "Parameterized types unsupported: %s", @@ -75,15 +74,7 @@ public List parseAndValidatateMetadata(Class targetType) { } /** - * @deprecated use {@link #parseAndValidateMetadata(Class, Method)} instead. - */ - @Deprecated - public MethodMetadata parseAndValidatateMetadata(Method method) { - return parseAndValidateMetadata(method.getDeclaringClass(), method); - } - - /** - * Called indirectly by {@link #parseAndValidatateMetadata(Class)}. + * Called indirectly by {@link #parseAndValidateMetadata(Class)}. */ protected MethodMetadata parseAndValidateMetadata(Class targetType, Method method) { MethodMetadata data = new MethodMetadata(); @@ -145,7 +136,7 @@ protected MethodMetadata parseAndValidateMetadata(Class targetType, Method me * @param data metadata collected so far relating to the current java method. * @param clz the class to process */ - protected void processAnnotationOnClass(MethodMetadata data, Class clz) {} + protected abstract void processAnnotationOnClass(MethodMetadata data, Class clz); /** * @param data metadata collected so far relating to the current java method. @@ -183,145 +174,4 @@ protected void nameParam(MethodMetadata data, String name, int i) { data.indexToName().put(i, names); } } - - class Default extends BaseContract { - @Override - protected void processAnnotationOnClass(MethodMetadata data, Class targetType) { - if (targetType.isAnnotationPresent(Headers.class)) { - String[] headersOnType = targetType.getAnnotation(Headers.class).value(); - checkState(headersOnType.length > 0, "Headers annotation was empty on type %s.", targetType.getName()); - Map> headers = toMap(headersOnType); - headers.putAll(data.template().headers()); - data.template().headers(null); // to clear - data.template().headers(headers); - } - } - - @Override - protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, Method method) { - Class annotationType = methodAnnotation.annotationType(); - if (annotationType == RequestLine.class) { - String requestLine = RequestLine.class.cast(methodAnnotation).value(); - checkState( - emptyToNull(requestLine) != null, - "RequestLine annotation was empty on method %s.", - method.getName()); - if (requestLine.indexOf(' ') == -1) { - checkState( - requestLine.indexOf('/') == -1, - "RequestLine annotation didn't start with an HTTP verb on method %s.", - method.getName()); - data.template().method(requestLine); - return; - } - data.template().method(requestLine.substring(0, requestLine.indexOf(' '))); - if (requestLine.indexOf(' ') == requestLine.lastIndexOf(' ')) { - // no HTTP version is ok - data.template().append(requestLine.substring(requestLine.indexOf(' ') + 1)); - } else { - // skip HTTP version - data.template() - .append(requestLine.substring(requestLine.indexOf(' ') + 1, requestLine.lastIndexOf(' '))); - } - - data.template() - .decodeSlash(RequestLine.class.cast(methodAnnotation).decodeSlash()); - - } else if (annotationType == Body.class) { - String body = Body.class.cast(methodAnnotation).value(); - checkState(emptyToNull(body) != null, "Body annotation was empty on method %s.", method.getName()); - if (body.indexOf('{') == -1) { - data.template().body(body); - } else { - data.template().bodyTemplate(body); - } - } else if (annotationType == Headers.class) { - String[] headersOnMethod = Headers.class.cast(methodAnnotation).value(); - checkState(headersOnMethod.length > 0, "Headers annotation was empty on method %s.", method.getName()); - data.template().headers(toMap(headersOnMethod)); - } - } - - @Override - protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) { - boolean isHttpAnnotation = false; - for (Annotation annotation : annotations) { - Class annotationType = annotation.annotationType(); - if (annotationType == Param.class) { - String name = ((Param) annotation).value(); - checkState(emptyToNull(name) != null, "Param annotation was empty on param %s.", paramIndex); - nameParam(data, name, paramIndex); - if (annotationType == Param.class) { - Class expander = ((Param) annotation).expander(); - if (expander != Param.ToStringExpander.class) { - data.indexToExpanderClass().put(paramIndex, expander); - } - } - isHttpAnnotation = true; - String varName = '{' + name + '}'; - if (data.template().url().indexOf(varName) == -1 - && !searchMapValuesContainsExact(data.template().queries(), varName) - && !searchMapValuesContainsSubstring(data.template().headers(), varName)) { - data.formParams().add(name); - } - } else if (annotationType == QueryMap.class) { - checkState(data.queryMapIndex() == null, "QueryMap annotation was present on multiple parameters."); - data.queryMapIndex(paramIndex); - data.queryMapEncoded(QueryMap.class.cast(annotation).encoded()); - isHttpAnnotation = true; - } else if (annotationType == HeaderMap.class) { - checkState( - data.headerMapIndex() == null, "HeaderMap annotation was present on multiple parameters."); - data.headerMapIndex(paramIndex); - isHttpAnnotation = true; - } - } - return isHttpAnnotation; - } - - private static boolean searchMapValuesContainsExact(Map> map, V search) { - Collection> values = map.values(); - if (values == null) { - return false; - } - - for (Collection entry : values) { - if (entry.contains(search)) { - return true; - } - } - - return false; - } - - private static boolean searchMapValuesContainsSubstring(Map> map, String search) { - Collection> values = map.values(); - if (values == null) { - return false; - } - - for (Collection entry : values) { - for (String value : entry) { - if (value.indexOf(search) != -1) { - return true; - } - } - } - - return false; - } - - private static Map> toMap(String[] input) { - Map> result = new LinkedHashMap>(input.length); - for (String header : input) { - int colon = header.indexOf(':'); - String name = header.substring(0, colon); - if (!result.containsKey(name)) { - result.put(name, new ArrayList(1)); - } - result.get(name).add(header.substring(colon + 2)); - } - return result; - } - } } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/DefaultMethodHandler.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/DefaultMethodHandler.java index d3c1aaafc..2975a5bbd 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/DefaultMethodHandler.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/DefaultMethodHandler.java @@ -16,18 +16,16 @@ package com.palantir.conjure.java.client.jaxrs.feign; -import com.palantir.conjure.java.client.jaxrs.feign.InvocationHandlerFactory.MethodHandler; +import com.palantir.logsafe.exceptions.SafeIllegalStateException; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles.Lookup; import java.lang.reflect.Field; import java.lang.reflect.Method; -import org.jvnet.animal_sniffer.IgnoreJRERequirement; /** * Handles default methods by directly invoking the default method code on the interface. * The bindTo method must be called on the result before invoke is called. */ -@IgnoreJRERequirement final class DefaultMethodHandler implements MethodHandler { // Uses Java 7 MethodHandle based reflection. As default methods will only exist when // run on a Java 8 JVM this will not affect use on legacy JVMs. @@ -58,7 +56,7 @@ public DefaultMethodHandler(Method defaultMethod) { */ public void bindTo(Object proxy) { if (handle != null) { - throw new IllegalStateException("Attempted to rebind a default method handler that was already bound"); + throw new SafeIllegalStateException("Attempted to rebind a default method handler that was already bound"); } handle = unboundHandle.bindTo(proxy); } @@ -70,7 +68,7 @@ public void bindTo(Object proxy) { @Override public Object invoke(Object[] argv) throws Throwable { if (handle == null) { - throw new IllegalStateException("Default method handler invoked before proxy has been bound."); + throw new SafeIllegalStateException("Default method handler invoked before proxy has been bound."); } return handle.invokeWithArguments(argv); } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Expander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Expander.java new file mode 100644 index 000000000..18608ce67 --- /dev/null +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Expander.java @@ -0,0 +1,25 @@ +/* + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.palantir.conjure.java.client.jaxrs.feign; + +public interface Expander { + + /** + * Expands the value into a string. Does not accept or return null. + */ + String expand(Object value); +} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Feign.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Feign.java index c3396c05b..14f3fd932 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Feign.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Feign.java @@ -15,17 +15,12 @@ */ package com.palantir.conjure.java.client.jaxrs.feign; -import com.palantir.conjure.java.client.jaxrs.feign.Logger.NoOpLogger; import com.palantir.conjure.java.client.jaxrs.feign.ReflectiveFeign.ParseHandlersByName; -import com.palantir.conjure.java.client.jaxrs.feign.Request.Options; -import com.palantir.conjure.java.client.jaxrs.feign.Target.HardCodedTarget; import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; import com.palantir.conjure.java.client.jaxrs.feign.codec.ErrorDecoder; import java.lang.reflect.Method; import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.List; /** * Feign's purpose is to ease development against http apis that feign restfulness.
In @@ -51,7 +46,7 @@ public static Builder builder() { * @param targetType {@link Target#type() type} of the Feign interface. * @param method invoked method, present on {@code type} or its super. */ - public static String configKey(Class targetType, Method method) { + public static String configKey(Class targetType, Method method) { StringBuilder builder = new StringBuilder(); builder.append(targetType.getSimpleName()); builder.append('#').append(method.getName()).append('('); @@ -65,39 +60,21 @@ public static String configKey(Class targetType, Method method) { return builder.append(')').toString(); } - /** - * @deprecated use {@link #configKey(Class, Method)} instead. - */ - @Deprecated - public static String configKey(Method method) { - return configKey(method.getDeclaringClass(), method); - } - /** * Returns a new instance of an HTTP API, defined by annotations in the {@link Feign Contract}, * for the specified {@code target}. You should cache this result. */ public abstract T newInstance(Target target); - public static class Builder { + public static final class Builder { - private final List requestInterceptors = new ArrayList(); - private Logger.Level logLevel = Logger.Level.NONE; - private Contract contract = new Contract.Default(); - private Client client = new Client.Default(null, null); - private Retryer retryer = new Retryer.Default(); - private Logger logger = new NoOpLogger(); - private Encoder encoder = new Encoder.Default(); - private Decoder decoder = new Decoder.Default(); - private ErrorDecoder errorDecoder = new ErrorDecoder.Default(); - private Options options = new Options(); - private InvocationHandlerFactory invocationHandlerFactory = new InvocationHandlerFactory.Default(); - private boolean decode404; + private Contract contract; + private Client client; + private Encoder encoder; + private Decoder decoder; + private ErrorDecoder errorDecoder; - public Builder logLevel(Logger.Level logLevel) { - this.logLevel = logLevel; - return this; - } + private Builder() {} public Builder contract(Contract contract) { this.contract = contract; @@ -109,16 +86,6 @@ public Builder client(Client client) { return this; } - public Builder retryer(Retryer retryer) { - this.retryer = retryer; - return this; - } - - public Builder logger(Logger logger) { - this.logger = logger; - return this; - } - public Builder encoder(Encoder encoder) { this.encoder = encoder; return this; @@ -129,78 +96,17 @@ public Builder decoder(Decoder decoder) { return this; } - /** - * This flag indicates that the {@link #decoder(Decoder) decoder} should process responses with - * 404 status, specifically returning null or empty instead of throwing {@link FeignException}. - * - *

All first-party (ex gson) decoders return well-known empty values defined by - * {@link Util#emptyValueOf}. To customize further, wrap an existing - * {@link #decoder(Decoder) decoder} or make your own. - * - *

This flag only works with 404, as opposed to all or arbitrary status codes. This was an - * explicit decision: 404 -> empty is safe, common and doesn't complicate redirection, retry or - * fallback policy. If your server returns a different status for not-found, correct via a - * custom {@link #client(Client) client}. - * - * @since 8.12 - */ - public Builder decode404() { - this.decode404 = true; - return this; - } - public Builder errorDecoder(ErrorDecoder errorDecoder) { this.errorDecoder = errorDecoder; return this; } - public Builder options(Options options) { - this.options = options; - return this; - } - - /** - * Adds a single request interceptor to the builder. - */ - public Builder requestInterceptor(RequestInterceptor requestInterceptor) { - this.requestInterceptors.add(requestInterceptor); - return this; - } - - /** - * Sets the full set of request interceptors for the builder, overwriting any previous - * interceptors. - */ - public Builder requestInterceptors(Iterable requestInterceptors) { - this.requestInterceptors.clear(); - for (RequestInterceptor requestInterceptor : requestInterceptors) { - this.requestInterceptors.add(requestInterceptor); - } - return this; - } - - /** - * Allows you to override how reflective dispatch works inside of Feign. - */ - public Builder invocationHandlerFactory(InvocationHandlerFactory invocationHandlerFactory) { - this.invocationHandlerFactory = invocationHandlerFactory; - return this; - } - - public T target(Class apiType, String url) { - return target(new HardCodedTarget(apiType, url)); - } - public T target(Target target) { - return build().newInstance(target); - } - - public Feign build() { - SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory( - client, retryer, requestInterceptors, logger, logLevel, decode404); - ParseHandlersByName handlersByName = new ParseHandlersByName( - contract, options, encoder, decoder, errorDecoder, synchronousMethodHandlerFactory); - return new ReflectiveFeign(handlersByName, invocationHandlerFactory); + SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = + new SynchronousMethodHandler.Factory(client); + ParseHandlersByName handlersByName = + new ParseHandlersByName(contract, encoder, decoder, errorDecoder, synchronousMethodHandlerFactory); + return new ReflectiveFeign(handlersByName).newInstance(target); } } } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/FeignException.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/FeignException.java deleted file mode 100644 index 2eb6c93bf..000000000 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/FeignException.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.palantir.conjure.java.client.jaxrs.feign; - -import static java.lang.String.format; - -import java.io.IOException; - -/** - * Origin exception type for all Http Apis. - */ -public class FeignException extends RuntimeException { - - private static final long serialVersionUID = 0; - private int status; - - protected FeignException(String message, Throwable cause) { - super(message, cause); - } - - protected FeignException(String message) { - super(message); - } - - protected FeignException(int status, String message) { - super(message); - this.status = status; - } - - public int status() { - return this.status; - } - - static FeignException errorReading(Request request, Response ignored, IOException cause) { - return new FeignException( - format("%s reading %s %s", cause.getMessage(), request.method(), request.url()), cause); - } - - public static FeignException errorStatus(String methodKey, Response response) { - String message = format("status %s reading %s", response.status(), methodKey); - try { - if (response.body() != null) { - String body = Util.toString(response.body().asReader()); - message += "; content:\n" + body; - } - } catch (IOException ignored) { // NOPMD - } - return new FeignException(response.status(), message); - } - - static FeignException errorExecuting(Request request, IOException cause) { - return new RetryableException( - format("%s executing %s %s", cause.getMessage(), request.method(), request.url()), cause, null); - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/HeaderMap.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/HeaderMap.java deleted file mode 100644 index 2100cd766..000000000 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/HeaderMap.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.palantir.conjure.java.client.jaxrs.feign; - -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Retention; -import java.util.List; -import java.util.Map; - -/** - * A template parameter that can be applied to a Map that contains header - * entries, where the keys are Strings that are the header field names and the - * values are the header field values. The headers specified by the map will be - * applied to the request after all other processing, and will take precedence - * over any previously specified header parameters. - *
- * This parameter is useful in cases where different header fields and values - * need to be set on an API method on a per-request basis in a thread-safe manner - * and independently of Feign client construction. A concrete example of a case - * like this are custom metadata header fields (e.g. as "x-amz-meta-*" or - * "x-goog-meta-*") where the header field names are dynamic and the range of keys - * cannot be determined a priori. The {@link Headers} annotation does not allow this - * because the header fields that it defines are static (it is not possible to add or - * remove fields on a per-request basis), and doing this using a custom {@link Target} - * or {@link RequestInterceptor} can be cumbersome (it requires more code for per-method - * customization, it is difficult to implement in a thread-safe manner and it requires - * customization when the Feign client for the API is built). - *
- *

- * ...
- * @RequestLine("GET /servers/{serverId}")
- * void get(@Param("serverId") String serverId, @HeaderMap Map);
- * ...
- * 
- * The annotated parameter must be an instance of {@link Map}, and the keys must - * be Strings. The header field value of a key will be the value of its toString - * method, except in the following cases: - *
- *
- *
    - *
  • if the value is null, the value will remain null (rather than converting - * to the String "null") - *
  • if the value is an {@link Iterable}, it is converted to a {@link List} of - * String objects where each value in the list is either null if the original - * value was null or the value's toString representation otherwise. - *
- *
- * Once this conversion is applied, the query keys and resulting String values - * follow the same contract as if they were set using - * {@link RequestTemplate#header(String, String...)}. - */ -@Retention(RUNTIME) -@java.lang.annotation.Target(PARAMETER) -public @interface HeaderMap {} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Headers.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Headers.java deleted file mode 100644 index ef78fcb30..000000000 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Headers.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.palantir.conjure.java.client.jaxrs.feign; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * Expands headers supplied in the {@code value}. Variables to the the right of the colon are expanded.
- *
- * @Headers("Content-Type: application/xml")
- * interface SoapApi {
- * ...
- * @RequestLine("GET /")
- * @Headers("Cache-Control: max-age=640000")
- * ...
- *
- * @RequestLine("POST /")
- * @Headers({
- *   "X-Foo: Bar",
- *   "X-Ping: {token}"
- * }) void post(@Param("token") String token);
- * ...
- * 
- *
Notes: - *
    - *
  • If you'd like curly braces literally in the header, urlencode them first.
  • - *
  • Headers do not overwrite each other. All headers with the same name will be included - * in the request.
  • - *
- *
Relationship to JAXRS

The following two forms are identical.

Feign: - *
- * @RequestLine("POST /")
- * @Headers({
- *   "X-Ping: {token}"
- * }) void post(@Named("token") String token);
- * ...
- * 
- *
JAX-RS: - *
- * @POST @Path("/")
- * void post(@HeaderParam("X-Ping") String token);
- * ...
- * 
- */ -@Target({METHOD, TYPE}) -@Retention(RUNTIME) -public @interface Headers { - - String[] value(); -} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/InvocationHandlerFactory.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/InvocationHandlerFactory.java deleted file mode 100644 index 13b2d704c..000000000 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/InvocationHandlerFactory.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.palantir.conjure.java.client.jaxrs.feign; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.util.Map; - -/** - * Controls reflective method dispatch. - */ -public interface InvocationHandlerFactory { - - InvocationHandler create(Target target, Map dispatch); - - /** - * Like {@link InvocationHandler#invoke(Object, Method, Object[])}, except for a - * single method. - */ - interface MethodHandler { - - Object invoke(Object[] argv) throws Throwable; - } - - static final class Default implements InvocationHandlerFactory { - - @Override - public InvocationHandler create(Target target, Map dispatch) { - return new ReflectiveFeign.FeignInvocationHandler(target, dispatch); - } - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Logger.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Logger.java deleted file mode 100644 index 35468f5d2..000000000 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Logger.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.palantir.conjure.java.client.jaxrs.feign; - -import static com.palantir.conjure.java.client.jaxrs.feign.Util.UTF_8; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.decodeOrDefault; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.valuesOrEmpty; - -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.logging.FileHandler; -import java.util.logging.LogRecord; -import java.util.logging.SimpleFormatter; - -/** - * Simple logging abstraction for debug messages. Adapted from {@code retrofit.RestAdapter.Log}. - */ -public abstract class Logger { - - protected static String methodTag(String configKey) { - return new StringBuilder() - .append('[') - .append(configKey.substring(0, configKey.indexOf('('))) - .append("] ") - .toString(); - } - - /** - * Override to log requests and responses using your own implementation. Messages will be http - * request and response text. - * - * @param configKey value of {@link Feign#configKey(Class, java.lang.reflect.Method)} - * @param format {@link java.util.Formatter format string} - * @param args arguments applied to {@code format} - */ - protected abstract void log(String configKey, String format, Object... args); - - protected void logRequest(String configKey, Level logLevel, Request request) { - log(configKey, "---> %s %s HTTP/1.1", request.method(), request.url()); - if (logLevel.ordinal() >= Level.HEADERS.ordinal()) { - - for (String field : request.headers().keySet()) { - for (String value : valuesOrEmpty(request.headers(), field)) { - log(configKey, "%s: %s", field, value); - } - } - - int bodyLength = 0; - if (request.body() != null) { - bodyLength = request.body().length; - if (logLevel.ordinal() >= Level.FULL.ordinal()) { - String bodyText = request.charset() != null ? new String(request.body(), request.charset()) : null; - log(configKey, ""); // CRLF - log(configKey, "%s", bodyText != null ? bodyText : "Binary data"); - } - } - log(configKey, "---> END HTTP (%s-byte body)", bodyLength); - } - } - - void logRetry(String configKey, Level logLevel) { - log(configKey, "---> RETRYING"); - } - - protected Response logAndRebufferResponse(String configKey, Level logLevel, Response response, long elapsedTime) - throws IOException { - String reason = response.reason() != null && logLevel.compareTo(Level.NONE) > 0 ? " " + response.reason() : ""; - log(configKey, "<--- HTTP/1.1 %s%s (%sms)", response.status(), reason, elapsedTime); - if (logLevel.ordinal() >= Level.HEADERS.ordinal()) { - - for (String field : response.headers().keySet()) { - for (String value : valuesOrEmpty(response.headers(), field)) { - log(configKey, "%s: %s", field, value); - } - } - - int bodyLength = 0; - if (response.body() != null) { - if (logLevel.ordinal() >= Level.FULL.ordinal()) { - log(configKey, ""); // CRLF - } - byte[] bodyData = Util.toByteArray(response.body().asInputStream()); - bodyLength = bodyData.length; - if (logLevel.ordinal() >= Level.FULL.ordinal() && bodyLength > 0) { - log(configKey, "%s", decodeOrDefault(bodyData, UTF_8, "Binary data")); - } - log(configKey, "<--- END HTTP (%s-byte body)", bodyLength); - return Response.create(response.status(), response.reason(), response.headers(), bodyData); - } else { - log(configKey, "<--- END HTTP (%s-byte body)", bodyLength); - } - } - return response; - } - - IOException logIOException(String configKey, Level logLevel, IOException ioe, long elapsedTime) { - log(configKey, "<--- ERROR %s: %s (%sms)", ioe.getClass().getSimpleName(), ioe.getMessage(), elapsedTime); - if (logLevel.ordinal() >= Level.FULL.ordinal()) { - StringWriter sw = new StringWriter(); - ioe.printStackTrace(new PrintWriter(sw)); - log(configKey, sw.toString()); - log(configKey, "<--- END ERROR"); - } - return ioe; - } - - /** - * Controls the level of logging. - */ - public enum Level { - /** - * No logging. - */ - NONE, - /** - * Log only the request method and URL and the response status code and execution time. - */ - BASIC, - /** - * Log the basic information along with request and response headers. - */ - HEADERS, - /** - * Log the headers, body, and metadata for both requests and responses. - */ - FULL - } - - /** - * logs to the category {@link Logger} at {@link java.util.logging.Level#FINE}. - */ - public static class ErrorLogger extends Logger { - @Override - protected void log(String configKey, String format, Object... args) { - System.err.printf(methodTag(configKey) + format + "%n", args); - } - } - - /** - * logs to the category {@link Logger} at {@link java.util.logging.Level#FINE}, if loggable. - */ - public static class JavaLogger extends Logger { - - final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(Logger.class.getName()); - - @Override - protected void logRequest(String configKey, Level logLevel, Request request) { - if (logger.isLoggable(java.util.logging.Level.FINE)) { - super.logRequest(configKey, logLevel, request); - } - } - - @Override - protected Response logAndRebufferResponse(String configKey, Level logLevel, Response response, long elapsedTime) - throws IOException { - if (logger.isLoggable(java.util.logging.Level.FINE)) { - return super.logAndRebufferResponse(configKey, logLevel, response, elapsedTime); - } - return response; - } - - @Override - protected void log(String configKey, String format, Object... args) { - logger.fine(String.format(methodTag(configKey) + format, args)); - } - - /** - * helper that configures jul to sanely log messages at FINE level without additional - * formatting. - */ - public JavaLogger appendToFile(String logfile) { - logger.setLevel(java.util.logging.Level.FINE); - try { - FileHandler handler = new FileHandler(logfile, true); - handler.setFormatter(new SimpleFormatter() { - @Override - public String format(LogRecord record) { - return String.format("%s%n", record.getMessage()); // NOPMD - } - }); - logger.addHandler(handler); - } catch (IOException e) { - throw new IllegalStateException("Could not add file handler.", e); - } - return this; - } - } - - public static class NoOpLogger extends Logger { - - @Override - protected void logRequest(String configKey, Level logLevel, Request request) {} - - @Override - protected Response logAndRebufferResponse(String configKey, Level logLevel, Response response, long elapsedTime) - throws IOException { - return response; - } - - @Override - protected void log(String configKey, String format, Object... args) {} - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/PathTemplateHeaderRewriter.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/MethodHandler.java similarity index 54% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/PathTemplateHeaderRewriter.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/MethodHandler.java index ce4b5ed6d..cb639749a 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/PathTemplateHeaderRewriter.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/MethodHandler.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,22 +14,16 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs.feign; -import com.palantir.conjure.java.client.jaxrs.feign.RequestInterceptor; -import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; /** - * No longer used. - * - * @deprecated no longer used + * Like {@link InvocationHandler#invoke(Object, Method, Object[])}, except for a + * single method. */ -@Deprecated -public enum PathTemplateHeaderRewriter implements RequestInterceptor { - INSTANCE; +public interface MethodHandler { - @Override - public void apply(RequestTemplate _template) { - // nop - } + Object invoke(Object[] argv) throws Throwable; } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/MethodMetadata.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/MethodMetadata.java index c833cf252..c41937f19 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/MethodMetadata.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/MethodMetadata.java @@ -15,8 +15,6 @@ */ package com.palantir.conjure.java.client.jaxrs.feign; -import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; -import java.io.Serializable; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; @@ -24,9 +22,9 @@ import java.util.List; import java.util.Map; -public final class MethodMetadata implements Serializable { +@SuppressWarnings("MissingSummary") +public final class MethodMetadata { - private static final long serialVersionUID = 1L; private String configKey; private transient Type returnType; private Integer urlIndex; @@ -47,6 +45,7 @@ public final class MethodMetadata implements Serializable { /** * @see Feign#configKey(Class, java.lang.reflect.Method) */ + @SuppressWarnings("MissingSummary") public String configKey() { return configKey; } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Param.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Param.java deleted file mode 100644 index ba504a1db..000000000 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Param.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.palantir.conjure.java.client.jaxrs.feign; - -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Retention; - -/** - * A named template parameter applied to {@link Headers}, {@linkplain RequestLine} or {@linkplain - * Body} - */ -@Retention(RUNTIME) -@java.lang.annotation.Target(PARAMETER) -public @interface Param { - - /** - * The name of the template parameter. - */ - String value(); - - /** - * How to expand the value of this parameter, if {@link ToStringExpander} isn't adequate. - */ - Class expander() default ToStringExpander.class; - - interface Expander { - - /** - * Expands the value into a string. Does not accept or return null. - */ - String expand(Object value); - } - - final class ToStringExpander implements Expander { - - @Override - public String expand(Object value) { - return value.toString(); - } - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/QueryMap.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/QueryMap.java deleted file mode 100644 index baea5645e..000000000 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/QueryMap.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.palantir.conjure.java.client.jaxrs.feign; - -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Retention; -import java.util.List; -import java.util.Map; - -/** - * A template parameter that can be applied to a Map that contains query - * parameters, where the keys are Strings that are the parameter names and the - * values are the parameter values. The queries specified by the map will be - * applied to the request after all other processing, and will take precedence - * over any previously specified query parameters. It is not necessary to - * reference the parameter map as a variable.
- *
- *
- * ...
- * @RequestLine("POST /servers")
- * void servers(@QueryMap Map);
- * ...
- *
- * @RequestLine("GET /servers/{serverId}?count={count}")
- * void get(@Param("serverId") String serverId, @Param("count") int count, @QueryMap Map);
- * ...
- * 
- * The annotated parameter must be an instance of {@link Map}, and the keys must - * be Strings. The query value of a key will be the value of its toString - * method, except in the following cases: - *
- *
- *
    - *
  • if the value is null, the value will remain null (rather than converting - * to the String "null") - *
  • if the value is an {@link Iterable}, it is converted to a {@link List} of - * String objects where each value in the list is either null if the original - * value was null or the value's toString representation otherwise. - *
- *
- * Once this conversion is applied, the query keys and resulting String values - * follow the same contract as if they were set using - * {@link RequestTemplate#query(String, String...)}. - */ -@Retention(RUNTIME) -@java.lang.annotation.Target(PARAMETER) -public @interface QueryMap { - /** Specifies whether parameter names and values are already encoded. */ - boolean encoded() default false; -} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/ReflectiveFeign.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/ReflectiveFeign.java index 4f9b3ff37..9a357f03e 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/ReflectiveFeign.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/ReflectiveFeign.java @@ -19,11 +19,7 @@ import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkState; -import com.palantir.conjure.java.client.jaxrs.feign.InvocationHandlerFactory.MethodHandler; -import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; -import com.palantir.conjure.java.client.jaxrs.feign.Request.Options; import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; -import com.palantir.conjure.java.client.jaxrs.feign.codec.EncodeException; import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; import com.palantir.conjure.java.client.jaxrs.feign.codec.ErrorDecoder; import java.lang.reflect.InvocationHandler; @@ -31,9 +27,9 @@ import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -41,23 +37,21 @@ public class ReflectiveFeign extends Feign { private final ParseHandlersByName targetToHandlersByName; - private final InvocationHandlerFactory factory; - ReflectiveFeign(ParseHandlersByName targetToHandlersByName, InvocationHandlerFactory factory) { + ReflectiveFeign(ParseHandlersByName targetToHandlersByName) { this.targetToHandlersByName = targetToHandlersByName; - this.factory = factory; } /** * creates an api binding to the {@code target}. As this invokes reflection, care should be taken * to cache the result. */ - @SuppressWarnings("unchecked") @Override + @SuppressWarnings({"ProxyNonConstantType", "RedundantControlFlow"}) public T newInstance(Target target) { Map nameToHandler = targetToHandlersByName.apply(target); - Map methodToHandler = new LinkedHashMap(); - List defaultMethodHandlers = new LinkedList(); + Map methodToHandler = new HashMap<>(); + List defaultMethodHandlers = new ArrayList<>(); for (Method method : target.type().getMethods()) { if (method.getDeclaringClass() == Object.class) { @@ -70,7 +64,7 @@ public T newInstance(Target target) { methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method))); } } - InvocationHandler handler = factory.create(target, methodToHandler); + InvocationHandler handler = new FeignInvocationHandler(target, methodToHandler); T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class[] {target.type()}, handler); for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) { @@ -81,16 +75,16 @@ public T newInstance(Target target) { static class FeignInvocationHandler implements InvocationHandler { - private final Target target; + private final Target target; private final Map dispatch; - FeignInvocationHandler(Target target, Map dispatch) { + FeignInvocationHandler(Target target, Map dispatch) { this.target = checkNotNull(target, "target"); this.dispatch = checkNotNull(dispatch, "dispatch for %s", target); } @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + public Object invoke(Object _proxy, Method method, Object[] args) throws Throwable { if ("equals".equals(method.getName())) { try { Object otherHandler = @@ -109,8 +103,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl @Override public boolean equals(Object obj) { - if (obj instanceof FeignInvocationHandler) { - FeignInvocationHandler other = (FeignInvocationHandler) obj; + if (obj instanceof FeignInvocationHandler other) { return target.equals(other.target); } return false; @@ -130,7 +123,6 @@ public String toString() { static final class ParseHandlersByName { private final Contract contract; - private final Options options; private final Encoder encoder; private final Decoder decoder; private final ErrorDecoder errorDecoder; @@ -138,32 +130,30 @@ static final class ParseHandlersByName { ParseHandlersByName( Contract contract, - Options options, Encoder encoder, Decoder decoder, ErrorDecoder errorDecoder, SynchronousMethodHandler.Factory factory) { - this.contract = contract; - this.options = options; - this.factory = factory; - this.errorDecoder = errorDecoder; + this.contract = checkNotNull(contract, "contract"); + this.factory = checkNotNull(factory, "factory"); + this.errorDecoder = checkNotNull(errorDecoder, "errorDecoder"); this.encoder = checkNotNull(encoder, "encoder"); this.decoder = checkNotNull(decoder, "decoder"); } - public Map apply(Target key) { - List metadata = contract.parseAndValidatateMetadata(key.type()); + public Map apply(Target key) { + List metadata = contract.parseAndValidateMetadata(key.type()); Map result = new LinkedHashMap(); for (MethodMetadata md : metadata) { BuildTemplateByResolvingArgs buildTemplate; - if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) { + if (!md.formParams().isEmpty()) { buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder); } else if (md.bodyIndex() != null) { buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder); } else { buildTemplate = new BuildTemplateByResolvingArgs(md); } - result.put(md.configKey(), factory.create(key, md, buildTemplate, options, decoder, errorDecoder)); + result.put(md.configKey(), factory.create(key, md, buildTemplate, decoder, errorDecoder)); } return result; } @@ -188,10 +178,11 @@ private BuildTemplateByResolvingArgs(MethodMetadata metadata) { try { indexToExpander.put( indexToExpanderClass.getKey(), - indexToExpanderClass.getValue().newInstance()); - } catch (InstantiationException e) { - throw new IllegalStateException(e); - } catch (IllegalAccessException e) { + indexToExpanderClass + .getValue() + .getDeclaredConstructor() + .newInstance()); + } catch (ReflectiveOperationException e) { throw new IllegalStateException(e); } } @@ -235,15 +226,15 @@ public RequestTemplate create(Object[] argv) { } private Object expandElements(Expander expander, Object value) { - if (value instanceof Iterable) { - return expandIterable(expander, (Iterable) value); + if (value instanceof Iterable iterable) { + return expandIterable(expander, iterable); } return expander.expand(value); } - private List expandIterable(Expander expander, Iterable value) { + private List expandIterable(Expander expander, Iterable value) { List values = new ArrayList(); - for (Object element : (Iterable) value) { + for (Object element : value) { if (element != null) { values.add(expander.expand(element)); } @@ -251,7 +242,6 @@ private List expandIterable(Expander expander, Iterable value) { return values; } - @SuppressWarnings("unchecked") private RequestTemplate addHeaderMapHeaders(Object[] argv, RequestTemplate mutable) { Map headerMap = (Map) argv[metadata.headerMapIndex()]; for (Entry currEntry : headerMap.entrySet()) { @@ -263,8 +253,8 @@ private RequestTemplate addHeaderMapHeaders(Object[] argv, RequestTemplate mutab Collection values = new ArrayList(); Object currValue = currEntry.getValue(); - if (currValue instanceof Iterable) { - Iterator iter = ((Iterable) currValue).iterator(); + if (currValue instanceof Iterable iterable) { + Iterator iter = iterable.iterator(); while (iter.hasNext()) { Object nextObject = iter.next(); values.add(nextObject == null ? null : nextObject.toString()); @@ -278,7 +268,6 @@ private RequestTemplate addHeaderMapHeaders(Object[] argv, RequestTemplate mutab return mutable; } - @SuppressWarnings("unchecked") private RequestTemplate addQueryMapQueryParameters(Object[] argv, RequestTemplate mutable) { Map queryMap = (Map) argv[metadata.queryMapIndex()]; for (Entry currEntry : queryMap.entrySet()) { @@ -290,8 +279,8 @@ private RequestTemplate addQueryMapQueryParameters(Object[] argv, RequestTemplat Collection values = new ArrayList(); Object currValue = currEntry.getValue(); - if (currValue instanceof Iterable) { - Iterator iter = ((Iterable) currValue).iterator(); + if (currValue instanceof Iterable iterable) { + Iterator iter = iterable.iterator(); while (iter.hasNext()) { Object nextObject = iter.next(); values.add(nextObject == null ? null : nextObject.toString()); @@ -305,12 +294,12 @@ private RequestTemplate addQueryMapQueryParameters(Object[] argv, RequestTemplat return mutable; } - protected RequestTemplate resolve(Object[] argv, RequestTemplate mutable, Map variables) { + protected RequestTemplate resolve(Object[] _argv, RequestTemplate mutable, Map variables) { return mutable.resolve(variables); } } - private static class BuildFormEncodedTemplateFromArgs extends BuildTemplateByResolvingArgs { + private static final class BuildFormEncodedTemplateFromArgs extends BuildTemplateByResolvingArgs { private final Encoder encoder; @@ -327,18 +316,12 @@ protected RequestTemplate resolve(Object[] argv, RequestTemplate mutable, Map variables) { Object body = argv[metadata.bodyIndex()]; checkArgument(body != null, "Body parameter %s was null", metadata.bodyIndex()); - try { - encoder.encode(body, metadata.bodyType(), mutable); - } catch (EncodeException e) { - throw e; - } catch (RuntimeException e) { - throw new EncodeException(e.getMessage(), e); - } + encoder.encode(body, metadata.bodyType(), mutable); return super.resolve(argv, mutable, variables); } } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Request.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Request.java index 8088886f6..7771ae1e3 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Request.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Request.java @@ -18,7 +18,7 @@ import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; import static com.palantir.conjure.java.client.jaxrs.feign.Util.valuesOrEmpty; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.Map; @@ -31,54 +31,40 @@ public final class Request { * No parameters can be null except {@code body} and {@code charset}. All parameters must be * effectively immutable, via safe copies, not mutating or otherwise. */ - public static Request create( - String method, String url, Map> headers, byte[] body, Charset charset) { - return new Request(method, url, headers, body, charset); + public static Request create(String method, String url, Map> headers, byte[] body) { + return new Request(method, url, headers, body); } private final String method; private final String url; private final Map> headers; private final byte[] body; - private final Charset charset; - Request(String method, String url, Map> headers, byte[] body, Charset charset) { + Request(String method, String url, Map> headers, byte[] body) { this.method = checkNotNull(method, "method of %s", url); this.url = checkNotNull(url, "url"); this.headers = checkNotNull(headers, "headers of %s %s", method, url); this.body = body; // nullable - this.charset = charset; // nullable } - /* Method to invoke on the server. */ + /** Method to invoke on the server. */ public String method() { return method; } - /* Fully resolved URL including query. */ + /** Fully resolved URL including query. */ public String url() { return url; } - /* Ordered list of headers that will be sent to the server. */ + /** Ordered list of headers that will be sent to the server. */ public Map> headers() { return headers; } - /** - * The character set with which the body is encoded, or null if unknown or not applicable. When - * this is present, you can use {@code new String(req.body(), req.charset())} to access the body - * as a String. - */ - public Charset charset() { - return charset; - } - /** * If present, this is the replayable body to send to the server. In some cases, this may be * interpretable as text. - * - * @see #charset() */ public byte[] body() { return body; @@ -94,42 +80,8 @@ public String toString() { } } if (body != null) { - builder.append('\n').append(charset != null ? new String(body, charset) : "Binary data"); + builder.append('\n').append(new String(body, StandardCharsets.UTF_8)); } return builder.toString(); } - - /* Controls the per-request settings currently required to be implemented by all {@link Client clients} */ - public static class Options { - - private final int connectTimeoutMillis; - private final int readTimeoutMillis; - - public Options(int connectTimeoutMillis, int readTimeoutMillis) { - this.connectTimeoutMillis = connectTimeoutMillis; - this.readTimeoutMillis = readTimeoutMillis; - } - - public Options() { - this(10 * 1000, 60 * 1000); - } - - /** - * Defaults to 10 seconds. {@code 0} implies no timeout. - * - * @see java.net.HttpURLConnection#getConnectTimeout() - */ - public int connectTimeoutMillis() { - return connectTimeoutMillis; - } - - /** - * Defaults to 60 seconds. {@code 0} implies no timeout. - * - * @see java.net.HttpURLConnection#getReadTimeout() - */ - public int readTimeoutMillis() { - return readTimeoutMillis; - } - } } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestInterceptor.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestInterceptor.java deleted file mode 100644 index 5c00ba6fb..000000000 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestInterceptor.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.palantir.conjure.java.client.jaxrs.feign; - -/** - * Zero or more {@code RequestInterceptors} may be configured for purposes such as adding headers to - * all requests. No guarantees are give with regards to the order that interceptors are applied. - * Once interceptors are applied, {@link Target#apply(RequestTemplate)} is called to create the - * immutable http request sent via {@link Client#execute(Request, Request.Options)}.

- * For example:
- *
- * public void apply(RequestTemplate input) {
- *     input.replaceHeader("X-Auth", currentToken);
- * }
- * 
- *

Configuration

{@code RequestInterceptors} are configured via {@link - * Feign.Builder#requestInterceptors}.

Implementation notes

Do not add - * parameters, such as {@code /path/{foo}/bar } in your implementation of {@link - * #apply(RequestTemplate)}.
Interceptors are applied after the template's parameters are - * {@link RequestTemplate#resolve(java.util.Map) resolved}. This is to ensure that you can - * implement signatures are interceptors.


Relationship to Retrofit 1.x

- * This class is similar to {@code RequestInterceptor.intercept()}, except that the implementation - * can read, remove, or otherwise mutate any part of the request template. - */ -public interface RequestInterceptor { - - /** - * Called for every request. Add data using methods on the supplied {@link RequestTemplate}. - */ - void apply(RequestTemplate template); -} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestLine.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestLine.java deleted file mode 100644 index 07fde1598..000000000 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestLine.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.palantir.conjure.java.client.jaxrs.feign; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Retention; - -/** - * Expands the request-line supplied in the {@code value}, permitting path and query variables, or - * just the http method.
- *
- * ...
- * @RequestLine("POST /servers")
- * ...
- *
- * @RequestLine("GET /servers/{serverId}?count={count}")
- * void get(@Param("serverId") String serverId, @Param("count") int count);
- * ...
- *
- * @RequestLine("GET")
- * Response getNext(URI nextLink);
- * ...
- * 
- * HTTP version suffix is optional, but permitted. There are no guarantees this version will impact - * that sent by the client.
- *
- * @RequestLine("POST /servers HTTP/1.1")
- * ...
- * 
- *
Note: Query params do not overwrite each other. All queries with the same - * name will be included in the request.

Relationship to JAXRS

The following - * two forms are identical.
Feign: - *
- * @RequestLine("GET /servers/{serverId}?count={count}")
- * void get(@Param("serverId") String serverId, @Param("count") int count);
- * ...
- * 
- *
JAX-RS: - *
- * @GET @Path("/servers/{serverId}")
- * void get(@PathParam("serverId") String serverId, @QueryParam("count") int count);
- * ...
- * 
- */ -@java.lang.annotation.Target(METHOD) -@Retention(RUNTIME) -public @interface RequestLine { - - String value(); - - boolean decodeSlash() default true; -} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestTemplate.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestTemplate.java index c5be7b04d..6d8f125d4 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestTemplate.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestTemplate.java @@ -24,11 +24,10 @@ import static com.palantir.conjure.java.client.jaxrs.feign.Util.valuesOrEmpty; import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; -import java.io.Serializable; +import java.io.UncheckedIOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; -import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -45,31 +44,28 @@ * javax.ws.rs.client.Invocation.Builder}, ensuring you can modify any part of the request. However, * this object is mutable, so needs to be guarded with the copy constructor. */ -public final class RequestTemplate implements Serializable { +@SuppressWarnings("MissingSummary") +public final class RequestTemplate { - private static final long serialVersionUID = 1L; private final Map> queries = new LinkedHashMap>(); private final Map> headers = new LinkedHashMap>(); private String method; - /* final to encourage mutable use vs replacing the object. */ + /** final to encourage mutable use vs replacing the object. */ private StringBuilder url = new StringBuilder(); - private transient Charset charset; + private byte[] body; - private String bodyTemplate; private boolean decodeSlash = true; public RequestTemplate() {} - /* Copy constructor. Use this when making templates. */ + /** Copy constructor. Use this when making templates. */ public RequestTemplate(RequestTemplate toCopy) { checkNotNull(toCopy, "toCopy"); this.method = toCopy.method; this.url.append(toCopy.url); this.queries.putAll(toCopy.queries); this.headers.putAll(toCopy.headers); - this.charset = toCopy.charset; this.body = toCopy.body; - this.bodyTemplate = toCopy.bodyTemplate; this.decodeSlash = toCopy.decodeSlash; } @@ -77,7 +73,7 @@ private static String urlDecode(String arg) { try { return URLDecoder.decode(arg, UTF_8.name()); } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); + throw new UncheckedIOException(e); } } @@ -85,21 +81,7 @@ private static String urlEncode(Object arg) { try { return URLEncoder.encode(String.valueOf(arg), UTF_8.name()); } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } - - private static boolean isHttpUrl(CharSequence value) { - return value.length() >= 4 && value.subSequence(0, 3).equals("http".substring(0, 3)); - } - - private static CharSequence removeTrailingSlash(CharSequence charSequence) { - if (charSequence != null - && charSequence.length() > 0 - && charSequence.charAt(charSequence.length() - 1) == '/') { - return charSequence.subSequence(0, charSequence.length() - 1); - } else { - return charSequence; + throw new UncheckedIOException(e); } } @@ -124,9 +106,10 @@ public static String expand(String template, Map variables) { boolean inVar = false; StringBuilder var = new StringBuilder(); StringBuilder builder = new StringBuilder(); - for (char c : template.toCharArray()) { + for (int i = 0; i < template.length(); i++) { + char c = template.charAt(i); switch (c) { - case '{': + case '{' -> { if (inVar) { // '{{' is an escape: write the brace and don't interpret as a variable builder.append("{"); @@ -134,8 +117,8 @@ public static String expand(String template, Map variables) { break; } inVar = true; - break; - case '}': + } + case '}' -> { if (!inVar) { // then write the brace literally builder.append('}'); break; @@ -149,13 +132,14 @@ public static String expand(String template, Map variables) { builder.append('{').append(key).append('}'); } var = new StringBuilder(); - break; - default: + } + default -> { if (inVar) { var.append(c); } else { builder.append(c); } + } } } return builder.toString(); @@ -229,32 +213,24 @@ public RequestTemplate resolve(Map unencoded) { } headers.clear(); headers.putAll(resolvedHeaders); - if (bodyTemplate != null) { - body(urlDecode(expand(bodyTemplate, encoded))); - } return this; } - /* roughly analogous to {@code javax.ws.rs.client.Target.request()}. */ + /** roughly analogous to {@code javax.ws.rs.client.Target.request()}. */ public Request request() { Map> safeCopy = new LinkedHashMap>(); safeCopy.putAll(headers); - return Request.create( - method, - new StringBuilder(url).append(queryLine()).toString(), - Collections.unmodifiableMap(safeCopy), - body, - charset); + return Request.create(method, url + queryLine(), Collections.unmodifiableMap(safeCopy), body); } - /* @see Request#method() */ + /** @see Request#method() */ public RequestTemplate method(String method) { this.method = checkNotNull(method, "method"); checkArgument(method.matches("^[A-Z]+$"), "Invalid HTTP Method: %s", method); return this; } - /* @see Request#method() */ + /** @see Request#method() */ public String method() { return method; } @@ -268,17 +244,19 @@ public boolean decodeSlash() { return decodeSlash; } - /* @see #url() */ - public RequestTemplate append(CharSequence value) { + /** @see #url() */ + public RequestTemplate append(String value) { url.append(value); url = pullAnyQueriesOutOfUrl(url); return this; } - /* @see #url() */ - public RequestTemplate insert(int pos, CharSequence value) { - if (isHttpUrl(value)) { - value = removeTrailingSlash(value); + /** @see #url() */ + public RequestTemplate insert(int pos, String value) { + if (value.startsWith("http")) { + if (value.endsWith("/")) { + value = value.substring(0, value.length() - 1); + } if (url.length() > 0 && url.charAt(0) != '/') { url.insert(0, '/'); } @@ -295,7 +273,7 @@ public String url() { * Replaces queries with the specified {@code name} with the {@code values} supplied. *
Values can be passed in decoded or in url-encoded form depending on the value of the * {@code encoded} parameter. - *
When the {@code value} is {@code null}, all queries with the {@code configKey} are + *
When the {@code values} is {@code null}, all queries with the {@code configKey} are * removed.


relationship to JAXRS 2.0

Like {@code WebTarget.query}, * except the values can be templatized.
ex.
*
@@ -319,7 +297,7 @@ public RequestTemplate query(boolean encoded, String name, String... values) {
         return doQuery(encoded, name, values);
     }
 
-    /* @see #query(boolean, String, String...) */
+    /** @see #query(boolean, String, String...) */
     public RequestTemplate query(boolean encoded, String name, Iterable values) {
         return doQuery(encoded, name, values);
     }
@@ -413,7 +391,7 @@ public Map> queries() {
 
     /**
      * Replaces headers with the specified {@code configKey} with the {@code values} supplied. 
- * When the {@code value} is {@code null}, all headers with the {@code configKey} are removed. + * When the {@code values} is {@code null}, all headers with the {@code configKey} are removed. *


relationship to JAXRS 2.0

Like {@code WebTarget.queries} and * {@code javax.ws.rs.client.Invocation.Builder.header}, except the values can be templatized. *
ex.
@@ -438,7 +416,7 @@ public RequestTemplate header(String name, String... values) { return this; } - /* @see #header(String, String...) */ + /** @see #header(String, String...) */ public RequestTemplate header(String name, Iterable values) { if (values != null) { return header(name, toArray(values, String.class)); @@ -481,35 +459,13 @@ public Map> headers() { * * @see Request#body() */ - public RequestTemplate body(byte[] bodyData, Charset charset) { - this.bodyTemplate = null; - this.charset = charset; + public RequestTemplate body(byte[] bodyData) { this.body = bodyData; int bodyLength = bodyData != null ? bodyData.length : 0; header(CONTENT_LENGTH, String.valueOf(bodyLength)); return this; } - /** - * replaces the {@link Util#CONTENT_LENGTH} header.
Usually populated by an {@link - * Encoder}. - * - * @see Request#body() - */ - public RequestTemplate body(String bodyText) { - byte[] bodyData = bodyText != null ? bodyText.getBytes(UTF_8) : null; - return body(bodyData, UTF_8); - } - - /** - * The character set with which the body is encoded, or null if unknown or not applicable. When - * this is present, you can use {@code new String(req.body(), req.charset())} to access the body - * as a String. - */ - public Charset charset() { - return charset; - } - /** * @see Request#body() */ @@ -517,26 +473,6 @@ public byte[] body() { return body; } - /** - * populated by {@link Body} - * - * @see Request#body() - */ - public RequestTemplate bodyTemplate(String bodyTemplate) { - this.bodyTemplate = bodyTemplate; - this.charset = null; - this.body = null; - return this; - } - - /** - * @see Request#body() - * @see #expand(String, Map) - */ - public String bodyTemplate() { - return bodyTemplate; - } - /** * if there are any query params in the URL, this will extract them out. */ diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Response.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Response.java index 2f331e4c8..e5342fd2a 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Response.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Response.java @@ -28,6 +28,7 @@ import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.Charset; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; @@ -253,7 +254,7 @@ private static Map> caseInsensitiveCopyOf(Map> entry : headers.entrySet()) { String headerName = entry.getKey(); if (!result.containsKey(headerName)) { - result.put(headerName.toLowerCase(Locale.ROOT), new LinkedList()); + result.put(headerName.toLowerCase(Locale.ROOT), new ArrayList<>()); } result.get(headerName).addAll(entry.getValue()); } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RetryableException.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RetryableException.java deleted file mode 100644 index de4bb4156..000000000 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RetryableException.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.palantir.conjure.java.client.jaxrs.feign; - -import com.palantir.conjure.java.client.jaxrs.feign.codec.ErrorDecoder; -import java.util.Date; - -/** - * This exception is raised when the {@link Response} is deemed to be retryable, typically via an - * {@link ErrorDecoder} when the {@link Response#status() status} is 503. - */ -public class RetryableException extends FeignException { - - private static final long serialVersionUID = 1L; - - private final Long retryAfter; - - /** - * @param retryAfter usually corresponds to the {@link Util#RETRY_AFTER} header. - */ - public RetryableException(String message, Throwable cause, Date retryAfter) { - super(message, cause); - this.retryAfter = retryAfter != null ? retryAfter.getTime() : null; - } - - /** - * @param retryAfter usually corresponds to the {@link Util#RETRY_AFTER} header. - */ - public RetryableException(String message, Date retryAfter) { - super(message); - this.retryAfter = retryAfter != null ? retryAfter.getTime() : null; - } - - /** - * Sometimes corresponds to the {@link Util#RETRY_AFTER} header present in {@code 503} - * status. Other times parsed from an application-specific response. Null if unknown. - */ - public Date retryAfter() { - return retryAfter != null ? new Date(retryAfter) : null; - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Retryer.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Retryer.java deleted file mode 100644 index ea5e05957..000000000 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Retryer.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.palantir.conjure.java.client.jaxrs.feign; - -import static java.util.concurrent.TimeUnit.SECONDS; - -/** - * Cloned for each invocation to {@link Client#execute(Request, Request.Options)}. - * Implementations may keep state to determine if retry operations should continue or not. - */ -public interface Retryer extends Cloneable { - - /** - * if retry is permitted, return (possibly after sleeping). Otherwise propagate the exception. - */ - void continueOrPropagate(RetryableException e); - - Retryer clone(); - - public static class Default implements Retryer { - - private final int maxAttempts; - private final long period; - private final long maxPeriod; - int attempt; - long sleptForMillis; - - public Default() { - this(100, SECONDS.toMillis(1), 5); - } - - public Default(long period, long maxPeriod, int maxAttempts) { - this.period = period; - this.maxPeriod = maxPeriod; - this.maxAttempts = maxAttempts; - this.attempt = 1; - } - - // visible for testing; - protected long currentTimeMillis() { - return System.currentTimeMillis(); - } - - public void continueOrPropagate(RetryableException e) { - if (attempt++ >= maxAttempts) { - throw e; - } - - long interval; - if (e.retryAfter() != null) { - interval = e.retryAfter().getTime() - currentTimeMillis(); - if (interval > maxPeriod) { - interval = maxPeriod; - } - if (interval < 0) { - return; - } - } else { - interval = nextMaxInterval(); - } - try { - Thread.sleep(interval); - } catch (InterruptedException ignored) { - Thread.currentThread().interrupt(); - } - sleptForMillis += interval; - } - - /** - * Calculates the time interval to a retry attempt.
The interval increases exponentially - * with each attempt, at a rate of nextInterval *= 1.5 (where 1.5 is the backoff factor), to the - * maximum interval. - * - * @return time in nanoseconds from now until the next attempt. - */ - long nextMaxInterval() { - long interval = (long) (period * Math.pow(1.5, attempt - 1)); - return interval > maxPeriod ? maxPeriod : interval; - } - - @Override - public Retryer clone() { - return new Default(period, maxPeriod, maxAttempts); - } - } - - /** - * Implementation that never retries request. It propagates the RetryableException. - */ - Retryer NEVER_RETRY = new Retryer() { - - @Override - public void continueOrPropagate(RetryableException e) { - throw e; - } - - @Override - public Retryer clone() { - return this; - } - }; -} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/SynchronousMethodHandler.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/SynchronousMethodHandler.java index ed7e1fe68..67f46c74c 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/SynchronousMethodHandler.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/SynchronousMethodHandler.java @@ -15,181 +15,94 @@ */ package com.palantir.conjure.java.client.jaxrs.feign; -import static com.palantir.conjure.java.client.jaxrs.feign.FeignException.errorExecuting; -import static com.palantir.conjure.java.client.jaxrs.feign.FeignException.errorReading; import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; import static com.palantir.conjure.java.client.jaxrs.feign.Util.ensureClosed; -import com.palantir.conjure.java.client.jaxrs.feign.InvocationHandlerFactory.MethodHandler; -import com.palantir.conjure.java.client.jaxrs.feign.Request.Options; -import com.palantir.conjure.java.client.jaxrs.feign.codec.DecodeException; import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import com.palantir.conjure.java.client.jaxrs.feign.codec.ErrorDecoder; -import java.io.IOException; -import java.util.List; -import java.util.concurrent.TimeUnit; final class SynchronousMethodHandler implements MethodHandler { - private static final long MAX_RESPONSE_BUFFER_SIZE = 8192L; + private static final long MAX_RESPONSE_BUFFER_SIZE = 8192L; - private final MethodMetadata metadata; - private final Target target; - private final Client client; - private final Retryer retryer; - private final List requestInterceptors; - private final Logger logger; - private final Logger.Level logLevel; - private final RequestTemplate.Factory buildTemplateFromArgs; - private final Options options; - private final Decoder decoder; - private final ErrorDecoder errorDecoder; - private final boolean decode404; - - private SynchronousMethodHandler(Target target, Client client, Retryer retryer, - List requestInterceptors, Logger logger, - Logger.Level logLevel, MethodMetadata metadata, - RequestTemplate.Factory buildTemplateFromArgs, Options options, - Decoder decoder, ErrorDecoder errorDecoder, boolean decode404) { - this.target = checkNotNull(target, "target"); - this.client = checkNotNull(client, "client for %s", target); - this.retryer = checkNotNull(retryer, "retryer for %s", target); - this.requestInterceptors = - checkNotNull(requestInterceptors, "requestInterceptors for %s", target); - this.logger = checkNotNull(logger, "logger for %s", target); - this.logLevel = checkNotNull(logLevel, "logLevel for %s", target); - this.metadata = checkNotNull(metadata, "metadata for %s", target); - this.buildTemplateFromArgs = checkNotNull(buildTemplateFromArgs, "metadata for %s", target); - this.options = checkNotNull(options, "options for %s", target); - this.errorDecoder = checkNotNull(errorDecoder, "errorDecoder for %s", target); - this.decoder = checkNotNull(decoder, "decoder for %s", target); - this.decode404 = decode404; - } - - @Override - public Object invoke(Object[] argv) throws Throwable { - RequestTemplate template = buildTemplateFromArgs.create(argv); - Retryer retryer = this.retryer.clone(); - while (true) { - try { - return executeAndDecode(template); - } catch (RetryableException e) { - retryer.continueOrPropagate(e); - if (logLevel != Logger.Level.NONE) { - logger.logRetry(metadata.configKey(), logLevel); - } - continue; - } - } - } - - Object executeAndDecode(RequestTemplate template) throws Throwable { - Request request = targetRequest(template); - - if (logLevel != Logger.Level.NONE) { - logger.logRequest(metadata.configKey(), logLevel, request); + private final MethodMetadata metadata; + private final Target target; + private final Client client; + private final RequestTemplate.Factory buildTemplateFromArgs; + private final Decoder decoder; + private final ErrorDecoder errorDecoder; + + private SynchronousMethodHandler( + Target target, + Client client, + MethodMetadata metadata, + RequestTemplate.Factory buildTemplateFromArgs, + Decoder decoder, + ErrorDecoder errorDecoder) { + this.target = checkNotNull(target, "target"); + this.client = checkNotNull(client, "client for %s", target); + this.metadata = checkNotNull(metadata, "metadata for %s", target); + this.buildTemplateFromArgs = checkNotNull(buildTemplateFromArgs, "metadata for %s", target); + this.errorDecoder = checkNotNull(errorDecoder, "errorDecoder for %s", target); + this.decoder = checkNotNull(decoder, "decoder for %s", target); } - Response response; - long start = System.nanoTime(); - try { - response = client.execute(request, options); - } catch (IOException e) { - if (logLevel != Logger.Level.NONE) { - logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start)); - } - throw errorExecuting(request, e); + @Override + public Object invoke(Object[] argv) throws Throwable { + RequestTemplate template = buildTemplateFromArgs.create(argv); + return executeAndDecode(template); } - long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); - boolean shouldClose = true; - try { - if (logLevel != Logger.Level.NONE) { - response = - logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime); - } - if (Response.class == metadata.returnType()) { - if (response.body() == null) { - return response; - } - if (response.body().length() == null || - response.body().length() > MAX_RESPONSE_BUFFER_SIZE) { - shouldClose = false; - return response; + Object executeAndDecode(RequestTemplate template) throws Throwable { + Request request = target.apply(new RequestTemplate(template)); + + Response response = client.execute(request); + + boolean shouldClose = true; + try { + if (Response.class == metadata.returnType()) { + if (response.body() == null) { + return response; + } + if (response.body().length() == null || response.body().length() > MAX_RESPONSE_BUFFER_SIZE) { + shouldClose = false; + return response; + } + // Ensure the response body is disconnected + byte[] bodyData = Util.toByteArray(response.body().asInputStream()); + return Response.create(response.status(), response.reason(), response.headers(), bodyData); + } + if (response.status() >= 200 && response.status() < 300) { + if (void.class == metadata.returnType()) { + return null; + } else { + return decoder.decode(response, metadata.returnType()); + } + } else { + throw errorDecoder.decode(metadata.configKey(), response); + } + } finally { + if (shouldClose) { + ensureClosed(response.body()); + } } - // Ensure the response body is disconnected - byte[] bodyData = Util.toByteArray(response.body().asInputStream()); - return Response.create(response.status(), response.reason(), response.headers(), bodyData); - } - if (response.status() >= 200 && response.status() < 300) { - if (void.class == metadata.returnType()) { - return null; - } else { - return decode(response); - } - } else if (decode404 && response.status() == 404) { - return decoder.decode(response, metadata.returnType()); - } else { - throw errorDecoder.decode(metadata.configKey(), response); - } - } catch (IOException e) { - if (logLevel != Logger.Level.NONE) { - logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime); - } - throw errorReading(request, response, e); - } finally { - if (shouldClose) { - ensureClosed(response.body()); - } - } - } - - long elapsedTime(long start) { - return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); - } - - Request targetRequest(RequestTemplate template) { - for (RequestInterceptor interceptor : requestInterceptors) { - interceptor.apply(template); - } - return target.apply(new RequestTemplate(template)); - } - - Object decode(Response response) throws Throwable { - try { - return decoder.decode(response, metadata.returnType()); - } catch (FeignException e) { - throw e; - } catch (RuntimeException e) { - throw new DecodeException(e.getMessage(), e); } - } - static class Factory { + static class Factory { - private final Client client; - private final Retryer retryer; - private final List requestInterceptors; - private final Logger logger; - private final Logger.Level logLevel; - private final boolean decode404; + private final Client client; - Factory(Client client, Retryer retryer, List requestInterceptors, - Logger logger, Logger.Level logLevel, boolean decode404) { - this.client = checkNotNull(client, "client"); - this.retryer = checkNotNull(retryer, "retryer"); - this.requestInterceptors = checkNotNull(requestInterceptors, "requestInterceptors"); - this.logger = checkNotNull(logger, "logger"); - this.logLevel = checkNotNull(logLevel, "logLevel"); - this.decode404 = decode404; - } + Factory(Client client) { + this.client = checkNotNull(client, "client"); + } - public MethodHandler create(Target target, MethodMetadata md, - RequestTemplate.Factory buildTemplateFromArgs, - Options options, Decoder decoder, ErrorDecoder errorDecoder) { - return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger, - logLevel, md, buildTemplateFromArgs, options, decoder, - errorDecoder, decode404); + public MethodHandler create( + Target target, + MethodMetadata md, + RequestTemplate.Factory buildTemplateFromArgs, + Decoder decoder, + ErrorDecoder errorDecoder) { + return new SynchronousMethodHandler(target, client, md, buildTemplateFromArgs, decoder, errorDecoder); + } } - } } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Target.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Target.java index 348ae1112..18e67460a 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Target.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Target.java @@ -15,9 +15,6 @@ */ package com.palantir.conjure.java.client.jaxrs.feign; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.emptyToNull; - /** *

relationship to JAXRS 2.0

Similar to {@code * javax.ws.rs.client.WebTarget}, as it produces requests. However, {@link RequestTemplate} is a @@ -27,13 +24,10 @@ */ public interface Target { - /* The type of the interface this target applies to. ex. {@code Route53}. */ + /** The type of the interface this target applies to. ex. {@code Route53}. */ Class type(); - /* configuration key associated with this target. For example, {@code route53}. */ - String name(); - - /* base HTTP URL of the target. For example, {@code https://api/v2}. */ + /** base HTTP URL of the target. For example, {@code https://api/v2}. */ String url(); /** @@ -50,140 +44,5 @@ public interface Target { * javax.ws.rs.client.WebTarget.request()}, except that we expect transient, but necessary * decoration to be applied on invocation. */ - public Request apply(RequestTemplate input); - - public static class HardCodedTarget implements Target { - - private final Class type; - private final String name; - private final String url; - - public HardCodedTarget(Class type, String url) { - this(type, url, url); - } - - public HardCodedTarget(Class type, String name, String url) { - this.type = checkNotNull(type, "type"); - this.name = checkNotNull(emptyToNull(name), "name"); - this.url = checkNotNull(emptyToNull(url), "url"); - } - - @Override - public Class type() { - return type; - } - - @Override - public String name() { - return name; - } - - @Override - public String url() { - return url; - } - - /* no authentication or other special activity. just insert the url. */ - @Override - public Request apply(RequestTemplate input) { - if (input.url().indexOf("http") != 0) { - input.insert(0, url()); - } - return input.request(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof HardCodedTarget) { - HardCodedTarget other = (HardCodedTarget) obj; - return type.equals(other.type) && name.equals(other.name) && url.equals(other.url); - } - return false; - } - - @Override - public int hashCode() { - int result = 17; - result = 31 * result + type.hashCode(); - result = 31 * result + name.hashCode(); - result = 31 * result + url.hashCode(); - return result; - } - - @Override - public String toString() { - if (name.equals(url)) { - return "HardCodedTarget(type=" + type.getSimpleName() + ", url=" + url + ")"; - } - return "HardCodedTarget(type=" + type.getSimpleName() + ", name=" + name + ", url=" + url + ")"; - } - } - - public static final class EmptyTarget implements Target { - - private final Class type; - private final String name; - - EmptyTarget(Class type, String name) { - this.type = checkNotNull(type, "type"); - this.name = checkNotNull(emptyToNull(name), "name"); - } - - public static EmptyTarget create(Class type) { - return new EmptyTarget(type, "empty:" + type.getSimpleName()); - } - - public static EmptyTarget create(Class type, String name) { - return new EmptyTarget(type, name); - } - - @Override - public Class type() { - return type; - } - - @Override - public String name() { - return name; - } - - @Override - public String url() { - throw new UnsupportedOperationException("Empty targets don't have URLs"); - } - - @Override - public Request apply(RequestTemplate input) { - if (input.url().indexOf("http") != 0) { - throw new UnsupportedOperationException( - "Request with non-absolute URL not supported with empty target"); - } - return input.request(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof EmptyTarget) { - EmptyTarget other = (EmptyTarget) obj; - return type.equals(other.type) && name.equals(other.name); - } - return false; - } - - @Override - public int hashCode() { - int result = 17; - result = 31 * result + type.hashCode(); - result = 31 * result + name.hashCode(); - return result; - } - - @Override - public String toString() { - if (name.equals("empty:" + type.getSimpleName())) { - return "EmptyTarget(type=" + type.getSimpleName() + ")"; - } - return "EmptyTarget(type=" + type.getSimpleName() + ", name=" + name + ")"; - } - } + Request apply(RequestTemplate input); } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Types.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Types.java index b4d6525f8..5eb985271 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Types.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Types.java @@ -44,9 +44,7 @@ static Class getRawType(Type type) { // Type is a normal class. return (Class) type; - } else if (type instanceof ParameterizedType) { - ParameterizedType parameterizedType = (ParameterizedType) type; - + } else if (type instanceof ParameterizedType parameterizedType) { // I'm not exactly sure why getRawType() returns Type instead of Class. Neal isn't either but // suspects some pathological case related to nested classes exists. Type rawType = parameterizedType.getRawType(); @@ -55,8 +53,8 @@ static Class getRawType(Type type) { } return (Class) rawType; - } else if (type instanceof GenericArrayType) { - Type componentType = ((GenericArrayType) type).getGenericComponentType(); + } else if (type instanceof GenericArrayType genericArrayType) { + Type componentType = genericArrayType.getGenericComponentType(); return Array.newInstance(getRawType(componentType), 0).getClass(); } else if (type instanceof TypeVariable) { @@ -64,8 +62,8 @@ static Class getRawType(Type type) { // type that's more general than necessary is okay. return Object.class; - } else if (type instanceof WildcardType) { - return getRawType(((WildcardType) type).getUpperBounds()[0]); + } else if (type instanceof WildcardType wildcardType) { + return getRawType(wildcardType.getUpperBounds()[0]); } else { String className = type == null ? "null" : type.getClass().getName(); @@ -85,39 +83,31 @@ static boolean equals(Type a, Type b) { } else if (a instanceof Class) { return a.equals(b); // Class already specifies equals(). - } else if (a instanceof ParameterizedType) { - if (!(b instanceof ParameterizedType)) { + } else if (a instanceof ParameterizedType pa) { + if (!(b instanceof ParameterizedType pb)) { return false; } - ParameterizedType pa = (ParameterizedType) a; - ParameterizedType pb = (ParameterizedType) b; return equal(pa.getOwnerType(), pb.getOwnerType()) && pa.getRawType().equals(pb.getRawType()) && Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments()); - } else if (a instanceof GenericArrayType) { - if (!(b instanceof GenericArrayType)) { + } else if (a instanceof GenericArrayType ga) { + if (!(b instanceof GenericArrayType gb)) { return false; } - GenericArrayType ga = (GenericArrayType) a; - GenericArrayType gb = (GenericArrayType) b; return equals(ga.getGenericComponentType(), gb.getGenericComponentType()); - } else if (a instanceof WildcardType) { - if (!(b instanceof WildcardType)) { + } else if (a instanceof WildcardType wa) { + if (!(b instanceof WildcardType wb)) { return false; } - WildcardType wa = (WildcardType) a; - WildcardType wb = (WildcardType) b; return Arrays.equals(wa.getUpperBounds(), wb.getUpperBounds()) && Arrays.equals(wa.getLowerBounds(), wb.getLowerBounds()); - } else if (a instanceof TypeVariable) { - if (!(b instanceof TypeVariable)) { + } else if (a instanceof TypeVariable va) { + if (!(b instanceof TypeVariable vb)) { return false; } - TypeVariable va = (TypeVariable) a; - TypeVariable vb = (TypeVariable) b; return va.getGenericDeclaration() == vb.getGenericDeclaration() && va.getName().equals(vb.getName()); @@ -275,9 +265,9 @@ private static Type resolveTypeVariable(Type context, Class contextRawType, T } Type declaredBy = getGenericSupertype(context, contextRawType, declaredByRaw); - if (declaredBy instanceof ParameterizedType) { + if (declaredBy instanceof ParameterizedType parameterizedType) { int index = indexOf(declaredByRaw.getTypeParameters(), unknown); - return ((ParameterizedType) declaredBy).getActualTypeArguments()[index]; + return parameterizedType.getActualTypeArguments()[index]; } return unknown; @@ -293,7 +283,7 @@ private static Class declaringClassOf(TypeVariable typeVariable) { } private static void checkNotPrimitive(Type type) { - if (type instanceof Class && ((Class) type).isPrimitive()) { + if (type instanceof Class classType && classType.isPrimitive()) { throw new IllegalArgumentException(); } } @@ -306,8 +296,8 @@ static final class ParameterizedTypeImpl implements ParameterizedType { ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) { // Require an owner type if the raw type needs it. - if (rawType instanceof Class - && (ownerType == null) != (((Class) rawType).getEnclosingClass() == null)) { + if (rawType instanceof Class classType + && (ownerType == null) != (classType.getEnclosingClass() == null)) { throw new IllegalArgumentException(); } @@ -323,21 +313,24 @@ static final class ParameterizedTypeImpl implements ParameterizedType { } } + @Override public Type[] getActualTypeArguments() { return typeArguments.clone(); } + @Override public Type getRawType() { return rawType; } + @Override public Type getOwnerType() { return ownerType; } @Override public boolean equals(Object other) { - return other instanceof ParameterizedType && Types.equals(this, (ParameterizedType) other); + return other instanceof ParameterizedType parameterizedType && Types.equals(this, parameterizedType); } @Override @@ -368,13 +361,14 @@ private static final class GenericArrayTypeImpl implements GenericArrayType { this.componentType = componentType; } + @Override public Type getGenericComponentType() { return componentType; } @Override public boolean equals(Object o) { - return o instanceof GenericArrayType && Types.equals(this, (GenericArrayType) o); + return o instanceof GenericArrayType genericArrayType && Types.equals(this, genericArrayType); } @Override @@ -426,17 +420,19 @@ static final class WildcardTypeImpl implements WildcardType { } } + @Override public Type[] getUpperBounds() { return new Type[] {upperBound}; } + @Override public Type[] getLowerBounds() { return lowerBound != null ? new Type[] {lowerBound} : EMPTY_TYPE_ARRAY; } @Override public boolean equals(Object other) { - return other instanceof WildcardType && Types.equals(this, (WildcardType) other); + return other instanceof WildcardType wildcardType && Types.equals(this, wildcardType); } @Override diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Util.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Util.java index eb324964e..6b4bcc4e0 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Util.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Util.java @@ -17,6 +17,7 @@ import static java.lang.String.format; +import com.google.errorprone.annotations.FormatMethod; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.IOException; @@ -26,9 +27,7 @@ import java.lang.reflect.Array; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.lang.reflect.WildcardType; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharacterCodingException; @@ -46,39 +45,17 @@ /** * Utilities, typically copied in from guava, so as to avoid dependency conflicts. */ -public class Util { +public final class Util { /** * The HTTP Content-Length header field name. */ public static final String CONTENT_LENGTH = "Content-Length"; - /** - * The HTTP Content-Encoding header field name. - */ - public static final String CONTENT_ENCODING = "Content-Encoding"; - /** - * The HTTP Retry-After header field name. - */ - public static final String RETRY_AFTER = "Retry-After"; - /** - * Value for the Content-Encoding header that indicates that GZIP encoding is in use. - */ - public static final String ENCODING_GZIP = "gzip"; - /** - * Value for the Content-Encoding header that indicates that DEFLATE encoding is in use. - */ - public static final String ENCODING_DEFLATE = "deflate"; /** * UTF-8: eight-bit UCS Transformation Format. */ public static final Charset UTF_8 = Charset.forName("UTF-8"); - // com.google.common.base.Charsets - /** - * ISO-8859-1: ISO Latin Alphabet Number 1 (ISO-LATIN-1). - */ - public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); - private static final int BUF_SIZE = 0x800; // 2K chars (4K bytes) /** @@ -93,6 +70,7 @@ private Util() { // no instances /** * Copy of {@code com.google.common.base.Preconditions#checkArgument}. */ + @FormatMethod public static void checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { if (!expression) { throw new IllegalArgumentException(format(errorMessageTemplate, errorMessageArgs)); @@ -102,6 +80,7 @@ public static void checkArgument(boolean expression, String errorMessageTemplate /** * Copy of {@code com.google.common.base.Preconditions#checkNotNull}. */ + @FormatMethod public static T checkNotNull(T reference, String errorMessageTemplate, Object... errorMessageArgs) { if (reference == null) { // If either of these parameters is null, the right thing happens anyway @@ -113,6 +92,7 @@ public static T checkNotNull(T reference, String errorMessageTemplate, Objec /** * Copy of {@code com.google.common.base.Preconditions#checkState}. */ + @FormatMethod public static void checkState(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { if (!expression) { throw new IllegalStateException(format(errorMessageTemplate, errorMessageArgs)); @@ -143,7 +123,6 @@ public static String emptyToNull(String string) { /** * Adapted from {@code com.google.common.base.Strings#emptyToNull}. */ - @SuppressWarnings("unchecked") public static T[] toArray(Iterable iterable, Class type) { Collection collection; if (iterable instanceof Collection) { @@ -174,34 +153,6 @@ public static void ensureClosed(Closeable closeable) { } } - /** - * Resolves the last type parameter of the parameterized {@code supertype}, based on the {@code - * genericContext}, into its upper bounds.

Implementation copied from {@code - * retrofit.RestMethodInfo}. - * - * @param genericContext Ex. {@link java.lang.reflect.Field#getGenericType()} - * @param supertype Ex. {@code Decoder.class} - * @return in the example above, the type parameter of {@code Decoder}. - * @throws IllegalStateException if {@code supertype} cannot be resolved into a parameterized type - * using {@code context}. - */ - public static Type resolveLastTypeParameter(Type genericContext, Class supertype) throws IllegalStateException { - Type resolvedSuperType = Types.getSupertype(genericContext, Types.getRawType(genericContext), supertype); - checkState( - resolvedSuperType instanceof ParameterizedType, - "could not resolve %s into a parameterized type %s", - genericContext, - supertype); - Type[] types = ParameterizedType.class.cast(resolvedSuperType).getActualTypeArguments(); - for (int i = 0; i < types.length; i++) { - Type type = types[i]; - if (type instanceof WildcardType) { - types[i] = ((WildcardType) type).getUpperBounds()[0]; - } - } - return types[types.length - 1]; - } - /** * This returns well known empty values for well-known java types. This returns null for types not * in the following list. @@ -235,14 +186,17 @@ public static Object emptyValueOf(Type type) { empties.put( Iterator.class, new Iterator() { // Collections.emptyIterator is a 1.7 api + @Override public boolean hasNext() { return false; } + @Override public Object next() { throw new NoSuchElementException(); } + @Override public void remove() { throw new IllegalStateException(); } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/auth/Base64.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/auth/Base64.java deleted file mode 100644 index 4c6791a12..000000000 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/auth/Base64.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.palantir.conjure.java.client.jaxrs.feign.auth; - -import java.io.UnsupportedEncodingException; - -/** - * copied from okhttp - * - * @author Alexander Y. Kleymenov - */ -final class Base64 { - - public static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; - private static final byte[] MAP = new byte[] { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', - 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', - 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', - '5', '6', '7', '8', '9', '+', '/' - }; - - private Base64() {} - - public static byte[] decode(byte[] in) { - return decode(in, in.length); - } - - public static byte[] decode(byte[] in, int len) { - // approximate output length - int length = len / 4 * 3; - // return an empty array on empty or short input without padding - if (length == 0) { - return EMPTY_BYTE_ARRAY; - } - // temporary array - byte[] out = new byte[length]; - // number of padding characters ('=') - int pad = 0; - byte chr; - // compute the number of the padding characters - // and adjust the length of the input - for (; ; len--) { - chr = in[len - 1]; - // skip the neutral characters - if ((chr == '\n') || (chr == '\r') || (chr == ' ') || (chr == '\t')) { - continue; - } - if (chr == '=') { - pad++; - } else { - break; - } - } - // index in the output array - int outIndex = 0; - // index in the input array - int inIndex = 0; - // holds the value of the input character - int bits = 0; - // holds the value of the input quantum - int quantum = 0; - for (int i = 0; i < len; i++) { - chr = in[i]; - // skip the neutral characters - if ((chr == '\n') || (chr == '\r') || (chr == ' ') || (chr == '\t')) { - continue; - } - if ((chr >= 'A') && (chr <= 'Z')) { - // char ASCII value - // A 65 0 - // Z 90 25 (ASCII - 65) - bits = chr - 65; - } else if ((chr >= 'a') && (chr <= 'z')) { - // char ASCII value - // a 97 26 - // z 122 51 (ASCII - 71) - bits = chr - 71; - } else if ((chr >= '0') && (chr <= '9')) { - // char ASCII value - // 0 48 52 - // 9 57 61 (ASCII + 4) - bits = chr + 4; - } else if (chr == '+') { - bits = 62; - } else if (chr == '/') { - bits = 63; - } else { - return null; - } - // append the value to the quantum - quantum = (quantum << 6) | (byte) bits; - if (inIndex % 4 == 3) { - // 4 characters were read, so make the output: - out[outIndex++] = (byte) (quantum >> 16); - out[outIndex++] = (byte) (quantum >> 8); - out[outIndex++] = (byte) quantum; - } - inIndex++; - } - if (pad > 0) { - // adjust the quantum value according to the padding - quantum = quantum << (6 * pad); - // make output - out[outIndex++] = (byte) (quantum >> 16); - if (pad == 1) { - out[outIndex++] = (byte) (quantum >> 8); - } - } - // create the resulting array - byte[] result = new byte[outIndex]; - System.arraycopy(out, 0, result, 0, outIndex); - return result; - } - - public static String encode(byte[] in) { - int length = (in.length + 2) * 4 / 3; - byte[] out = new byte[length]; - int index = 0, end = in.length - in.length % 3; - for (int i = 0; i < end; i += 3) { - out[index++] = MAP[(in[i] & 0xff) >> 2]; - out[index++] = MAP[((in[i] & 0x03) << 4) | ((in[i + 1] & 0xff) >> 4)]; - out[index++] = MAP[((in[i + 1] & 0x0f) << 2) | ((in[i + 2] & 0xff) >> 6)]; - out[index++] = MAP[(in[i + 2] & 0x3f)]; - } - switch (in.length % 3) { - case 1: - out[index++] = MAP[(in[end] & 0xff) >> 2]; - out[index++] = MAP[(in[end] & 0x03) << 4]; - out[index++] = '='; - out[index++] = '='; - break; - case 2: - out[index++] = MAP[(in[end] & 0xff) >> 2]; - out[index++] = MAP[((in[end] & 0x03) << 4) | ((in[end + 1] & 0xff) >> 4)]; - out[index++] = MAP[((in[end + 1] & 0x0f) << 2)]; - out[index++] = '='; - break; - } - try { - return new String(out, 0, index, "US-ASCII"); - } catch (UnsupportedEncodingException e) { - throw new AssertionError(e); - } - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/auth/BasicAuthRequestInterceptor.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/auth/BasicAuthRequestInterceptor.java deleted file mode 100644 index 2fa8c25ca..000000000 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/auth/BasicAuthRequestInterceptor.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.palantir.conjure.java.client.jaxrs.feign.auth; - -import static com.palantir.conjure.java.client.jaxrs.feign.Util.ISO_8859_1; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; - -import com.palantir.conjure.java.client.jaxrs.feign.RequestInterceptor; -import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; -import java.nio.charset.Charset; - -/** - * An interceptor that adds the request header needed to use HTTP basic authentication. - */ -public class BasicAuthRequestInterceptor implements RequestInterceptor { - - private final String headerValue; - - /** - * Creates an interceptor that authenticates all requests with the specified username and password - * encoded using ISO-8859-1. - * - * @param username the username to use for authentication - * @param password the password to use for authentication - */ - public BasicAuthRequestInterceptor(String username, String password) { - this(username, password, ISO_8859_1); - } - - /** - * Creates an interceptor that authenticates all requests with the specified username and password - * encoded using the specified charset. - * - * @param username the username to use for authentication - * @param password the password to use for authentication - * @param charset the charset to use when encoding the credentials - */ - public BasicAuthRequestInterceptor(String username, String password, Charset charset) { - checkNotNull(username, "username"); - checkNotNull(password, "password"); - this.headerValue = "Basic " + base64Encode((username + ":" + password).getBytes(charset)); - } - - /* - * This uses a Sun internal method; if we ever encounter a case where this method is not available, the appropriate - * response would be to pull the necessary portions of Guava's BaseEncoding class into Util. - */ - private static String base64Encode(byte[] bytes) { - return Base64.encode(bytes); - } - - @Override - public void apply(RequestTemplate template) { - template.header("Authorization", headerValue); - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/DecodeException.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/DecodeException.java deleted file mode 100644 index a2addcdd0..000000000 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/DecodeException.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.palantir.conjure.java.client.jaxrs.feign.codec; - -import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; - -import com.palantir.conjure.java.client.jaxrs.feign.FeignException; - -/** - * Similar to {@code javax.websocket.DecodeException}, raised when a problem occurs decoding a - * message. Note that {@code DecodeException} is not an {@code IOException}, nor does it have one - * set as its cause. - */ -public class DecodeException extends FeignException { - - private static final long serialVersionUID = 1L; - - /** - * @param message the reason for the failure. - */ - public DecodeException(String message) { - super(checkNotNull(message, "message")); - } - - /** - * @param message possibly null reason for the failure. - * @param cause the cause of the error. - */ - public DecodeException(String message, Throwable cause) { - super(message, checkNotNull(cause, "cause")); - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/Decoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/Decoder.java index ca7480931..a49100dd3 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/Decoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/Decoder.java @@ -16,10 +16,8 @@ package com.palantir.conjure.java.client.jaxrs.feign.codec; import com.palantir.conjure.java.client.jaxrs.feign.Feign; -import com.palantir.conjure.java.client.jaxrs.feign.FeignException; import com.palantir.conjure.java.client.jaxrs.feign.Response; import com.palantir.conjure.java.client.jaxrs.feign.Target; -import com.palantir.conjure.java.client.jaxrs.feign.Util; import java.io.IOException; import java.lang.reflect.Type; @@ -50,9 +48,6 @@ * Target#type() interface} processed by {@link Feign#newInstance(Target)}. When * writing your implementation of Decoder, ensure you also test parameterized types such as {@code * List}. - *

Note on exception propagation

Exceptions thrown by {@link Decoder}s get wrapped in - * a {@link DecodeException} unless they are a subclass of {@link FeignException} already, and unless - * the client was configured with {@link Feign.Builder#decode404()}. */ public interface Decoder { @@ -66,22 +61,6 @@ public interface Decoder { * the method corresponding to this {@code response}. * @return instance of {@code type} * @throws IOException will be propagated safely to the caller. - * @throws DecodeException when decoding failed due to a checked exception besides IOException. - * @throws FeignException when decoding succeeds, but conveys the operation failed. */ - Object decode(Response response, Type type) throws IOException, DecodeException, FeignException; - - /** Default implementation of {@code Decoder}. */ - public class Default extends StringDecoder { - - @Override - public Object decode(Response response, Type type) throws IOException { - if (response.status() == 404) return Util.emptyValueOf(type); - if (response.body() == null) return null; - if (byte[].class.equals(type)) { - return Util.toByteArray(response.body().asInputStream()); - } - return super.decode(response, type); - } - } + Object decode(Response response, Type type) throws IOException; } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/EncodeException.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/EncodeException.java deleted file mode 100644 index 9f8f6e395..000000000 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/EncodeException.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.palantir.conjure.java.client.jaxrs.feign.codec; - -import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; - -import com.palantir.conjure.java.client.jaxrs.feign.FeignException; - -/** - * Similar to {@code javax.websocket.EncodeException}, raised when a problem occurs encoding a - * message. Note that {@code EncodeException} is not an {@code IOException}, nor does it have one - * set as its cause. - */ -public class EncodeException extends FeignException { - - private static final long serialVersionUID = 1L; - - /** - * @param message the reason for the failure. - */ - public EncodeException(String message) { - super(checkNotNull(message, "message")); - } - - /** - * @param message possibly null reason for the failure. - * @param cause the cause of the error. - */ - public EncodeException(String message, Throwable cause) { - super(message, checkNotNull(cause, "cause")); - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/Encoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/Encoder.java index a75a23646..d5eb5da18 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/Encoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/Encoder.java @@ -15,8 +15,6 @@ */ package com.palantir.conjure.java.client.jaxrs.feign.codec; -import static java.lang.String.format; - import com.palantir.conjure.java.client.jaxrs.feign.MethodMetadata; import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; import com.palantir.conjure.java.client.jaxrs.feign.Util; @@ -70,24 +68,6 @@ public interface Encoder { * @param bodyType the type the object should be encoded as. {@link #MAP_STRING_WILDCARD} * indicates form encoding. * @param template the request template to populate. - * @throws EncodeException when encoding failed due to a checked exception. */ - void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException; - - /** - * Default implementation of {@code Encoder}. - */ - class Default implements Encoder { - - @Override - public void encode(Object object, Type bodyType, RequestTemplate template) { - if (bodyType == String.class) { - template.body(object.toString()); - } else if (bodyType == byte[].class) { - template.body((byte[]) object, null); - } else if (object != null) { - throw new EncodeException(format("%s is not a type supported by this encoder.", object.getClass())); - } - } - } + void encode(Object object, Type bodyType, RequestTemplate template); } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/ErrorDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/ErrorDecoder.java index 123502544..a2942ed41 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/ErrorDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/ErrorDecoder.java @@ -15,23 +15,8 @@ */ package com.palantir.conjure.java.client.jaxrs.feign.codec; -import static com.palantir.conjure.java.client.jaxrs.feign.FeignException.errorStatus; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.RETRY_AFTER; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; -import static java.util.Locale.US; -import static java.util.concurrent.TimeUnit.SECONDS; - import com.palantir.conjure.java.client.jaxrs.feign.Feign; -import com.palantir.conjure.java.client.jaxrs.feign.FeignException; import com.palantir.conjure.java.client.jaxrs.feign.Response; -import com.palantir.conjure.java.client.jaxrs.feign.RetryableException; -import com.palantir.conjure.java.client.jaxrs.feign.Util; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Collection; -import java.util.Date; -import java.util.Map; /** * Allows you to massage an exception into a application-specific one. Converting out to a throttle @@ -57,95 +42,21 @@ * range are classified as errors, addressed by the {@link ErrorDecoder}. That said, certain RPC * apis return errors defined in the {@link Response#body()} even on a 200 status. For example, in * the DynECT api, a job still running condition is returned with a 200 status, encoded in json. - * When scenarios like this occur, you should raise an application-specific exception (which may be - * {@link RetryableException retryable}). - * - *

Not Found Semantics - *

It is commonly the case that 404 (Not Found) status has semantic value in HTTP apis. While - * the default behavior is to raise exeception, users can alternatively enable 404 processing via - * {@link Feign.Builder#decode404()}. + * When scenarios like this occur, you should raise an application-specific exception. */ public interface ErrorDecoder { /** * Implement this method in order to decode an HTTP {@link Response} when {@link * Response#status()} is not in the 2xx range. Please raise application-specific exceptions where - * possible. If your exception is retryable, wrap or subclass {@link RetryableException} + * possible. * * @param methodKey {@link Feign#configKey} of the java method that invoked the request. * ex. {@code IAM#getUser()} * @param response HTTP response where {@link Response#status() status} is greater than or equal * to {@code 300}. * @return Exception IOException, if there was a network error reading the response or an - * application-specific exception decoded by the implementation. If the throwable is retryable, it - * should be wrapped, or a subtype of {@link RetryableException} - */ - public Exception decode(String methodKey, Response response); - - public static class Default implements ErrorDecoder { - - private final RetryAfterDecoder retryAfterDecoder = new RetryAfterDecoder(); - - @Override - public Exception decode(String methodKey, Response response) { - FeignException exception = errorStatus(methodKey, response); - Date retryAfter = retryAfterDecoder.apply(firstOrNull(response.headers(), RETRY_AFTER)); - if (retryAfter != null) { - return new RetryableException(exception.getMessage(), exception, retryAfter); - } - return exception; - } - - private T firstOrNull(Map> map, String key) { - if (map.containsKey(key) && !map.get(key).isEmpty()) { - return map.get(key).iterator().next(); - } - return null; - } - } - - /** - * Decodes a {@link Util#RETRY_AFTER} header into an absolute date, if possible.
See Retry-After format + * application-specific exception decoded by the implementation. */ - static class RetryAfterDecoder { - - static final DateFormat RFC822_FORMAT = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", US); - private final DateFormat rfc822Format; - - RetryAfterDecoder() { - this(RFC822_FORMAT); - } - - RetryAfterDecoder(DateFormat rfc822Format) { - this.rfc822Format = checkNotNull(rfc822Format, "rfc822Format"); - } - - protected long currentTimeMillis() { - return System.currentTimeMillis(); - } - - /** - * returns a date that corresponds to the first time a request can be retried. - * - * @param retryAfter String in Retry-After format - */ - public Date apply(String retryAfter) { - if (retryAfter == null) { - return null; - } - if (retryAfter.matches("^[0-9]+$")) { - long deltaMillis = SECONDS.toMillis(Long.parseLong(retryAfter)); - return new Date(currentTimeMillis() + deltaMillis); - } - synchronized (rfc822Format) { - try { - return rfc822Format.parse(retryAfter); - } catch (ParseException ignored) { - return null; - } - } - } - } + Exception decode(String methodKey, Response response); } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/StringDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/StringDecoder.java deleted file mode 100644 index e2c936564..000000000 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/StringDecoder.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.palantir.conjure.java.client.jaxrs.feign.codec; - -import static java.lang.String.format; - -import com.palantir.conjure.java.client.jaxrs.feign.Response; -import com.palantir.conjure.java.client.jaxrs.feign.Util; -import java.io.IOException; -import java.lang.reflect.Type; - -public class StringDecoder implements Decoder { - - @Override - public Object decode(Response response, Type type) throws IOException { - Response.Body body = response.body(); - if (body == null) { - return null; - } - if (String.class.equals(type)) { - return Util.toString(body.asReader()); - } - throw new DecodeException(format("%s is not a type supported by this decoder.", type)); - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/jackson/JacksonEncoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/jackson/JacksonEncoder.java deleted file mode 100644 index 1ee3529c6..000000000 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/jackson/JacksonEncoder.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.palantir.conjure.java.client.jaxrs.feign.jackson; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.Module; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; -import com.palantir.conjure.java.client.jaxrs.feign.codec.EncodeException; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; -import java.lang.reflect.Type; -import java.util.Collections; - -public class JacksonEncoder implements Encoder { - - private final ObjectMapper mapper; - - public JacksonEncoder() { - this(Collections.emptyList()); - } - - public JacksonEncoder(Iterable modules) { - this(new ObjectMapper() - .setSerializationInclusion(JsonInclude.Include.NON_NULL) - .configure(SerializationFeature.INDENT_OUTPUT, true) - .registerModules(modules)); - } - - public JacksonEncoder(ObjectMapper mapper) { - this.mapper = mapper; - } - - @Override - public void encode(Object object, Type bodyType, RequestTemplate template) { - try { - JavaType javaType = mapper.getTypeFactory().constructType(bodyType); - template.body(mapper.writerFor(javaType).writeValueAsString(object)); - } catch (JsonProcessingException e) { - throw new EncodeException(e.getMessage(), e); - } - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/AbstractDelegatingContract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/AbstractDelegatingContract.java index 91066fad6..7daef3456 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/AbstractDelegatingContract.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/AbstractDelegatingContract.java @@ -26,7 +26,7 @@ /** * Base class that provides the structure for a delegating {@link Contract}. Delegates the initial - * {@link #parseAndValidatateMetadata(Class)} call to the wrapped Contract and then calls {@link #processMetadata(Class, + * {@link #parseAndValidateMetadata(Class)} call to the wrapped Contract and then calls {@link #processMetadata(Class, * Method, MethodMetadata)} on all of the methods that have metadata from the initial call. */ abstract class AbstractDelegatingContract implements Contract { @@ -38,8 +38,8 @@ abstract class AbstractDelegatingContract implements Contract { } @Override - public final List parseAndValidatateMetadata(Class targetType) { - List mdList = delegate.parseAndValidatateMetadata(targetType); + public final List parseAndValidateMetadata(Class targetType) { + List mdList = delegate.parseAndValidateMetadata(targetType); Map methodMetadataByConfigKey = new LinkedHashMap(); for (MethodMetadata md : mdList) { diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CborDelegateDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CborDelegateDecoder.java index 6afd493d1..25803ca0d 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CborDelegateDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CborDelegateDecoder.java @@ -20,7 +20,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.net.HttpHeaders; -import com.palantir.conjure.java.client.jaxrs.feign.FeignException; import com.palantir.conjure.java.client.jaxrs.feign.Response; import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import java.io.IOException; @@ -48,7 +47,7 @@ public CborDelegateDecoder(ObjectMapper cborMapper, Decoder delegate) { } @Override - public Object decode(Response response, Type type) throws IOException, FeignException { + public Object decode(Response response, Type type) throws IOException { Collection contentTypes = HeaderAccessUtils.caseInsensitiveGet(response.headers(), HttpHeaders.CONTENT_TYPE); if (contentTypes == null) { diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CborDelegateEncoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CborDelegateEncoder.java index bbcf7f3bb..8cf78fe36 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CborDelegateEncoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CborDelegateEncoder.java @@ -22,11 +22,9 @@ import com.google.common.collect.ImmutableSet; import com.google.common.net.HttpHeaders; import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; -import com.palantir.conjure.java.client.jaxrs.feign.codec.EncodeException; import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; import java.io.UncheckedIOException; import java.lang.reflect.Type; -import java.nio.charset.StandardCharsets; import java.util.Collection; /** @@ -51,7 +49,7 @@ public CborDelegateEncoder(ObjectMapper cborMapper, Encoder delegate) { } @Override - public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException { + public void encode(Object object, Type bodyType, RequestTemplate template) { Collection contentTypes = HeaderAccessUtils.caseInsensitiveGet(template.headers(), HttpHeaders.CONTENT_TYPE); if (contentTypes == null) { @@ -65,7 +63,7 @@ public void encode(Object object, Type bodyType, RequestTemplate template) throw try { JavaType javaType = cborMapper.getTypeFactory().constructType(bodyType); - template.body(cborMapper.writerFor(javaType).writeValueAsBytes(object), StandardCharsets.UTF_8); + template.body(cborMapper.writerFor(javaType).writeValueAsBytes(object)); } catch (JsonProcessingException e) { throw new UncheckedIOException(e); } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaEmptyOptionalExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaEmptyOptionalExpander.java index f305f0482..b05976cc8 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaEmptyOptionalExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaEmptyOptionalExpander.java @@ -17,7 +17,7 @@ package com.palantir.conjure.java.client.jaxrs.feignimpl; import com.google.common.base.Preconditions; -import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Expander; import java.util.Objects; /** diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaNullOptionalExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaNullOptionalExpander.java index c7219bfeb..c7fff6ab7 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaNullOptionalExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaNullOptionalExpander.java @@ -17,7 +17,7 @@ package com.palantir.conjure.java.client.jaxrs.feignimpl; import com.google.common.base.Preconditions; -import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Expander; import java.util.Objects; /** diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalAwareDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalAwareDecoder.java index d6116c2f3..1cde96a9d 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalAwareDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalAwareDecoder.java @@ -18,7 +18,6 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.palantir.conjure.java.client.jaxrs.feign.FeignException; import com.palantir.conjure.java.client.jaxrs.feign.Response; import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import java.io.IOException; @@ -38,7 +37,7 @@ public GuavaOptionalAwareDecoder(Decoder delegate) { } @Override - public Object decode(Response response, Type type) throws IOException, FeignException { + public Object decode(Response response, Type type) throws IOException { if (RawTypes.get(type).equals(com.google.common.base.Optional.class)) { if (response.status() == 204) { return com.google.common.base.Optional.absent(); diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateDecoder.java index d93daa51b..e188443ec 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateDecoder.java @@ -17,7 +17,6 @@ package com.palantir.conjure.java.client.jaxrs.feignimpl; import com.codahale.metrics.Meter; -import com.palantir.conjure.java.client.jaxrs.feign.FeignException; import com.palantir.conjure.java.client.jaxrs.feign.Response; import com.palantir.conjure.java.client.jaxrs.feign.Util; import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; @@ -49,7 +48,7 @@ public InputStreamDelegateDecoder(@Safe String clientNameForLogging, Decoder del } @Override - public Object decode(Response response, Type type) throws IOException, FeignException { + public Object decode(Response response, Type type) throws IOException { if (type.equals(InputStream.class)) { byte[] body = response.body() != null ? Util.toByteArray(response.body().asInputStream()) : new byte[0]; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateEncoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateEncoder.java index 60d21adca..63fa37555 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateEncoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateEncoder.java @@ -19,7 +19,6 @@ import com.codahale.metrics.Meter; import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; import com.palantir.conjure.java.client.jaxrs.feign.Util; -import com.palantir.conjure.java.client.jaxrs.feign.codec.EncodeException; import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; import com.palantir.conjure.java.client.jaxrs.feignimpl.FeignClientMetrics.DangerousBuffering_Direction; import com.palantir.logsafe.Safe; @@ -28,7 +27,6 @@ import java.io.InputStream; import java.io.UncheckedIOException; import java.lang.reflect.Type; -import java.nio.charset.StandardCharsets; /** If the body type is an InputStream, write it into the body, otherwise pass to delegate. */ public final class InputStreamDelegateEncoder implements Encoder { @@ -50,12 +48,12 @@ public InputStreamDelegateEncoder(@Safe String clientNameForLogging, Encoder del } @Override - public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException { + public void encode(Object object, Type bodyType, RequestTemplate template) { if (bodyType.equals(InputStream.class)) { try { byte[] bytes = Util.toByteArray((InputStream) object); dangerousBufferingMeter.mark(Math.max(1, bytes.length)); - template.body(bytes, StandardCharsets.UTF_8); + template.body(bytes); } catch (IOException e) { throw new UncheckedIOException(e); } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/jackson/JacksonDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/JacksonDecoder.java similarity index 50% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/jackson/JacksonDecoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/JacksonDecoder.java index 4c3a1e91c..5c92b2655 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/jackson/JacksonDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/JacksonDecoder.java @@ -13,12 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feign.jackson; +package com.palantir.conjure.java.client.jaxrs.feignimpl; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.RuntimeJsonMappingException; import com.palantir.conjure.java.client.jaxrs.feign.Response; import com.palantir.conjure.java.client.jaxrs.feign.Util; import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; @@ -26,47 +23,34 @@ import java.io.IOException; import java.io.Reader; import java.lang.reflect.Type; -import java.util.Collections; -public class JacksonDecoder implements Decoder { +public final class JacksonDecoder implements Decoder { private final ObjectMapper mapper; - public JacksonDecoder() { - this(Collections.emptyList()); - } - - public JacksonDecoder(Iterable modules) { - this(new ObjectMapper() - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - .registerModules(modules)); - } - public JacksonDecoder(ObjectMapper mapper) { this.mapper = mapper; } @Override public Object decode(Response response, Type type) throws IOException { - if (response.status() == 404) return Util.emptyValueOf(type); - if (response.body() == null) return null; + if (response.status() == 404) { + return Util.emptyValueOf(type); + } + if (response.body() == null) { + return null; + } Reader reader = response.body().asReader(); if (!reader.markSupported()) { reader = new BufferedReader(reader, 1); } - try { - // Read the first byte to see if we have any data - reader.mark(1); - if (reader.read() == -1) { - return null; // Eagerly returning null avoids "No content to map due to end-of-input" - } - reader.reset(); - return mapper.readValue(reader, mapper.constructType(type)); - } catch (RuntimeJsonMappingException e) { - if (e.getCause() != null && e.getCause() instanceof IOException) { - throw IOException.class.cast(e.getCause()); - } - throw e; + + // Read the first byte to see if we have any data + reader.mark(1); + if (reader.read() == -1) { + return null; // Eagerly returning null avoids "No content to map due to end-of-input" } + reader.reset(); + return mapper.readValue(reader, mapper.constructType(type)); } } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/ConjureFeignJacksonEncoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/JacksonEncoder.java similarity index 64% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/ConjureFeignJacksonEncoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/JacksonEncoder.java index b4e9ac6fa..a8c787bdd 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/ConjureFeignJacksonEncoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/JacksonEncoder.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,27 +14,21 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs; +package com.palantir.conjure.java.client.jaxrs.feignimpl; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; -import com.palantir.conjure.java.client.jaxrs.feign.codec.EncodeException; import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; -import com.palantir.conjure.java.client.jaxrs.feign.jackson.JacksonEncoder; +import java.io.UncheckedIOException; import java.lang.reflect.Type; -import java.nio.charset.StandardCharsets; -/** - * Similar to {@link JacksonEncoder}, but optimized to avoid intermediate String representation of request - * body. See upstream PR https://github.com/OpenFeign/feign/pull/989 . - */ -final class ConjureFeignJacksonEncoder implements Encoder { +public final class JacksonEncoder implements Encoder { private final ObjectMapper mapper; - ConjureFeignJacksonEncoder(ObjectMapper mapper) { + public JacksonEncoder(ObjectMapper mapper) { this.mapper = mapper; } @@ -42,9 +36,9 @@ final class ConjureFeignJacksonEncoder implements Encoder { public void encode(Object object, Type bodyType, RequestTemplate template) { try { JavaType javaType = mapper.getTypeFactory().constructType(bodyType); - template.body(mapper.writerFor(javaType).writeValueAsBytes(object), StandardCharsets.UTF_8); + template.body(mapper.writerFor(javaType).writeValueAsBytes(object)); } catch (JsonProcessingException e) { - throw new EncodeException(e.getMessage(), e); + throw new UncheckedIOException(e); } } } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalDoubleExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalDoubleExpander.java index df2bce964..17697c93f 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalDoubleExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalDoubleExpander.java @@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument; -import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Expander; import java.util.OptionalDouble; /** diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalExpander.java index c845cd272..496d62dd8 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalExpander.java @@ -17,7 +17,7 @@ package com.palantir.conjure.java.client.jaxrs.feignimpl; import com.google.common.base.Preconditions; -import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Expander; import java.util.Objects; import java.util.Optional; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalIntExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalIntExpander.java index 9a1b239b9..74faea4bb 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalIntExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalIntExpander.java @@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument; -import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Expander; import java.util.OptionalInt; /** diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalLongExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalLongExpander.java index b0fafc1dc..f2f370859 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalLongExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalLongExpander.java @@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument; -import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Expander; import java.util.OptionalLong; /** diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalDoubleExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalDoubleExpander.java index 5a8bdf576..a575a1437 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalDoubleExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalDoubleExpander.java @@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument; -import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Expander; import java.util.OptionalDouble; /** diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalExpander.java index 149709008..8d2d73b66 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalExpander.java @@ -17,7 +17,7 @@ package com.palantir.conjure.java.client.jaxrs.feignimpl; import com.google.common.base.Preconditions; -import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Expander; import java.util.Objects; import java.util.Optional; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalIntExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalIntExpander.java index bd40f6c86..bca6496db 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalIntExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalIntExpander.java @@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument; -import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Expander; import java.util.OptionalInt; /** diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalLongExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalLongExpander.java index e2fff40c5..4d8d9b961 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalLongExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalLongExpander.java @@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument; -import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; +import com.palantir.conjure.java.client.jaxrs.feign.Expander; import java.util.OptionalLong; /** diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareContract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareContract.java index 247fe3dfc..ce05552cf 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareContract.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareContract.java @@ -19,8 +19,8 @@ import com.google.common.collect.ImmutableList; import com.palantir.conjure.java.client.jaxrs.JaxRsJakartaCompatibility.Annotations; import com.palantir.conjure.java.client.jaxrs.feign.Contract; +import com.palantir.conjure.java.client.jaxrs.feign.Expander; import com.palantir.conjure.java.client.jaxrs.feign.MethodMetadata; -import com.palantir.conjure.java.client.jaxrs.feign.Param.Expander; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Arrays; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareDecoder.java index 38bbabd16..45876f7c1 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareDecoder.java @@ -18,7 +18,6 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.palantir.conjure.java.client.jaxrs.feign.FeignException; import com.palantir.conjure.java.client.jaxrs.feign.Response; import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import java.io.IOException; @@ -39,7 +38,7 @@ public Java8OptionalAwareDecoder(Decoder delegate) { } @Override - public Object decode(Response response, Type type) throws IOException, FeignException { + public Object decode(Response response, Type type) throws IOException { if (RawTypes.get(type).equals(Optional.class)) { if (response.status() == 204) { return Optional.empty(); diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverReturnNullDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverReturnNullDecoder.java index 2d6dd7abc..52285d538 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverReturnNullDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverReturnNullDecoder.java @@ -16,7 +16,6 @@ package com.palantir.conjure.java.client.jaxrs.feignimpl; -import com.palantir.conjure.java.client.jaxrs.feign.FeignException; import com.palantir.conjure.java.client.jaxrs.feign.Response; import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import com.palantir.logsafe.Preconditions; @@ -32,7 +31,7 @@ public NeverReturnNullDecoder(Decoder delegate) { } @Override - public Object decode(Response response, Type type) throws FeignException, IOException { + public Object decode(Response response, Type type) throws IOException { Object object = delegate.decode(response, type); Preconditions.checkNotNull(object, "Unexpected null body", SafeArg.of("status", response.status())); diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateDecoder.java index aefdc05dc..6e4c14c24 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateDecoder.java @@ -19,20 +19,20 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.net.HttpHeaders; -import com.palantir.conjure.java.client.jaxrs.feign.FeignException; import com.palantir.conjure.java.client.jaxrs.feign.Response; +import com.palantir.conjure.java.client.jaxrs.feign.Util; import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; -import com.palantir.conjure.java.client.jaxrs.feign.codec.StringDecoder; +import com.palantir.logsafe.SafeArg; +import com.palantir.logsafe.exceptions.SafeRuntimeException; import java.io.IOException; import java.lang.reflect.Type; import java.util.Collection; /** - * Delegates to a {@link StringDecoder} if the response has a Content-Type of text/plain, or falls back to the given + * Decodes the response as a string if the response has a Content-Type of text/plain, or falls back to the given * delegate otherwise. */ public final class TextDelegateDecoder implements Decoder { - private static final Decoder stringDecoder = new StringDecoder(); private final Decoder delegate; @@ -41,7 +41,7 @@ public TextDelegateDecoder(Decoder delegate) { } @Override - public Object decode(Response response, Type type) throws IOException, FeignException { + public Object decode(Response response, Type type) throws IOException { Collection contentTypes = HeaderAccessUtils.caseInsensitiveGet(response.headers(), HttpHeaders.CONTENT_TYPE); if (contentTypes == null) { @@ -50,11 +50,14 @@ public Object decode(Response response, Type type) throws IOException, FeignExce // In the case of multiple content types, or an unknown content type, we'll use the delegate instead. if (contentTypes.size() == 1 && Iterables.getOnlyElement(contentTypes, "").startsWith("text/plain")) { - Object decoded = stringDecoder.decode(response, type); - if (decoded == null) { + Response.Body body = response.body(); + if (body == null) { return ""; } - return decoded; + if (String.class.equals(type)) { + return Util.toString(body.asReader()); + } + throw new SafeRuntimeException("Type is not supported by this encoder", SafeArg.of("type", type)); } return delegate.decode(response, type); diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateEncoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateEncoder.java index 80db73d80..56d7df42f 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateEncoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateEncoder.java @@ -20,17 +20,18 @@ import com.google.common.collect.Iterables; import com.google.common.net.HttpHeaders; import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; -import com.palantir.conjure.java.client.jaxrs.feign.codec.EncodeException; import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; +import com.palantir.logsafe.SafeArg; +import com.palantir.logsafe.exceptions.SafeRuntimeException; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import java.util.Collection; /** - * Delegates to a {@link Encoder.Default} if the response has a Content-Type of text/plain, or falls back to - * the given delegate otherwise. + * Encodes the value as a string if the request has a Content-Type of text/plain, or falls back to the given delegate + * otherwise. */ public final class TextDelegateEncoder implements Encoder { - private static final Encoder defaultEncoder = new Encoder.Default(); private final Encoder delegate; @@ -39,7 +40,7 @@ public TextDelegateEncoder(Encoder delegate) { } @Override - public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException { + public void encode(Object object, Type bodyType, RequestTemplate template) { Collection contentTypes = HeaderAccessUtils.caseInsensitiveGet(template.headers(), HttpHeaders.CONTENT_TYPE); if (contentTypes == null) { @@ -49,7 +50,14 @@ public void encode(Object object, Type bodyType, RequestTemplate template) throw // In the case of multiple content types, or an unknown content type, we'll use the delegate instead. if (contentTypes.size() == 1 && Iterables.getOnlyElement(contentTypes, "").equals("text/plain")) { - defaultEncoder.encode(object, bodyType, template); + if (bodyType == String.class) { + template.body(object.toString().getBytes(StandardCharsets.UTF_8)); + } else if (bodyType == byte[].class) { + template.body((byte[]) object); + } else if (object != null) { + throw new SafeRuntimeException( + "Type is not supported by this encoder", SafeArg.of("type", object.getClass())); + } } else { delegate.encode(object, bodyType, template); } diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverReturnNullDecoderTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverReturnNullDecoderTest.java index b8f76c5a2..7f647d306 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverReturnNullDecoderTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverReturnNullDecoderTest.java @@ -22,7 +22,6 @@ import com.palantir.conjure.java.client.jaxrs.TestBase; import com.palantir.conjure.java.client.jaxrs.feign.Response; import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; -import com.palantir.conjure.java.client.jaxrs.feign.jackson.JacksonDecoder; import com.palantir.conjure.java.serialization.ObjectMappers; import com.palantir.logsafe.SafeArg; import com.palantir.logsafe.testing.Assertions; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/QosErrorDecoderTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/QosErrorDecoderTest.java index 234f49bd6..66ee00a73 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/QosErrorDecoderTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/QosErrorDecoderTest.java @@ -29,7 +29,6 @@ import com.palantir.conjure.java.api.errors.QosReason.RetryHint; import com.palantir.conjure.java.api.errors.QosReasons; import com.palantir.conjure.java.client.jaxrs.feign.Response; -import com.palantir.conjure.java.client.jaxrs.feign.codec.ErrorDecoder; import jakarta.ws.rs.core.HttpHeaders; import java.time.Duration; import java.util.Collection; @@ -41,7 +40,7 @@ public final class QosErrorDecoderTest { private static final String methodKey = "method"; static QosErrorDecoder decoder() { - return new QosErrorDecoder(new ErrorDecoder.Default()); + return new QosErrorDecoder(null); } @Test diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateDecoderTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateDecoderTest.java index a381506da..88606db25 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateDecoderTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateDecoderTest.java @@ -28,9 +28,7 @@ import com.google.common.net.HttpHeaders; import com.palantir.conjure.java.client.jaxrs.JaxRsClient; import com.palantir.conjure.java.client.jaxrs.TestBase; -import com.palantir.conjure.java.client.jaxrs.feign.FeignException; import com.palantir.conjure.java.client.jaxrs.feign.Response; -import com.palantir.conjure.java.client.jaxrs.feign.codec.DecodeException; import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import com.palantir.conjure.java.okhttp.HostMetricsRegistry; import com.palantir.undertest.UndertowServerExtension; @@ -78,7 +76,8 @@ public void testUsesStringDecoderWithTextPlain() throws Exception { @Test public void testCannotReturnStringWithMediaTypeJson() { assertThatThrownBy(() -> service.getJsonString("foo")) - .isInstanceOf(FeignException.class) + .isInstanceOf(RuntimeException.class) + .cause() .hasMessageStartingWith("Unrecognized token 'foo': was expecting " + "(JSON String, Number, Array, Object or token 'null', 'true' or 'false')"); } @@ -146,8 +145,6 @@ public void testUsesDelegateWithNonTextContentType() throws Exception { @Test public void testStandardClientsUseTextDelegateEncoder() { assertThat(service.getString("string")).isEqualTo("string"); - assertThatExceptionOfType(DecodeException.class) - .isThrownBy(() -> service.getString(null)) - .withCauseInstanceOf(NullPointerException.class); + assertThatExceptionOfType(NullPointerException.class).isThrownBy(() -> service.getString(null)); } } From 575c84d5cb5fe1786ac36720e59c7c08918b8625 Mon Sep 17 00:00:00 2001 From: Patrick Koenig Date: Fri, 27 Mar 2026 01:08:38 -0400 Subject: [PATCH 4/6] Make internal things package-private while we're breaking the ABI --- .palantir/revapi.yml | 213 ++++++++++++++++++ .../AbstractDelegatingContract.java | 9 +- .../AbstractFeignJaxRsClientBuilder.java | 28 +-- .../{feignimpl => }/CborDelegateDecoder.java | 10 +- .../{feignimpl => }/CborDelegateEncoder.java | 10 +- .../java/client/jaxrs/{feign => }/Client.java | 4 +- .../client/jaxrs/CompatibleJaxRsContract.java | 10 +- .../client/jaxrs/{feign => }/Contract.java | 18 +- .../jaxrs/{feign/codec => }/Decoder.java | 7 +- .../{feign => }/DefaultMethodHandler.java | 6 +- .../client/jaxrs/DialogueFeignClient.java | 24 +- .../EmptyContainerDecoder.java | 10 +- .../jaxrs/{feign/codec => }/Encoder.java | 7 +- .../EndpointNameHeaderEnrichmentContract.java | 14 +- .../jaxrs/{feign/codec => }/ErrorDecoder.java | 7 +- .../client/jaxrs/{feign => }/Expander.java | 4 +- .../java/client/jaxrs/{feign => }/Feign.java | 29 ++- .../client/jaxrs/FeignJaxRsClientBuilder.java | 6 +- .../GuavaEmptyOptionalExpander.java | 7 +- .../GuavaNullOptionalExpander.java | 7 +- .../GuavaOptionalAwareContract.java | 10 +- .../GuavaOptionalAwareDecoder.java | 10 +- .../{feignimpl => }/HeaderAccessUtils.java | 12 +- .../InputStreamDelegateDecoder.java | 15 +- .../InputStreamDelegateEncoder.java | 15 +- .../jaxrs/{feignimpl => }/JacksonDecoder.java | 9 +- .../jaxrs/{feignimpl => }/JacksonEncoder.java | 8 +- .../Java8EmptyOptionalDoubleExpander.java | 7 +- .../Java8EmptyOptionalExpander.java | 7 +- .../Java8EmptyOptionalIntExpander.java | 7 +- .../Java8EmptyOptionalLongExpander.java | 7 +- .../Java8NullOptionalDoubleExpander.java | 7 +- .../Java8NullOptionalExpander.java | 7 +- .../Java8NullOptionalIntExpander.java | 7 +- .../Java8NullOptionalLongExpander.java | 7 +- .../Java8OptionalAwareContract.java | 13 +- .../Java8OptionalAwareDecoder.java | 10 +- .../jaxrs/JaxRsJakartaCompatibility.java | 10 +- .../jaxrs/{feign => }/MethodHandler.java | 2 +- .../MethodHeaderEnrichmentContract.java | 12 +- .../jaxrs/{feign => }/MethodMetadata.java | 48 ++-- .../NeverReturnNullDecoder.java | 10 +- .../{feignimpl => }/QosErrorDecoder.java | 10 +- .../jaxrs/{feignimpl => }/RawTypes.java | 4 +- .../jaxrs/{feign => }/ReflectiveFeign.java | 25 +- .../client/jaxrs/{feign => }/Request.java | 18 +- .../jaxrs/{feign => }/RequestTemplate.java | 71 +++--- .../client/jaxrs/{feign => }/Response.java | 35 ++- .../SlashEncodingContract.java | 12 +- .../{feign => }/SynchronousMethodHandler.java | 9 +- .../java/client/jaxrs/{feign => }/Target.java | 2 +- .../{feignimpl => }/TextDelegateDecoder.java | 11 +- .../{feignimpl => }/TextDelegateEncoder.java | 10 +- .../java/client/jaxrs/{feign => }/Types.java | 2 +- .../java/client/jaxrs/{feign => }/Util.java | 34 +-- .../jaxrs/feignimpl/BackoffStrategy.java | 26 --- .../NeverRetryingBackoffStrategy.java | 27 --- .../PathTemplateHeaderEnrichmentContract.java | 57 ----- .../src/main/metrics/dangerous-buffering.yml | 2 +- .../CollectionDefaultDecodingTest.java | 6 +- .../EmptyContainerDecoderTest.java | 6 +- .../GuavaOptionalAwareDecoderTest.java | 6 +- .../GuavaOptionalComplexType.java | 4 +- .../{feignimpl => }/GuavaTestServer.java | 4 +- .../HeaderAccessUtilsTest.java | 4 +- .../InputStreamDelegateDecoderTest.java | 8 +- .../InputStreamDelegateEncoderTest.java | 8 +- .../{feignimpl => }/Java8ComplexType.java | 4 +- .../Java8OptionalAwareDecoderTest.java | 8 +- .../{feignimpl => }/Java8TestServer.java | 4 +- .../NeverReturnNullDecoderTest.java | 7 +- .../{feignimpl => }/QosErrorDecoderTest.java | 5 +- .../SlashEncodingContractTest.java | 7 +- .../TextDelegateDecoderTest.java | 8 +- .../TextDelegateEncoderTest.java | 6 +- .../{feignimpl => }/TextEncoderTest.java | 7 +- .../jaxrs/{feignimpl => }/UserAgentTest.java | 8 +- .../jaxrs/FeignJaxRsScalaClientBuilder.java | 6 +- 78 files changed, 550 insertions(+), 591 deletions(-) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/AbstractDelegatingContract.java (83%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/CborDelegateDecoder.java (87%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/CborDelegateEncoder.java (85%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feign => }/Client.java (93%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feign => }/Contract.java (91%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feign/codec => }/Decoder.java (90%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feign => }/DefaultMethodHandler.java (95%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/EmptyContainerDecoder.java (94%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feign/codec => }/Encoder.java (89%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/EndpointNameHeaderEnrichmentContract.java (76%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feign/codec => }/ErrorDecoder.java (91%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feign => }/Expander.java (90%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feign => }/Feign.java (80%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/GuavaEmptyOptionalExpander.java (83%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/GuavaNullOptionalExpander.java (83%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/GuavaOptionalAwareContract.java (87%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/GuavaOptionalAwareDecoder.java (83%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/HeaderAccessUtils.java (79%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/InputStreamDelegateDecoder.java (75%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/InputStreamDelegateEncoder.java (76%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/JacksonDecoder.java (82%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/JacksonEncoder.java (81%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/Java8EmptyOptionalDoubleExpander.java (81%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/Java8EmptyOptionalExpander.java (81%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/Java8EmptyOptionalIntExpander.java (81%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/Java8EmptyOptionalLongExpander.java (81%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/Java8NullOptionalDoubleExpander.java (81%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/Java8NullOptionalExpander.java (81%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/Java8NullOptionalIntExpander.java (81%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/Java8NullOptionalLongExpander.java (81%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/Java8OptionalAwareContract.java (89%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/Java8OptionalAwareDecoder.java (83%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feign => }/MethodHandler.java (94%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/MethodHeaderEnrichmentContract.java (65%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feign => }/MethodMetadata.java (72%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/NeverReturnNullDecoder.java (74%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/QosErrorDecoder.java (79%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/RawTypes.java (88%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feign => }/ReflectiveFeign.java (92%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feign => }/Request.java (83%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feign => }/RequestTemplate.java (90%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feign => }/Response.java (87%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/SlashEncodingContract.java (62%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feign => }/SynchronousMethodHandler.java (91%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feign => }/Target.java (97%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/TextDelegateDecoder.java (83%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/TextDelegateEncoder.java (85%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feign => }/Types.java (99%) rename conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/{feign => }/Util.java (86%) delete mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/BackoffStrategy.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverRetryingBackoffStrategy.java delete mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/PathTemplateHeaderEnrichmentContract.java rename conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/CollectionDefaultDecodingTest.java (88%) rename conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/EmptyContainerDecoderTest.java (97%) rename conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/GuavaOptionalAwareDecoderTest.java (96%) rename conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/GuavaOptionalComplexType.java (95%) rename conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/GuavaTestServer.java (98%) rename conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/HeaderAccessUtilsTest.java (94%) rename conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/InputStreamDelegateDecoderTest.java (91%) rename conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/InputStreamDelegateEncoderTest.java (88%) rename conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/Java8ComplexType.java (95%) rename conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/Java8OptionalAwareDecoderTest.java (93%) rename conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/Java8TestServer.java (98%) rename conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/NeverReturnNullDecoderTest.java (90%) rename conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/QosErrorDecoderTest.java (96%) rename conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/SlashEncodingContractTest.java (92%) rename conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/TextDelegateDecoderTest.java (94%) rename conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/TextDelegateEncoderTest.java (91%) rename conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/TextEncoderTest.java (88%) rename conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/{feignimpl => }/UserAgentTest.java (90%) diff --git a/.palantir/revapi.yml b/.palantir/revapi.yml index 50d35298f..a7ba76966 100644 --- a/.palantir/revapi.yml +++ b/.palantir/revapi.yml @@ -91,3 +91,216 @@ acceptedBreaks: old: "method void com.palantir.conjure.java.client.config.ClientConfiguration::checkTimeoutPrecision(java.time.Duration,\ \ java.lang.String)" justification: "Removed internal method" + com.palantir.conjure.java.runtime:conjure-java-jaxrs-client: + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.CborDelegateDecoder" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.CborDelegateEncoder" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.EmptyContainerDecoder" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.EndpointNameHeaderEnrichmentContract" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.GuavaEmptyOptionalExpander" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.GuavaNullOptionalExpander" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.GuavaOptionalAwareContract" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.GuavaOptionalAwareDecoder" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.HeaderAccessUtils" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.InputStreamDelegateDecoder" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.InputStreamDelegateEncoder" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.Java8EmptyOptionalDoubleExpander" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.Java8EmptyOptionalExpander" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.Java8EmptyOptionalIntExpander" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.Java8EmptyOptionalLongExpander" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.Java8NullOptionalDoubleExpander" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.Java8NullOptionalExpander" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.Java8NullOptionalIntExpander" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.Java8NullOptionalLongExpander" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.Java8OptionalAwareContract" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.Java8OptionalAwareDecoder" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.MethodHeaderEnrichmentContract" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.NeverReturnNullDecoder" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.PathTemplateHeaderEnrichmentContract" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.QosErrorDecoder" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.SlashEncodingContract" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.TextDelegateDecoder" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class com.palantir.conjure.java.client.jaxrs.feignimpl.TextDelegateEncoder" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class feign.ConjureCborDelegateDecoder" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class feign.ConjureCborDelegateEncoder" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class feign.ConjureEmptyContainerDecoder" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class feign.ConjureGuavaOptionalAwareDecoder" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class feign.ConjureInputStreamDelegateDecoder" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class feign.ConjureInputStreamDelegateEncoder" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class feign.ConjureJava8OptionalAwareDecoder" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class feign.ConjureNeverReturnNullDecoder" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class feign.ConjureTextDelegateDecoder" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "class feign.ConjureTextDelegateEncoder" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "enum com.palantir.conjure.java.client.jaxrs.feignimpl.NeverRetryingBackoffStrategy" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "enum com.palantir.conjure.java.client.jaxrs.feignimpl.PathTemplateHeaderRewriter" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.removed" + old: "interface com.palantir.conjure.java.client.jaxrs.feignimpl.BackoffStrategy" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.visibilityReduced" + old: "class com.palantir.conjure.java.client.jaxrs.CompatibleJaxRsContract" + new: "class com.palantir.conjure.java.client.jaxrs.CompatibleJaxRsContract" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.visibilityReduced" + old: "class com.palantir.conjure.java.client.jaxrs.FeignJaxRsClientBuilder" + new: "class com.palantir.conjure.java.client.jaxrs.FeignJaxRsClientBuilder" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.visibilityReduced" + old: "class com.palantir.conjure.java.client.jaxrs.JaxRsJakartaCompatibility" + new: "class com.palantir.conjure.java.client.jaxrs.JaxRsJakartaCompatibility" + justification: "Removing Feign dependency and making internals private" + - code: "java.class.visibilityReduced" + old: "enum com.palantir.conjure.java.client.jaxrs.JaxRsJakartaCompatibility.Annotations" + new: "enum com.palantir.conjure.java.client.jaxrs.JaxRsJakartaCompatibility.Annotations" + justification: "Removing Feign dependency and making internals private" + - code: "java.method.removed" + old: "method feign.MethodMetadata feign.Contract.BaseContract::parseAndValidatateMetadata(java.lang.reflect.Method)\ + \ @ com.palantir.conjure.java.client.jaxrs.CompatibleJaxRsContract" + justification: "Removing Feign dependency and making internals private" + - code: "java.method.removed" + old: "method java.util.Collection feign.Contract.BaseContract::addTemplatedParam(java.util.Collection,\ + \ java.lang.String) @ com.palantir.conjure.java.client.jaxrs.CompatibleJaxRsContract" + justification: "Removing Feign dependency and making internals private" + - code: "java.method.removed" + old: "method java.util.List feign.Contract.BaseContract::parseAndValidatateMetadata(java.lang.Class)\ + \ @ com.palantir.conjure.java.client.jaxrs.CompatibleJaxRsContract" + justification: "Removing Feign dependency and making internals private" + - code: "java.method.removed" + old: "method void feign.Contract.BaseContract::nameParam(feign.MethodMetadata,\ + \ java.lang.String, int) @ com.palantir.conjure.java.client.jaxrs.CompatibleJaxRsContract" + justification: "Removing Feign dependency and making internals private" + - code: "java.method.visibilityReduced" + old: "method boolean com.palantir.conjure.java.client.jaxrs.CompatibleJaxRsContract::processAnnotationsOnParameter(feign.MethodMetadata,\ + \ java.lang.annotation.Annotation[], int)" + new: "method boolean com.palantir.conjure.java.client.jaxrs.CompatibleJaxRsContract::processAnnotationsOnParameter(com.palantir.conjure.java.client.jaxrs.MethodMetadata,\ + \ java.lang.annotation.Annotation[], int)" + justification: "Removing Feign dependency and making internals private" + - code: "java.method.visibilityReduced" + old: "method boolean com.palantir.conjure.java.client.jaxrs.JaxRsJakartaCompatibility.Annotations::matches(java.lang.Class)" + new: "method boolean com.palantir.conjure.java.client.jaxrs.JaxRsJakartaCompatibility.Annotations::matches(java.lang.Class)" + justification: "Removing Feign dependency and making internals private" + - code: "java.method.visibilityReduced" + old: "method boolean com.palantir.conjure.java.client.jaxrs.JaxRsJakartaCompatibility.Annotations::matches(java.util.Set>)" + new: "method boolean com.palantir.conjure.java.client.jaxrs.JaxRsJakartaCompatibility.Annotations::matches(java.util.Set>)" + justification: "Removing Feign dependency and making internals private" + - code: "java.method.visibilityReduced" + old: "method com.fasterxml.jackson.databind.ObjectMapper com.palantir.conjure.java.client.jaxrs.FeignJaxRsClientBuilder::getCborObjectMapper()" + new: "method com.fasterxml.jackson.databind.ObjectMapper com.palantir.conjure.java.client.jaxrs.FeignJaxRsClientBuilder::getCborObjectMapper()" + justification: "Removing Feign dependency and making internals private" + - code: "java.method.visibilityReduced" + old: "method com.fasterxml.jackson.databind.ObjectMapper com.palantir.conjure.java.client.jaxrs.FeignJaxRsClientBuilder::getObjectMapper()" + new: "method com.fasterxml.jackson.databind.ObjectMapper com.palantir.conjure.java.client.jaxrs.FeignJaxRsClientBuilder::getObjectMapper()" + justification: "Removing Feign dependency and making internals private" + - code: "java.method.visibilityReduced" + old: "method java.lang.annotation.Annotation com.palantir.conjure.java.client.jaxrs.JaxRsJakartaCompatibility.Annotations::getAnnotation(java.lang.reflect.AnnotatedElement)" + new: "method java.lang.annotation.Annotation com.palantir.conjure.java.client.jaxrs.JaxRsJakartaCompatibility.Annotations::getAnnotation(java.lang.reflect.AnnotatedElement)" + justification: "Removing Feign dependency and making internals private" + - code: "java.method.visibilityReduced" + old: "method void com.palantir.conjure.java.client.jaxrs.CompatibleJaxRsContract::()" + new: "method void com.palantir.conjure.java.client.jaxrs.CompatibleJaxRsContract::()" + justification: "Removing Feign dependency and making internals private" + - code: "java.method.visibilityReduced" + old: "method void com.palantir.conjure.java.client.jaxrs.CompatibleJaxRsContract::processAnnotationOnClass(feign.MethodMetadata,\ + \ java.lang.Class)" + new: "method void com.palantir.conjure.java.client.jaxrs.CompatibleJaxRsContract::processAnnotationOnClass(com.palantir.conjure.java.client.jaxrs.MethodMetadata,\ + \ java.lang.Class)" + justification: "Removing Feign dependency and making internals private" + - code: "java.method.visibilityReduced" + old: "method void com.palantir.conjure.java.client.jaxrs.CompatibleJaxRsContract::processAnnotationOnMethod(feign.MethodMetadata,\ + \ java.lang.annotation.Annotation, java.lang.reflect.Method)" + new: "method void com.palantir.conjure.java.client.jaxrs.CompatibleJaxRsContract::processAnnotationOnMethod(com.palantir.conjure.java.client.jaxrs.MethodMetadata,\ + \ java.lang.annotation.Annotation, java.lang.reflect.Method)" + justification: "Removing Feign dependency and making internals private" + com.palantir.conjure.java.runtime:conjure-scala-jaxrs-client: + - code: "java.class.visibilityReduced" + old: "class com.palantir.conjure.java.client.jaxrs.FeignJaxRsScalaClientBuilder" + new: "class com.palantir.conjure.java.client.jaxrs.FeignJaxRsScalaClientBuilder" + justification: "Removing Feign dependency and making internals private" + - code: "java.method.visibilityReduced" + old: "method com.fasterxml.jackson.databind.ObjectMapper com.palantir.conjure.java.client.jaxrs.FeignJaxRsScalaClientBuilder::getCborObjectMapper()" + new: "method com.fasterxml.jackson.databind.ObjectMapper com.palantir.conjure.java.client.jaxrs.FeignJaxRsScalaClientBuilder::getCborObjectMapper()" + justification: "Removing Feign dependency and making internals private" + - code: "java.method.visibilityReduced" + old: "method com.fasterxml.jackson.databind.ObjectMapper com.palantir.conjure.java.client.jaxrs.FeignJaxRsScalaClientBuilder::getObjectMapper()" + new: "method com.fasterxml.jackson.databind.ObjectMapper com.palantir.conjure.java.client.jaxrs.FeignJaxRsScalaClientBuilder::getObjectMapper()" + justification: "Removing Feign dependency and making internals private" diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/AbstractDelegatingContract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/AbstractDelegatingContract.java similarity index 83% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/AbstractDelegatingContract.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/AbstractDelegatingContract.java index 7daef3456..756257c93 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/AbstractDelegatingContract.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/AbstractDelegatingContract.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,8 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; -import com.palantir.conjure.java.client.jaxrs.feign.Contract; -import com.palantir.conjure.java.client.jaxrs.feign.Feign; -import com.palantir.conjure.java.client.jaxrs.feign.MethodMetadata; import java.lang.reflect.Method; import java.util.LinkedHashMap; import java.util.List; @@ -60,5 +57,5 @@ public final List parseAndValidateMetadata(Class targetType) return mdList; } - protected abstract void processMetadata(Class targetType, Method method, MethodMetadata metadata); + abstract void processMetadata(Class targetType, Method method, MethodMetadata metadata); } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/AbstractFeignJaxRsClientBuilder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/AbstractFeignJaxRsClientBuilder.java index 96c87d085..577b1f9b8 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/AbstractFeignJaxRsClientBuilder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/AbstractFeignJaxRsClientBuilder.java @@ -21,30 +21,6 @@ import com.palantir.conjure.java.annotations.JaxRsServer; import com.palantir.conjure.java.api.config.service.UserAgent; import com.palantir.conjure.java.client.config.ClientConfiguration; -import com.palantir.conjure.java.client.jaxrs.feign.Contract; -import com.palantir.conjure.java.client.jaxrs.feign.Feign; -import com.palantir.conjure.java.client.jaxrs.feign.Request; -import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; -import com.palantir.conjure.java.client.jaxrs.feign.Target; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; -import com.palantir.conjure.java.client.jaxrs.feignimpl.CborDelegateDecoder; -import com.palantir.conjure.java.client.jaxrs.feignimpl.CborDelegateEncoder; -import com.palantir.conjure.java.client.jaxrs.feignimpl.EmptyContainerDecoder; -import com.palantir.conjure.java.client.jaxrs.feignimpl.EndpointNameHeaderEnrichmentContract; -import com.palantir.conjure.java.client.jaxrs.feignimpl.GuavaOptionalAwareContract; -import com.palantir.conjure.java.client.jaxrs.feignimpl.GuavaOptionalAwareDecoder; -import com.palantir.conjure.java.client.jaxrs.feignimpl.InputStreamDelegateDecoder; -import com.palantir.conjure.java.client.jaxrs.feignimpl.InputStreamDelegateEncoder; -import com.palantir.conjure.java.client.jaxrs.feignimpl.JacksonDecoder; -import com.palantir.conjure.java.client.jaxrs.feignimpl.JacksonEncoder; -import com.palantir.conjure.java.client.jaxrs.feignimpl.Java8OptionalAwareContract; -import com.palantir.conjure.java.client.jaxrs.feignimpl.Java8OptionalAwareDecoder; -import com.palantir.conjure.java.client.jaxrs.feignimpl.MethodHeaderEnrichmentContract; -import com.palantir.conjure.java.client.jaxrs.feignimpl.NeverReturnNullDecoder; -import com.palantir.conjure.java.client.jaxrs.feignimpl.SlashEncodingContract; -import com.palantir.conjure.java.client.jaxrs.feignimpl.TextDelegateDecoder; -import com.palantir.conjure.java.client.jaxrs.feignimpl.TextDelegateEncoder; import com.palantir.conjure.java.dialogue.serde.DefaultConjureRuntime; import com.palantir.conjure.java.okhttp.HostEventsSink; import com.palantir.dialogue.Channel; @@ -70,9 +46,9 @@ abstract class AbstractFeignJaxRsClientBuilder { this.config = config; } - protected abstract ObjectMapper getObjectMapper(); + abstract ObjectMapper getObjectMapper(); - protected abstract ObjectMapper getCborObjectMapper(); + abstract ObjectMapper getCborObjectMapper(); /** Set the host metrics registry to use when constructing the OkHttp client. */ final AbstractFeignJaxRsClientBuilder hostEventsSink(HostEventsSink newHostEventsSink) { diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CborDelegateDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/CborDelegateDecoder.java similarity index 87% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CborDelegateDecoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/CborDelegateDecoder.java index 25803ca0d..894d5dcde 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CborDelegateDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/CborDelegateDecoder.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,14 +14,12 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.net.HttpHeaders; -import com.palantir.conjure.java.client.jaxrs.feign.Response; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import java.io.IOException; import java.io.PushbackInputStream; import java.lang.reflect.Type; @@ -36,12 +34,12 @@ *

Ideally we'll codegen a client which handles the content-type switching where necessary (multiple possible * response Content-Types from the server) and does not do the checking where this is known at compile time. */ -public final class CborDelegateDecoder implements Decoder { +final class CborDelegateDecoder implements Decoder { private final ObjectMapper cborMapper; private final Decoder delegate; - public CborDelegateDecoder(ObjectMapper cborMapper, Decoder delegate) { + CborDelegateDecoder(ObjectMapper cborMapper, Decoder delegate) { this.cborMapper = cborMapper; this.delegate = delegate; } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CborDelegateEncoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/CborDelegateEncoder.java similarity index 85% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CborDelegateEncoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/CborDelegateEncoder.java index 8cf78fe36..7fbc6c1e3 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CborDelegateEncoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/CborDelegateEncoder.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,15 +14,13 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableSet; import com.google.common.net.HttpHeaders; -import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; import java.io.UncheckedIOException; import java.lang.reflect.Type; import java.util.Collection; @@ -36,14 +34,14 @@ *

In the future we will likely codegen the client and thus remove the need for scanning the headers on every * request. */ -public final class CborDelegateEncoder implements Encoder { +final class CborDelegateEncoder implements Encoder { public static final String MIME_TYPE = "application/cbor"; private final ObjectMapper cborMapper; private final Encoder delegate; - public CborDelegateEncoder(ObjectMapper cborMapper, Encoder delegate) { + CborDelegateEncoder(ObjectMapper cborMapper, Encoder delegate) { this.cborMapper = cborMapper; this.delegate = delegate; } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Client.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Client.java similarity index 93% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Client.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Client.java index 72c890cb5..fe73a7967 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Client.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Client.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feign; +package com.palantir.conjure.java.client.jaxrs; import java.io.IOException; /** * Submits HTTP {@link Request requests}. Implementations are expected to be thread-safe. */ -public interface Client { +interface Client { /** * Executes a request against its {@link Request#url() url} and returns a response. diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/CompatibleJaxRsContract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/CompatibleJaxRsContract.java index 742fe877d..9fa0936d5 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/CompatibleJaxRsContract.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/CompatibleJaxRsContract.java @@ -18,8 +18,6 @@ import com.google.common.base.Strings; import com.google.common.net.HttpHeaders; import com.palantir.conjure.java.client.jaxrs.JaxRsJakartaCompatibility.Annotations; -import com.palantir.conjure.java.client.jaxrs.feign.Contract; -import com.palantir.conjure.java.client.jaxrs.feign.MethodMetadata; import com.palantir.logsafe.Preconditions; import com.palantir.logsafe.SafeArg; import com.palantir.logsafe.exceptions.SafeIllegalStateException; @@ -34,10 +32,10 @@ * JAXRSContract.java which is licensed under Apache 2. * We have modified the implementation to handle both jaxrs and jakarta annotations, easing migrations. */ -public final class CompatibleJaxRsContract extends Contract.BaseContract { +final class CompatibleJaxRsContract extends Contract.BaseContract { @Override - protected void processAnnotationOnClass(MethodMetadata data, Class clz) { + void processAnnotationOnClass(MethodMetadata data, Class clz) { Annotation path = Annotations.PATH.getAnnotation(clz); if (path != null) { String pathValue = Strings.emptyToNull(getAnnotationValue(path)); @@ -63,7 +61,7 @@ protected void processAnnotationOnClass(MethodMetadata data, Class clz) { } @Override - protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, Method method) { + void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, Method method) { Class annotationType = methodAnnotation.annotationType(); Annotation http = Annotations.HTTP_METHOD.getAnnotation(annotationType); if (http != null) { @@ -112,7 +110,7 @@ private void handleConsumesAnnotation(MethodMetadata data, Annotation consumes, } @Override - protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) { + boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) { boolean isHttpParam = false; for (Annotation parameterAnnotation : annotations) { Class annotationType = diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Contract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Contract.java similarity index 91% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Contract.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Contract.java index bf89e059a..60d17927a 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Contract.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Contract.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feign; +package com.palantir.conjure.java.client.jaxrs; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkState; +import static com.palantir.conjure.java.client.jaxrs.Util.checkState; import java.lang.annotation.Annotation; import java.lang.reflect.Method; @@ -31,7 +31,7 @@ * Defines what annotations and values are valid on interfaces. */ @SuppressWarnings("MissingSummary") -public interface Contract { +interface Contract { /** * Called to parse the methods in the class that are linked to HTTP requests. @@ -76,7 +76,7 @@ public List parseAndValidateMetadata(Class targetType) { /** * Called indirectly by {@link #parseAndValidateMetadata(Class)}. */ - protected MethodMetadata parseAndValidateMetadata(Class targetType, Method method) { + MethodMetadata parseAndValidateMetadata(Class targetType, Method method) { MethodMetadata data = new MethodMetadata(); data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType())); data.configKey(Feign.configKey(targetType, method)); @@ -136,14 +136,14 @@ protected MethodMetadata parseAndValidateMetadata(Class targetType, Method me * @param data metadata collected so far relating to the current java method. * @param clz the class to process */ - protected abstract void processAnnotationOnClass(MethodMetadata data, Class clz); + abstract void processAnnotationOnClass(MethodMetadata data, Class clz); /** * @param data metadata collected so far relating to the current java method. * @param annotation annotations present on the current method annotation. * @param method method currently being processed. */ - protected abstract void processAnnotationOnMethod(MethodMetadata data, Annotation annotation, Method method); + abstract void processAnnotationOnMethod(MethodMetadata data, Annotation annotation, Method method); /** * @param data metadata collected so far relating to the current java method. @@ -153,10 +153,10 @@ protected MethodMetadata parseAndValidateMetadata(Class targetType, Method me * @return true if you called {@link #nameParam(MethodMetadata, String, int)} after finding an * http-relevant annotation. */ - protected abstract boolean processAnnotationsOnParameter( + abstract boolean processAnnotationsOnParameter( MethodMetadata data, Annotation[] annotations, int paramIndex); - protected Collection addTemplatedParam(Collection possiblyNull, String name) { + Collection addTemplatedParam(Collection possiblyNull, String name) { if (possiblyNull == null) { possiblyNull = new ArrayList(); } @@ -167,7 +167,7 @@ protected Collection addTemplatedParam(Collection possiblyNull, /** * links a parameter name to its index in the method signature. */ - protected void nameParam(MethodMetadata data, String name, int i) { + void nameParam(MethodMetadata data, String name, int i) { Collection names = data.indexToName().containsKey(i) ? data.indexToName().get(i) : new ArrayList(); names.add(name); diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/Decoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Decoder.java similarity index 90% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/Decoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Decoder.java index a49100dd3..d64bea901 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/Decoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Decoder.java @@ -13,11 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feign.codec; +package com.palantir.conjure.java.client.jaxrs; -import com.palantir.conjure.java.client.jaxrs.feign.Feign; -import com.palantir.conjure.java.client.jaxrs.feign.Response; -import com.palantir.conjure.java.client.jaxrs.feign.Target; import java.io.IOException; import java.lang.reflect.Type; @@ -49,7 +46,7 @@ * writing your implementation of Decoder, ensure you also test parameterized types such as {@code * List}. */ -public interface Decoder { +interface Decoder { /** * Decodes an http response into an object corresponding to its {@link diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/DefaultMethodHandler.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/DefaultMethodHandler.java similarity index 95% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/DefaultMethodHandler.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/DefaultMethodHandler.java index 2975a5bbd..71048cd3d 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/DefaultMethodHandler.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/DefaultMethodHandler.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feign; +package com.palantir.conjure.java.client.jaxrs; import com.palantir.logsafe.exceptions.SafeIllegalStateException; import java.lang.invoke.MethodHandle; @@ -35,7 +35,7 @@ final class DefaultMethodHandler implements MethodHandler { // handle is effectively final after bindTo has been called. private MethodHandle handle; - public DefaultMethodHandler(Method defaultMethod) { + DefaultMethodHandler(Method defaultMethod) { try { Class declaringClass = defaultMethod.getDeclaringClass(); Field field = Lookup.class.getDeclaredField("IMPL_LOOKUP"); @@ -54,7 +54,7 @@ public DefaultMethodHandler(Method defaultMethod) { * Bind this handler to a proxy object. After bound, DefaultMethodHandler#invoke will act as if it was called * on the proxy object. Must be called once and only once for a given instance of DefaultMethodHandler */ - public void bindTo(Object proxy) { + void bindTo(Object proxy) { if (handle != null) { throw new SafeIllegalStateException("Attempted to rebind a default method handler that was already bound"); } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/DialogueFeignClient.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/DialogueFeignClient.java index 2054d09b0..b453c3880 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/DialogueFeignClient.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/DialogueFeignClient.java @@ -26,11 +26,6 @@ import com.google.common.primitives.Ints; import com.google.common.util.concurrent.UncheckedExecutionException; import com.palantir.conjure.java.api.errors.UnknownRemoteException; -import com.palantir.conjure.java.client.jaxrs.feign.Client; -import com.palantir.conjure.java.client.jaxrs.feign.Request; -import com.palantir.conjure.java.client.jaxrs.feign.codec.ErrorDecoder; -import com.palantir.conjure.java.client.jaxrs.feignimpl.EndpointNameHeaderEnrichmentContract; -import com.palantir.conjure.java.client.jaxrs.feignimpl.MethodHeaderEnrichmentContract; import com.palantir.dialogue.Channel; import com.palantir.dialogue.ConjureRuntime; import com.palantir.dialogue.Deserializer; @@ -95,7 +90,7 @@ final class DialogueFeignClient implements Client { } @Override - public com.palantir.conjure.java.client.jaxrs.feign.Response execute(Request request) throws IOException { + public com.palantir.conjure.java.client.jaxrs.Response execute(Request request) throws IOException { com.palantir.dialogue.Request.Builder builder = com.palantir.dialogue.Request.builder(); builder.putPathParams(REQUEST_URL_PATH_PARAM, request.url()); @@ -211,8 +206,7 @@ public void close() { } } - private static final class DialogueResponseBody - implements com.palantir.conjure.java.client.jaxrs.feign.Response.Body { + private static final class DialogueResponseBody implements com.palantir.conjure.java.client.jaxrs.Response.Body { private final Response response; @@ -278,12 +272,12 @@ public String toString() { } } - enum FeignResponseDeserializer implements Deserializer { + enum FeignResponseDeserializer implements Deserializer { INSTANCE; @Override - public com.palantir.conjure.java.client.jaxrs.feign.Response deserialize(Response response) { - return com.palantir.conjure.java.client.jaxrs.feign.Response.create( + public com.palantir.conjure.java.client.jaxrs.Response deserialize(Response response) { + return com.palantir.conjure.java.client.jaxrs.Response.create( response.code(), null, Multimaps.asMap((Multimap) response.headers()), @@ -300,17 +294,17 @@ public Optional accepts() { /** Converts back from a feign response into a dialogue response for exception mapping. */ private static final class FeignDialogueResponse implements Response { - private final com.palantir.conjure.java.client.jaxrs.feign.Response delegate; + private final com.palantir.conjure.java.client.jaxrs.Response delegate; private final ResponseAttachments attachments; - FeignDialogueResponse(com.palantir.conjure.java.client.jaxrs.feign.Response delegate) { + FeignDialogueResponse(com.palantir.conjure.java.client.jaxrs.Response delegate) { this.delegate = delegate; this.attachments = ResponseAttachments.create(); } @Override public InputStream body() { - com.palantir.conjure.java.client.jaxrs.feign.Response.Body body = delegate.body(); + com.palantir.conjure.java.client.jaxrs.Response.Body body = delegate.body(); if (body != null) { try { return body.asInputStream(); @@ -367,7 +361,7 @@ static final class RemoteExceptionDecoder implements ErrorDecoder { } @Override - public Exception decode(String _methodKey, com.palantir.conjure.java.client.jaxrs.feign.Response response) { + public Exception decode(String _methodKey, com.palantir.conjure.java.client.jaxrs.Response response) { try { // The dialogue empty body deserializer properly handles exception mapping runtime.bodySerDe().emptyBodyDeserializer().deserialize(new FeignDialogueResponse(response)); diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/EmptyContainerDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/EmptyContainerDecoder.java similarity index 94% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/EmptyContainerDecoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/EmptyContainerDecoder.java index 9ed48ebba..772f6aff9 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/EmptyContainerDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/EmptyContainerDecoder.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,15 +14,13 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.benmanes.caffeine.cache.CacheLoader; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; -import com.palantir.conjure.java.client.jaxrs.feign.Response; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import com.palantir.logsafe.Preconditions; import com.palantir.logsafe.SafeArg; import com.palantir.logsafe.logger.SafeLogger; @@ -48,12 +46,12 @@ * *

Empty instances are cached and re-used to avoid reflection and exceptions on a hot codepath. */ -public final class EmptyContainerDecoder implements Decoder { +final class EmptyContainerDecoder implements Decoder { private final LoadingCache blankInstanceCache; private final Decoder delegate; - public EmptyContainerDecoder(ObjectMapper mapper, Decoder delegate) { + EmptyContainerDecoder(ObjectMapper mapper, Decoder delegate) { this.delegate = delegate; this.blankInstanceCache = Caffeine.newBuilder() .maximumSize(1000) diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/Encoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Encoder.java similarity index 89% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/Encoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Encoder.java index d5eb5da18..42c53f4f1 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/Encoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Encoder.java @@ -13,11 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feign.codec; +package com.palantir.conjure.java.client.jaxrs; -import com.palantir.conjure.java.client.jaxrs.feign.MethodMetadata; -import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; -import com.palantir.conjure.java.client.jaxrs.feign.Util; import java.lang.reflect.Type; /** @@ -57,7 +54,7 @@ * password); * */ -public interface Encoder { +interface Encoder { /** Type literal for {@code Map}, indicating the object to encode is a form. */ Type MAP_STRING_WILDCARD = Util.MAP_STRING_WILDCARD; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/EndpointNameHeaderEnrichmentContract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/EndpointNameHeaderEnrichmentContract.java similarity index 76% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/EndpointNameHeaderEnrichmentContract.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/EndpointNameHeaderEnrichmentContract.java index 00f21ce86..f6845d962 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/EndpointNameHeaderEnrichmentContract.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/EndpointNameHeaderEnrichmentContract.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,8 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; -import com.palantir.conjure.java.client.jaxrs.feign.Contract; -import com.palantir.conjure.java.client.jaxrs.feign.MethodMetadata; import com.palantir.dialogue.HttpMethod; import com.palantir.logsafe.SafeArg; import com.palantir.logsafe.exceptions.SafeIllegalArgumentException; @@ -30,16 +28,16 @@ * * This should be considered internal API and should not be depended upon. */ -public final class EndpointNameHeaderEnrichmentContract extends AbstractDelegatingContract { +final class EndpointNameHeaderEnrichmentContract extends AbstractDelegatingContract { - public static final String ENDPOINT_NAME_HEADER = "dialogue-endpoint-name"; + static final String ENDPOINT_NAME_HEADER = "dialogue-endpoint-name"; - public EndpointNameHeaderEnrichmentContract(Contract delegate) { + EndpointNameHeaderEnrichmentContract(Contract delegate) { super(delegate); } @Override - protected void processMetadata(Class targetType, Method method, MethodMetadata metadata) { + void processMetadata(Class targetType, Method method, MethodMetadata metadata) { String httpMethod = metadata.template().method(); if (httpMethod == null) { throw new SafeNullPointerException( diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/ErrorDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/ErrorDecoder.java similarity index 91% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/ErrorDecoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/ErrorDecoder.java index a2942ed41..825f309f8 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/codec/ErrorDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/ErrorDecoder.java @@ -13,10 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feign.codec; - -import com.palantir.conjure.java.client.jaxrs.feign.Feign; -import com.palantir.conjure.java.client.jaxrs.feign.Response; +package com.palantir.conjure.java.client.jaxrs; /** * Allows you to massage an exception into a application-specific one. Converting out to a throttle @@ -44,7 +41,7 @@ * the DynECT api, a job still running condition is returned with a 200 status, encoded in json. * When scenarios like this occur, you should raise an application-specific exception. */ -public interface ErrorDecoder { +interface ErrorDecoder { /** * Implement this method in order to decode an HTTP {@link Response} when {@link diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Expander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Expander.java similarity index 90% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Expander.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Expander.java index 18608ce67..477111b17 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Expander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Expander.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feign; +package com.palantir.conjure.java.client.jaxrs; -public interface Expander { +interface Expander { /** * Expands the value into a string. Does not accept or return null. diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Feign.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Feign.java similarity index 80% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Feign.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Feign.java index 14f3fd932..cb3957e27 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Feign.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Feign.java @@ -13,12 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feign; +package com.palantir.conjure.java.client.jaxrs; -import com.palantir.conjure.java.client.jaxrs.feign.ReflectiveFeign.ParseHandlersByName; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; -import com.palantir.conjure.java.client.jaxrs.feign.codec.ErrorDecoder; +import com.palantir.conjure.java.client.jaxrs.ReflectiveFeign.ParseHandlersByName; import java.lang.reflect.Method; import java.lang.reflect.Type; @@ -27,9 +24,9 @@ * implementation, Feign is a {@link Feign#newInstance factory} for generating {@link Target * targeted} http apis. */ -public abstract class Feign { +abstract class Feign { - public static Builder builder() { + static Builder builder() { return new Builder(); } @@ -46,7 +43,7 @@ public static Builder builder() { * @param targetType {@link Target#type() type} of the Feign interface. * @param method invoked method, present on {@code type} or its super. */ - public static String configKey(Class targetType, Method method) { + static String configKey(Class targetType, Method method) { StringBuilder builder = new StringBuilder(); builder.append(targetType.getSimpleName()); builder.append('#').append(method.getName()).append('('); @@ -64,9 +61,9 @@ public static String configKey(Class targetType, Method method) { * Returns a new instance of an HTTP API, defined by annotations in the {@link Feign Contract}, * for the specified {@code target}. You should cache this result. */ - public abstract T newInstance(Target target); + abstract T newInstance(Target target); - public static final class Builder { + static final class Builder { private Contract contract; private Client client; @@ -76,32 +73,32 @@ public static final class Builder { private Builder() {} - public Builder contract(Contract contract) { + Builder contract(Contract contract) { this.contract = contract; return this; } - public Builder client(Client client) { + Builder client(Client client) { this.client = client; return this; } - public Builder encoder(Encoder encoder) { + Builder encoder(Encoder encoder) { this.encoder = encoder; return this; } - public Builder decoder(Decoder decoder) { + Builder decoder(Decoder decoder) { this.decoder = decoder; return this; } - public Builder errorDecoder(ErrorDecoder errorDecoder) { + Builder errorDecoder(ErrorDecoder errorDecoder) { this.errorDecoder = errorDecoder; return this; } - public T target(Target target) { + T target(Target target) { SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory(client); ParseHandlersByName handlersByName = diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/FeignJaxRsClientBuilder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/FeignJaxRsClientBuilder.java index 8d4e3c5f4..bf7e80e79 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/FeignJaxRsClientBuilder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/FeignJaxRsClientBuilder.java @@ -22,7 +22,7 @@ import com.palantir.conjure.java.client.config.ClientConfiguration; import com.palantir.conjure.java.serialization.ObjectMappers; -public final class FeignJaxRsClientBuilder extends AbstractFeignJaxRsClientBuilder { +final class FeignJaxRsClientBuilder extends AbstractFeignJaxRsClientBuilder { static final JsonMapper JSON_MAPPER = ObjectMappers.newClientJsonMapper(); static final CBORMapper CBOR_MAPPER = ObjectMappers.newClientCborMapper(); @@ -32,12 +32,12 @@ public final class FeignJaxRsClientBuilder extends AbstractFeignJaxRsClientBuild } @Override - protected ObjectMapper getObjectMapper() { + ObjectMapper getObjectMapper() { return JSON_MAPPER; } @Override - protected ObjectMapper getCborObjectMapper() { + ObjectMapper getCborObjectMapper() { return CBOR_MAPPER; } } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaEmptyOptionalExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/GuavaEmptyOptionalExpander.java similarity index 83% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaEmptyOptionalExpander.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/GuavaEmptyOptionalExpander.java index b05976cc8..896e7b355 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaEmptyOptionalExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/GuavaEmptyOptionalExpander.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,17 +14,16 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import com.google.common.base.Preconditions; -import com.palantir.conjure.java.client.jaxrs.feign.Expander; import java.util.Objects; /** * Expands Optional by using the empty string for {@link com.google.common.base.Optional#absent()} and the * {@link Object#toString()} of the value otherwise. */ -public final class GuavaEmptyOptionalExpander implements Expander { +final class GuavaEmptyOptionalExpander implements Expander { @Override public String expand(Object value) { diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaNullOptionalExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/GuavaNullOptionalExpander.java similarity index 83% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaNullOptionalExpander.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/GuavaNullOptionalExpander.java index c7fff6ab7..a330d5e11 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaNullOptionalExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/GuavaNullOptionalExpander.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,17 +14,16 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import com.google.common.base.Preconditions; -import com.palantir.conjure.java.client.jaxrs.feign.Expander; import java.util.Objects; /** * Expands Optional by using null for {@link com.google.common.base.Optional#absent()} and the {@link Object#toString()} * of the value otherwise. */ -public final class GuavaNullOptionalExpander implements Expander { +final class GuavaNullOptionalExpander implements Expander { @Override public String expand(Object value) { diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalAwareContract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/GuavaOptionalAwareContract.java similarity index 87% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalAwareContract.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/GuavaOptionalAwareContract.java index a4eab1253..646f9eda4 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalAwareContract.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/GuavaOptionalAwareContract.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,9 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import com.palantir.conjure.java.client.jaxrs.JaxRsJakartaCompatibility.Annotations; -import com.palantir.conjure.java.client.jaxrs.feign.Contract; -import com.palantir.conjure.java.client.jaxrs.feign.MethodMetadata; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Arrays; @@ -35,9 +33,9 @@ *

{@link jakarta.ws.rs.PathParam}s require a value, and so we explicitly disallow use with * {@link com.google.common.base.Optional}. */ -public final class GuavaOptionalAwareContract extends AbstractDelegatingContract { +final class GuavaOptionalAwareContract extends AbstractDelegatingContract { - public GuavaOptionalAwareContract(Contract delegate) { + GuavaOptionalAwareContract(Contract delegate) { super(delegate); } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalAwareDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/GuavaOptionalAwareDecoder.java similarity index 83% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalAwareDecoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/GuavaOptionalAwareDecoder.java index 1cde96a9d..d45ec3dff 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalAwareDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/GuavaOptionalAwareDecoder.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,12 +14,10 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import static com.google.common.base.Preconditions.checkNotNull; -import com.palantir.conjure.java.client.jaxrs.feign.Response; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import java.io.IOException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -28,11 +26,11 @@ * Decorates a Feign {@link Decoder} such that it returns {@link com.google.common.base.Optional#absent} when observing * an HTTP 204 error code for a method with {@link Type} {@link com.google.common.base.Optional}. */ -public final class GuavaOptionalAwareDecoder implements Decoder { +final class GuavaOptionalAwareDecoder implements Decoder { private final Decoder delegate; - public GuavaOptionalAwareDecoder(Decoder delegate) { + GuavaOptionalAwareDecoder(Decoder delegate) { this.delegate = delegate; } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/HeaderAccessUtils.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/HeaderAccessUtils.java similarity index 79% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/HeaderAccessUtils.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/HeaderAccessUtils.java index f310c600f..1fa371697 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/HeaderAccessUtils.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/HeaderAccessUtils.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,8 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; -import com.palantir.conjure.java.client.jaxrs.feign.Request; -import com.palantir.conjure.java.client.jaxrs.feign.Response; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -30,10 +28,10 @@ * map which is case-insensitive with respect to the key. com.netflix.feign:feign-core:8.18.0 will have it for the * {@link Response} headers due to https://github.com/Netflix/feign/pull/418. */ -public final class HeaderAccessUtils { +final class HeaderAccessUtils { private HeaderAccessUtils() {} - public static boolean caseInsensitiveContains(Map> headers, String headerName) { + static boolean caseInsensitiveContains(Map> headers, String headerName) { for (String key : headers.keySet()) { if (headerName.equalsIgnoreCase(key)) { return true; @@ -46,7 +44,7 @@ public static boolean caseInsensitiveContains(Map> he * Compares the keys of the map to the headerName in a case-insensitive manner and returns null if it was never * found. */ - public static Collection caseInsensitiveGet(Map> headers, String headerName) { + static Collection caseInsensitiveGet(Map> headers, String headerName) { List result = new ArrayList<>(); boolean neverFound = true; for (Map.Entry> entry : headers.entrySet()) { diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/InputStreamDelegateDecoder.java similarity index 75% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateDecoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/InputStreamDelegateDecoder.java index e188443ec..8b47cdc43 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/InputStreamDelegateDecoder.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,10 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import com.codahale.metrics.Meter; -import com.palantir.conjure.java.client.jaxrs.feign.Response; -import com.palantir.conjure.java.client.jaxrs.feign.Util; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; -import com.palantir.conjure.java.client.jaxrs.feignimpl.FeignClientMetrics.DangerousBuffering_Direction; +import com.palantir.conjure.java.client.jaxrs.FeignClientMetrics.DangerousBuffering_Direction; import com.palantir.logsafe.Safe; import com.palantir.tritium.metrics.registry.SharedTaggedMetricRegistries; import java.io.ByteArrayInputStream; @@ -29,16 +26,16 @@ import java.lang.reflect.Type; /** If the return type is InputStream, return it, otherwise delegate to provided decoder. */ -public final class InputStreamDelegateDecoder implements Decoder { +final class InputStreamDelegateDecoder implements Decoder { private final Decoder delegate; private final Meter dangerousBufferingMeter; - public InputStreamDelegateDecoder(Decoder delegate) { + InputStreamDelegateDecoder(Decoder delegate) { this("unknown", delegate); } @SuppressWarnings("deprecation") // No access to a TaggedMetricRegistry without breaking API - public InputStreamDelegateDecoder(@Safe String clientNameForLogging, Decoder delegate) { + InputStreamDelegateDecoder(@Safe String clientNameForLogging, Decoder delegate) { this.delegate = delegate; this.dangerousBufferingMeter = FeignClientMetrics.of(SharedTaggedMetricRegistries.getSingleton()) .dangerousBuffering() diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateEncoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/InputStreamDelegateEncoder.java similarity index 76% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateEncoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/InputStreamDelegateEncoder.java index 63fa37555..9858799c5 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateEncoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/InputStreamDelegateEncoder.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,10 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import com.codahale.metrics.Meter; -import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; -import com.palantir.conjure.java.client.jaxrs.feign.Util; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; -import com.palantir.conjure.java.client.jaxrs.feignimpl.FeignClientMetrics.DangerousBuffering_Direction; +import com.palantir.conjure.java.client.jaxrs.FeignClientMetrics.DangerousBuffering_Direction; import com.palantir.logsafe.Safe; import com.palantir.tritium.metrics.registry.SharedTaggedMetricRegistries; import java.io.IOException; @@ -29,16 +26,16 @@ import java.lang.reflect.Type; /** If the body type is an InputStream, write it into the body, otherwise pass to delegate. */ -public final class InputStreamDelegateEncoder implements Encoder { +final class InputStreamDelegateEncoder implements Encoder { private final Encoder delegate; private final Meter dangerousBufferingMeter; - public InputStreamDelegateEncoder(Encoder delegate) { + InputStreamDelegateEncoder(Encoder delegate) { this("unknown", delegate); } @SuppressWarnings("deprecation") // No access to a TaggedMetricRegistry without breaking API - public InputStreamDelegateEncoder(@Safe String clientNameForLogging, Encoder delegate) { + InputStreamDelegateEncoder(@Safe String clientNameForLogging, Encoder delegate) { this.delegate = delegate; this.dangerousBufferingMeter = FeignClientMetrics.of(SharedTaggedMetricRegistries.getSingleton()) .dangerousBuffering() diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/JacksonDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/JacksonDecoder.java similarity index 82% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/JacksonDecoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/JacksonDecoder.java index 5c92b2655..58b4e9b93 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/JacksonDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/JacksonDecoder.java @@ -13,22 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import com.fasterxml.jackson.databind.ObjectMapper; -import com.palantir.conjure.java.client.jaxrs.feign.Response; -import com.palantir.conjure.java.client.jaxrs.feign.Util; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.lang.reflect.Type; -public final class JacksonDecoder implements Decoder { +final class JacksonDecoder implements Decoder { private final ObjectMapper mapper; - public JacksonDecoder(ObjectMapper mapper) { + JacksonDecoder(ObjectMapper mapper) { this.mapper = mapper; } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/JacksonEncoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/JacksonEncoder.java similarity index 81% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/JacksonEncoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/JacksonEncoder.java index a8c787bdd..f1de49ad4 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/JacksonEncoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/JacksonEncoder.java @@ -14,21 +14,19 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; -import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; import java.io.UncheckedIOException; import java.lang.reflect.Type; -public final class JacksonEncoder implements Encoder { +final class JacksonEncoder implements Encoder { private final ObjectMapper mapper; - public JacksonEncoder(ObjectMapper mapper) { + JacksonEncoder(ObjectMapper mapper) { this.mapper = mapper; } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalDoubleExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8EmptyOptionalDoubleExpander.java similarity index 81% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalDoubleExpander.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8EmptyOptionalDoubleExpander.java index 17697c93f..b9cb91778 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalDoubleExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8EmptyOptionalDoubleExpander.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,18 +14,17 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import static com.google.common.base.Preconditions.checkArgument; -import com.palantir.conjure.java.client.jaxrs.feign.Expander; import java.util.OptionalDouble; /** * Expands OptionalDouble by using the empty string for {@link OptionalDouble#empty()} and the {@link Double#toString()} * of the value otherwise. */ -public final class Java8EmptyOptionalDoubleExpander implements Expander { +final class Java8EmptyOptionalDoubleExpander implements Expander { @Override public String expand(Object value) { diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8EmptyOptionalExpander.java similarity index 81% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalExpander.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8EmptyOptionalExpander.java index 496d62dd8..f34098157 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8EmptyOptionalExpander.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,9 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import com.google.common.base.Preconditions; -import com.palantir.conjure.java.client.jaxrs.feign.Expander; import java.util.Objects; import java.util.Optional; @@ -25,7 +24,7 @@ * Expands Optional by using the empty string for {@link Optional#empty()} and the {@link Object#toString()} of the * value otherwise. */ -public final class Java8EmptyOptionalExpander implements Expander { +final class Java8EmptyOptionalExpander implements Expander { @Override public String expand(Object value) { diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalIntExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8EmptyOptionalIntExpander.java similarity index 81% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalIntExpander.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8EmptyOptionalIntExpander.java index 74faea4bb..ea49a7795 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalIntExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8EmptyOptionalIntExpander.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,18 +14,17 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import static com.google.common.base.Preconditions.checkArgument; -import com.palantir.conjure.java.client.jaxrs.feign.Expander; import java.util.OptionalInt; /** * Expands OptionalInt by using the empty string for {@link OptionalInt#empty()} and the {@link Integer#toString()} of * the value otherwise. */ -public final class Java8EmptyOptionalIntExpander implements Expander { +final class Java8EmptyOptionalIntExpander implements Expander { @Override public String expand(Object value) { diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalLongExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8EmptyOptionalLongExpander.java similarity index 81% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalLongExpander.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8EmptyOptionalLongExpander.java index f2f370859..fb854d718 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8EmptyOptionalLongExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8EmptyOptionalLongExpander.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,18 +14,17 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import static com.google.common.base.Preconditions.checkArgument; -import com.palantir.conjure.java.client.jaxrs.feign.Expander; import java.util.OptionalLong; /** * Expands OptionalLong by using the empty string for {@link OptionalLong#empty()} and the {@link Long#toString()} of * the value otherwise. */ -public final class Java8EmptyOptionalLongExpander implements Expander { +final class Java8EmptyOptionalLongExpander implements Expander { @Override public String expand(Object value) { diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalDoubleExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8NullOptionalDoubleExpander.java similarity index 81% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalDoubleExpander.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8NullOptionalDoubleExpander.java index a575a1437..aa7f8fbfd 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalDoubleExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8NullOptionalDoubleExpander.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,18 +14,17 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import static com.google.common.base.Preconditions.checkArgument; -import com.palantir.conjure.java.client.jaxrs.feign.Expander; import java.util.OptionalDouble; /** * Expands OptionalDouble by using null for {@link OptionalDouble#empty()} and the {@link Double#toString()} of the * value otherwise. */ -public final class Java8NullOptionalDoubleExpander implements Expander { +final class Java8NullOptionalDoubleExpander implements Expander { @Override public String expand(Object value) { diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8NullOptionalExpander.java similarity index 81% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalExpander.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8NullOptionalExpander.java index 8d2d73b66..aa257d3fa 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8NullOptionalExpander.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,17 +14,16 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import com.google.common.base.Preconditions; -import com.palantir.conjure.java.client.jaxrs.feign.Expander; import java.util.Objects; import java.util.Optional; /** * Expands Optional by using null for {@link Optional#empty()} and the {@link Object#toString()} of the value otherwise. */ -public final class Java8NullOptionalExpander implements Expander { +final class Java8NullOptionalExpander implements Expander { @Override public String expand(Object value) { diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalIntExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8NullOptionalIntExpander.java similarity index 81% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalIntExpander.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8NullOptionalIntExpander.java index bca6496db..cab4aa0e9 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalIntExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8NullOptionalIntExpander.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,18 +14,17 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import static com.google.common.base.Preconditions.checkArgument; -import com.palantir.conjure.java.client.jaxrs.feign.Expander; import java.util.OptionalInt; /** * Expands OptionalInt by using null for {@link OptionalInt#empty()} and the {@link Integer#toString()} of the value * otherwise. */ -public final class Java8NullOptionalIntExpander implements Expander { +final class Java8NullOptionalIntExpander implements Expander { @Override public String expand(Object value) { diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalLongExpander.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8NullOptionalLongExpander.java similarity index 81% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalLongExpander.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8NullOptionalLongExpander.java index 4d8d9b961..c3e56646e 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8NullOptionalLongExpander.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8NullOptionalLongExpander.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,18 +14,17 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import static com.google.common.base.Preconditions.checkArgument; -import com.palantir.conjure.java.client.jaxrs.feign.Expander; import java.util.OptionalLong; /** * Expands OptionalLong by using null for {@link OptionalLong#empty()} and the {@link Long#toString()} of the value * otherwise. */ -public final class Java8NullOptionalLongExpander implements Expander { +final class Java8NullOptionalLongExpander implements Expander { @Override public String expand(Object value) { diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareContract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8OptionalAwareContract.java similarity index 89% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareContract.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8OptionalAwareContract.java index ce05552cf..60c4ae796 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareContract.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8OptionalAwareContract.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,10 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import com.google.common.collect.ImmutableList; import com.palantir.conjure.java.client.jaxrs.JaxRsJakartaCompatibility.Annotations; -import com.palantir.conjure.java.client.jaxrs.feign.Contract; -import com.palantir.conjure.java.client.jaxrs.feign.Expander; -import com.palantir.conjure.java.client.jaxrs.feign.MethodMetadata; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Arrays; @@ -41,7 +38,7 @@ * *

{@link jakarta.ws.rs.PathParam}s require a value, and so we explicitly disallow use with {@link Optional}. */ -public final class Java8OptionalAwareContract extends AbstractDelegatingContract { +final class Java8OptionalAwareContract extends AbstractDelegatingContract { private static final List expanders = ImmutableList.of( new ExpanderDef(Optional.class, Java8EmptyOptionalExpander.class, Java8NullOptionalExpander.class), @@ -53,12 +50,12 @@ public final class Java8OptionalAwareContract extends AbstractDelegatingContract new ExpanderDef( OptionalLong.class, Java8EmptyOptionalLongExpander.class, Java8NullOptionalLongExpander.class)); - public Java8OptionalAwareContract(Contract delegate) { + Java8OptionalAwareContract(Contract delegate) { super(delegate); } @Override - protected void processMetadata(Class targetType, Method method, MethodMetadata metadata) { + void processMetadata(Class targetType, Method method, MethodMetadata metadata) { Class[] parameterTypes = method.getParameterTypes(); Annotation[][] annotations = method.getParameterAnnotations(); for (int i = 0; i < parameterTypes.length; i++) { diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8OptionalAwareDecoder.java similarity index 83% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareDecoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8OptionalAwareDecoder.java index 45876f7c1..8f391f718 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Java8OptionalAwareDecoder.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,12 +14,10 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import static com.google.common.base.Preconditions.checkNotNull; -import com.palantir.conjure.java.client.jaxrs.feign.Response; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import java.io.IOException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -29,11 +27,11 @@ * Decorates a Feign {@link Decoder} such that it returns {@link Optional#empty} when observing an HTTP 204 error code * for a method with {@link Type} {@link Optional}. */ -public final class Java8OptionalAwareDecoder implements Decoder { +final class Java8OptionalAwareDecoder implements Decoder { private final Decoder delegate; - public Java8OptionalAwareDecoder(Decoder delegate) { + Java8OptionalAwareDecoder(Decoder delegate) { this.delegate = delegate; } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/JaxRsJakartaCompatibility.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/JaxRsJakartaCompatibility.java index f645f907b..ec3b2ec42 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/JaxRsJakartaCompatibility.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/JaxRsJakartaCompatibility.java @@ -21,7 +21,7 @@ import java.util.Set; import javax.annotation.Nullable; -public final class JaxRsJakartaCompatibility { +final class JaxRsJakartaCompatibility { private JaxRsJakartaCompatibility() {} @Nullable @@ -72,7 +72,7 @@ private JaxRsJakartaCompatibility() {} @Nullable private static final Class JAKARTA_QUERY_PARAM = resolve("jakarta.ws.rs.QueryParam"); - public enum Annotations { + enum Annotations { CONSUMES(JAVAX_CONSUMES, JAKARTA_CONSUMES), PRODUCES(JAVAX_PRODUCES, JAKARTA_PRODUCES), PATH_PARAM(JAVAX_PATH_PARAM, JAKARTA_PATH_PARAM), @@ -94,16 +94,16 @@ public enum Annotations { this.jakarta = jakarta; } - public boolean matches(Class annotation) { + boolean matches(Class annotation) { return annotation == jakarta || annotation == javax; } - public boolean matches(Set> annotations) { + boolean matches(Set> annotations) { return annotations.contains(jakarta) || annotations.contains(javax); } @Nullable - public Annotation getAnnotation(AnnotatedElement element) { + Annotation getAnnotation(AnnotatedElement element) { if (jakarta != null) { Annotation annotation = element.getAnnotation(jakarta); if (annotation != null) { diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/MethodHandler.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/MethodHandler.java similarity index 94% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/MethodHandler.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/MethodHandler.java index cb639749a..e2a3cf5b8 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/MethodHandler.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/MethodHandler.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feign; +package com.palantir.conjure.java.client.jaxrs; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/MethodHeaderEnrichmentContract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/MethodHeaderEnrichmentContract.java similarity index 65% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/MethodHeaderEnrichmentContract.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/MethodHeaderEnrichmentContract.java index b03794a09..cfbf47b6d 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/MethodHeaderEnrichmentContract.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/MethodHeaderEnrichmentContract.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,8 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; -import com.palantir.conjure.java.client.jaxrs.feign.Contract; -import com.palantir.conjure.java.client.jaxrs.feign.MethodMetadata; import java.lang.reflect.Method; /** @@ -25,16 +23,16 @@ * * This should be considered internal API and should not be depended upon. */ -public final class MethodHeaderEnrichmentContract extends AbstractDelegatingContract { +final class MethodHeaderEnrichmentContract extends AbstractDelegatingContract { public static final String METHOD_HEADER = "dialogue-method"; - public MethodHeaderEnrichmentContract(Contract delegate) { + MethodHeaderEnrichmentContract(Contract delegate) { super(delegate); } @Override - protected void processMetadata(Class _targetType, Method method, MethodMetadata metadata) { + void processMetadata(Class _targetType, Method method, MethodMetadata metadata) { metadata.template().header(METHOD_HEADER, method.toString()); } } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/MethodMetadata.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/MethodMetadata.java similarity index 72% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/MethodMetadata.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/MethodMetadata.java index c41937f19..a9ee8964d 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/MethodMetadata.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/MethodMetadata.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feign; +package com.palantir.conjure.java.client.jaxrs; import java.lang.reflect.Type; import java.util.ArrayList; @@ -23,7 +23,7 @@ import java.util.Map; @SuppressWarnings("MissingSummary") -public final class MethodMetadata { +final class MethodMetadata { private String configKey; private transient Type returnType; @@ -46,65 +46,65 @@ public final class MethodMetadata { * @see Feign#configKey(Class, java.lang.reflect.Method) */ @SuppressWarnings("MissingSummary") - public String configKey() { + String configKey() { return configKey; } - public MethodMetadata configKey(String configKey) { + MethodMetadata configKey(String configKey) { this.configKey = configKey; return this; } - public Type returnType() { + Type returnType() { return returnType; } - public MethodMetadata returnType(Type returnType) { + MethodMetadata returnType(Type returnType) { this.returnType = returnType; return this; } - public Integer urlIndex() { + Integer urlIndex() { return urlIndex; } - public MethodMetadata urlIndex(Integer urlIndex) { + MethodMetadata urlIndex(Integer urlIndex) { this.urlIndex = urlIndex; return this; } - public Integer bodyIndex() { + Integer bodyIndex() { return bodyIndex; } - public MethodMetadata bodyIndex(Integer bodyIndex) { + MethodMetadata bodyIndex(Integer bodyIndex) { this.bodyIndex = bodyIndex; return this; } - public Integer headerMapIndex() { + Integer headerMapIndex() { return headerMapIndex; } - public MethodMetadata headerMapIndex(Integer headerMapIndex) { + MethodMetadata headerMapIndex(Integer headerMapIndex) { this.headerMapIndex = headerMapIndex; return this; } - public Integer queryMapIndex() { + Integer queryMapIndex() { return queryMapIndex; } - public MethodMetadata queryMapIndex(Integer queryMapIndex) { + MethodMetadata queryMapIndex(Integer queryMapIndex) { this.queryMapIndex = queryMapIndex; return this; } - public boolean queryMapEncoded() { + boolean queryMapEncoded() { return queryMapEncoded; } - public MethodMetadata queryMapEncoded(boolean queryMapEncoded) { + MethodMetadata queryMapEncoded(boolean queryMapEncoded) { this.queryMapEncoded = queryMapEncoded; return this; } @@ -112,31 +112,31 @@ public MethodMetadata queryMapEncoded(boolean queryMapEncoded) { /** * Type corresponding to {@link #bodyIndex()}. */ - public Type bodyType() { + Type bodyType() { return bodyType; } - public MethodMetadata bodyType(Type bodyType) { + MethodMetadata bodyType(Type bodyType) { this.bodyType = bodyType; return this; } - public RequestTemplate template() { + RequestTemplate template() { return template; } - public List formParams() { + List formParams() { return formParams; } - public Map> indexToName() { + Map> indexToName() { return indexToName; } /** * If {@link #indexToExpander} is null, classes here will be instantiated by newInstance. */ - public Map> indexToExpanderClass() { + Map> indexToExpanderClass() { return indexToExpanderClass; } @@ -144,7 +144,7 @@ public Map> indexToExpanderClass() { * After {@link #indexToExpanderClass} is populated, this is set by contracts that support * runtime injection. */ - public MethodMetadata indexToExpander(Map indexToExpander) { + MethodMetadata indexToExpander(Map indexToExpander) { this.indexToExpander = indexToExpander; return this; } @@ -152,7 +152,7 @@ public MethodMetadata indexToExpander(Map indexToExpander) { /** * When not null, this value will be used instead of {@link #indexToExpander()}. */ - public Map indexToExpander() { + Map indexToExpander() { return indexToExpander; } } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverReturnNullDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/NeverReturnNullDecoder.java similarity index 74% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverReturnNullDecoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/NeverReturnNullDecoder.java index 52285d538..f9f118bf6 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverReturnNullDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/NeverReturnNullDecoder.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,19 +14,17 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; -import com.palantir.conjure.java.client.jaxrs.feign.Response; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import com.palantir.logsafe.Preconditions; import com.palantir.logsafe.SafeArg; import java.io.IOException; import java.lang.reflect.Type; -public final class NeverReturnNullDecoder implements Decoder { +final class NeverReturnNullDecoder implements Decoder { private final Decoder delegate; - public NeverReturnNullDecoder(Decoder delegate) { + NeverReturnNullDecoder(Decoder delegate) { this.delegate = delegate; } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/QosErrorDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/QosErrorDecoder.java similarity index 79% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/QosErrorDecoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/QosErrorDecoder.java index 2552cfc81..d2b0d680b 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/QosErrorDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/QosErrorDecoder.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,20 +14,18 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import com.palantir.conjure.java.QosExceptionResponseMapper; -import com.palantir.conjure.java.client.jaxrs.feign.Response; -import com.palantir.conjure.java.client.jaxrs.feign.codec.ErrorDecoder; import java.util.Collection; import java.util.Optional; import java.util.function.Function; import java.util.stream.Stream; -public final class QosErrorDecoder implements ErrorDecoder { +final class QosErrorDecoder implements ErrorDecoder { private final ErrorDecoder delegate; - public QosErrorDecoder(ErrorDecoder delegate) { + QosErrorDecoder(ErrorDecoder delegate) { this.delegate = delegate; } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/RawTypes.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/RawTypes.java similarity index 88% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/RawTypes.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/RawTypes.java index 266aa4535..c4edc7e30 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/RawTypes.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/RawTypes.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import com.google.common.reflect.TypeToken; import java.lang.reflect.Type; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/ReflectiveFeign.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/ReflectiveFeign.java similarity index 92% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/ReflectiveFeign.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/ReflectiveFeign.java index 9a357f03e..c1139d9b6 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/ReflectiveFeign.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/ReflectiveFeign.java @@ -13,15 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feign; +package com.palantir.conjure.java.client.jaxrs; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkArgument; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkState; +import static com.palantir.conjure.java.client.jaxrs.Util.checkArgument; +import static com.palantir.conjure.java.client.jaxrs.Util.checkNotNull; +import static com.palantir.conjure.java.client.jaxrs.Util.checkState; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; -import com.palantir.conjure.java.client.jaxrs.feign.codec.ErrorDecoder; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; @@ -34,7 +31,7 @@ import java.util.Map; import java.util.Map.Entry; -public class ReflectiveFeign extends Feign { +final class ReflectiveFeign extends Feign { private final ParseHandlersByName targetToHandlersByName; @@ -48,7 +45,7 @@ public class ReflectiveFeign extends Feign { */ @Override @SuppressWarnings({"ProxyNonConstantType", "RedundantControlFlow"}) - public T newInstance(Target target) { + T newInstance(Target target) { Map nameToHandler = targetToHandlersByName.apply(target); Map methodToHandler = new HashMap<>(); List defaultMethodHandlers = new ArrayList<>(); @@ -141,7 +138,7 @@ static final class ParseHandlersByName { this.decoder = checkNotNull(decoder, "decoder"); } - public Map apply(Target key) { + Map apply(Target key) { List metadata = contract.parseAndValidateMetadata(key.type()); Map result = new LinkedHashMap(); for (MethodMetadata md : metadata) { @@ -161,7 +158,7 @@ public Map apply(Target key) { private static class BuildTemplateByResolvingArgs implements RequestTemplate.Factory { - protected final MethodMetadata metadata; + final MethodMetadata metadata; private final Map indexToExpander = new LinkedHashMap(); private BuildTemplateByResolvingArgs(MethodMetadata metadata) { @@ -294,7 +291,7 @@ private RequestTemplate addQueryMapQueryParameters(Object[] argv, RequestTemplat return mutable; } - protected RequestTemplate resolve(Object[] _argv, RequestTemplate mutable, Map variables) { + RequestTemplate resolve(Object[] _argv, RequestTemplate mutable, Map variables) { return mutable.resolve(variables); } } @@ -309,7 +306,7 @@ private BuildFormEncodedTemplateFromArgs(MethodMetadata metadata, Encoder encode } @Override - protected RequestTemplate resolve(Object[] argv, RequestTemplate mutable, Map variables) { + RequestTemplate resolve(Object[] argv, RequestTemplate mutable, Map variables) { Map formVariables = new LinkedHashMap(); for (Entry entry : variables.entrySet()) { if (metadata.formParams().contains(entry.getKey())) { @@ -331,7 +328,7 @@ private BuildEncodedTemplateFromArgs(MethodMetadata metadata, Encoder encoder) { } @Override - protected RequestTemplate resolve(Object[] argv, RequestTemplate mutable, Map variables) { + RequestTemplate resolve(Object[] argv, RequestTemplate mutable, Map variables) { Object body = argv[metadata.bodyIndex()]; checkArgument(body != null, "Body parameter %s was null", metadata.bodyIndex()); encoder.encode(body, metadata.bodyType(), mutable); diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Request.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Request.java similarity index 83% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Request.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Request.java index 7771ae1e3..c25085030 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Request.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Request.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feign; +package com.palantir.conjure.java.client.jaxrs; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.valuesOrEmpty; +import static com.palantir.conjure.java.client.jaxrs.Util.checkNotNull; +import static com.palantir.conjure.java.client.jaxrs.Util.valuesOrEmpty; import java.nio.charset.StandardCharsets; import java.util.Collection; @@ -25,13 +25,13 @@ /** * An immutable request to an http server. */ -public final class Request { +final class Request { /** * No parameters can be null except {@code body} and {@code charset}. All parameters must be * effectively immutable, via safe copies, not mutating or otherwise. */ - public static Request create(String method, String url, Map> headers, byte[] body) { + static Request create(String method, String url, Map> headers, byte[] body) { return new Request(method, url, headers, body); } @@ -48,17 +48,17 @@ public static Request create(String method, String url, Map> headers() { + Map> headers() { return headers; } @@ -66,7 +66,7 @@ public Map> headers() { * If present, this is the replayable body to send to the server. In some cases, this may be * interpretable as text. */ - public byte[] body() { + byte[] body() { return body; } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestTemplate.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/RequestTemplate.java similarity index 90% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestTemplate.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/RequestTemplate.java index 6d8f125d4..c418a6414 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/RequestTemplate.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/RequestTemplate.java @@ -13,17 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feign; +package com.palantir.conjure.java.client.jaxrs; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.CONTENT_LENGTH; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.UTF_8; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkArgument; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.emptyToNull; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.toArray; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.valuesOrEmpty; +import static com.palantir.conjure.java.client.jaxrs.Util.CONTENT_LENGTH; +import static com.palantir.conjure.java.client.jaxrs.Util.UTF_8; +import static com.palantir.conjure.java.client.jaxrs.Util.checkArgument; +import static com.palantir.conjure.java.client.jaxrs.Util.checkNotNull; +import static com.palantir.conjure.java.client.jaxrs.Util.emptyToNull; +import static com.palantir.conjure.java.client.jaxrs.Util.toArray; +import static com.palantir.conjure.java.client.jaxrs.Util.valuesOrEmpty; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; import java.io.UncheckedIOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; @@ -45,7 +44,7 @@ * this object is mutable, so needs to be guarded with the copy constructor. */ @SuppressWarnings("MissingSummary") -public final class RequestTemplate { +final class RequestTemplate { private final Map> queries = new LinkedHashMap>(); private final Map> headers = new LinkedHashMap>(); @@ -56,10 +55,10 @@ public final class RequestTemplate { private byte[] body; private boolean decodeSlash = true; - public RequestTemplate() {} + RequestTemplate() {} /** Copy constructor. Use this when making templates. */ - public RequestTemplate(RequestTemplate toCopy) { + RequestTemplate(RequestTemplate toCopy) { checkNotNull(toCopy, "toCopy"); this.method = toCopy.method; this.url.append(toCopy.url); @@ -95,7 +94,7 @@ private static String urlEncode(Object arg) { * @param variables to the URI template * @return expanded template, leaving any unresolved parameters literal */ - public static String expand(String template, Map variables) { + static String expand(String template, Map variables) { // skip expansion if there's no valid variables set. ex. {a} is the // first valid if (checkNotNull(template, "template").length() < 3) { @@ -190,7 +189,7 @@ private static void putKV(String stringToParse, Map> * similar to {@code javax.ws.rs.client.WebTarget.resolveTemplates(templateValues, true)} , except * that the template values apply to any part of the request, not just the URL */ - public RequestTemplate resolve(Map unencoded) { + RequestTemplate resolve(Map unencoded) { replaceQueryValues(unencoded); Map encoded = new LinkedHashMap(); for (Entry entry : unencoded.entrySet()) { @@ -217,42 +216,42 @@ public RequestTemplate resolve(Map unencoded) { } /** roughly analogous to {@code javax.ws.rs.client.Target.request()}. */ - public Request request() { + Request request() { Map> safeCopy = new LinkedHashMap>(); safeCopy.putAll(headers); return Request.create(method, url + queryLine(), Collections.unmodifiableMap(safeCopy), body); } /** @see Request#method() */ - public RequestTemplate method(String method) { + RequestTemplate method(String method) { this.method = checkNotNull(method, "method"); checkArgument(method.matches("^[A-Z]+$"), "Invalid HTTP Method: %s", method); return this; } /** @see Request#method() */ - public String method() { + String method() { return method; } - public RequestTemplate decodeSlash(boolean decodeSlash) { + RequestTemplate decodeSlash(boolean decodeSlash) { this.decodeSlash = decodeSlash; return this; } - public boolean decodeSlash() { + boolean decodeSlash() { return decodeSlash; } /** @see #url() */ - public RequestTemplate append(String value) { + RequestTemplate append(String value) { url.append(value); url = pullAnyQueriesOutOfUrl(url); return this; } /** @see #url() */ - public RequestTemplate insert(int pos, String value) { + RequestTemplate insert(int pos, String value) { if (value.startsWith("http")) { if (value.endsWith("/")) { value = value.substring(0, value.length() - 1); @@ -265,7 +264,7 @@ public RequestTemplate insert(int pos, String value) { return this; } - public String url() { + String url() { return url.toString(); } @@ -293,12 +292,12 @@ public String url() { * to be null. * @see #queries() */ - public RequestTemplate query(boolean encoded, String name, String... values) { + RequestTemplate query(boolean encoded, String name, String... values) { return doQuery(encoded, name, values); } /** @see #query(boolean, String, String...) */ - public RequestTemplate query(boolean encoded, String name, Iterable values) { + RequestTemplate query(boolean encoded, String name, Iterable values) { return doQuery(encoded, name, values); } @@ -306,7 +305,7 @@ public RequestTemplate query(boolean encoded, String name, Iterable valu * Shortcut for {@code query(false, String, String...)} * @see #query(boolean, String, String...) */ - public RequestTemplate query(String name, String... values) { + RequestTemplate query(String name, String... values) { return doQuery(false, name, values); } @@ -314,7 +313,7 @@ public RequestTemplate query(String name, String... values) { * Shortcut for {@code query(false, String, Iterable)} * @see #query(boolean, String, String...) */ - public RequestTemplate query(String name, Iterable values) { + RequestTemplate query(String name, Iterable values) { return doQuery(false, name, values); } @@ -357,7 +356,7 @@ private static String encodeIfNotVariable(String in) { * @param queries if null, remove all queries. else value to replace all queries with. * @see #queries() */ - public RequestTemplate queries(Map> queries) { + RequestTemplate queries(Map> queries) { if (queries == null || queries.isEmpty()) { this.queries.clear(); } else { @@ -373,7 +372,7 @@ public RequestTemplate queries(Map> queries) { * * @see Request#url() */ - public Map> queries() { + Map> queries() { Map> decoded = new LinkedHashMap>(); for (String field : queries.keySet()) { Collection decodedValues = new ArrayList(); @@ -404,7 +403,7 @@ public Map> queries() { * be null. * @see #headers() */ - public RequestTemplate header(String name, String... values) { + RequestTemplate header(String name, String... values) { checkNotNull(name, "header name"); if (values == null || (values.length == 1 && values[0] == null)) { headers.remove(name); @@ -417,7 +416,7 @@ public RequestTemplate header(String name, String... values) { } /** @see #header(String, String...) */ - public RequestTemplate header(String name, Iterable values) { + RequestTemplate header(String name, Iterable values) { if (values != null) { return header(name, toArray(values, String.class)); } @@ -435,7 +434,7 @@ public RequestTemplate header(String name, Iterable values) { * @param headers if null, remove all headers. else value to replace all headers with. * @see #headers() */ - public RequestTemplate headers(Map> headers) { + RequestTemplate headers(Map> headers) { if (headers == null || headers.isEmpty()) { this.headers.clear(); } else { @@ -449,7 +448,7 @@ public RequestTemplate headers(Map> headers) { * * @see Request#headers() */ - public Map> headers() { + Map> headers() { return Collections.unmodifiableMap(headers); } @@ -459,7 +458,7 @@ public Map> headers() { * * @see Request#body() */ - public RequestTemplate body(byte[] bodyData) { + RequestTemplate body(byte[] bodyData) { this.body = bodyData; int bodyLength = bodyData != null ? bodyData.length : 0; header(CONTENT_LENGTH, String.valueOf(bodyLength)); @@ -469,7 +468,7 @@ public RequestTemplate body(byte[] bodyData) { /** * @see Request#body() */ - public byte[] body() { + byte[] body() { return body; } @@ -528,7 +527,7 @@ public String toString() { * Replaces query values which are templated with corresponding values from the {@code unencoded} * map. Any unresolved queries are removed. */ - public void replaceQueryValues(Map unencoded) { + void replaceQueryValues(Map unencoded) { Iterator>> iterator = queries.entrySet().iterator(); while (iterator.hasNext()) { @@ -563,7 +562,7 @@ public void replaceQueryValues(Map unencoded) { } } - public String queryLine() { + String queryLine() { if (queries.isEmpty()) { return ""; } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Response.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Response.java similarity index 87% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Response.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Response.java index e5342fd2a..abb14f99c 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Response.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Response.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feign; +package com.palantir.conjure.java.client.jaxrs; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.UTF_8; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkState; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.decodeOrDefault; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.valuesOrEmpty; +import static com.palantir.conjure.java.client.jaxrs.Util.UTF_8; +import static com.palantir.conjure.java.client.jaxrs.Util.checkNotNull; +import static com.palantir.conjure.java.client.jaxrs.Util.checkState; +import static com.palantir.conjure.java.client.jaxrs.Util.decodeOrDefault; +import static com.palantir.conjure.java.client.jaxrs.Util.valuesOrEmpty; import java.io.ByteArrayInputStream; import java.io.Closeable; @@ -31,7 +31,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.LinkedList; import java.util.Locale; import java.util.Map; import java.util.TreeMap; @@ -39,7 +38,7 @@ /** * An immutable response to an http invocation which only returns string content. */ -public final class Response implements Closeable { +final class Response implements Closeable { private final int status; private final String reason; @@ -54,7 +53,7 @@ private Response(int status, String reason, Map> head this.body = body; // nullable } - public static Response create( + static Response create( int status, String reason, Map> headers, @@ -63,16 +62,16 @@ public static Response create( return new Response(status, reason, headers, InputStreamBody.orNull(inputStream, length)); } - public static Response create(int status, String reason, Map> headers, byte[] data) { + static Response create(int status, String reason, Map> headers, byte[] data) { return new Response(status, reason, headers, ByteArrayBody.orNull(data)); } - public static Response create( + static Response create( int status, String reason, Map> headers, String text, Charset charset) { return new Response(status, reason, headers, ByteArrayBody.orNull(text, charset)); } - public static Response create(int status, String reason, Map> headers, Body body) { + static Response create(int status, String reason, Map> headers, Body body) { return new Response(status, reason, headers, body); } @@ -81,7 +80,7 @@ public static Response create(int status, String reason, Maprfc2616 */ - public int status() { + int status() { return status; } @@ -90,21 +89,21 @@ public int status() { * * See https://github.com/http2/http2-spec/issues/202 */ - public String reason() { + String reason() { return reason; } /** * Returns a case-insensitive mapping of header names to their values. */ - public Map> headers() { + Map> headers() { return headers; } /** * if present, the response had a body */ - public Body body() { + Body body() { return body; } @@ -127,7 +126,7 @@ public void close() { Util.ensureClosed(body); } - public interface Body extends Closeable { + interface Body extends Closeable { /** * length in bytes, if known. Null if unknown or greater than {@link Integer#MAX_VALUE}. @@ -200,7 +199,7 @@ private static final class ByteArrayBody implements Body { private final byte[] data; - public ByteArrayBody(byte[] data) { + ByteArrayBody(byte[] data) { this.data = data; } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/SlashEncodingContract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/SlashEncodingContract.java similarity index 62% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/SlashEncodingContract.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/SlashEncodingContract.java index 254c387f1..d3e433e90 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/SlashEncodingContract.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/SlashEncodingContract.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,21 +14,19 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; -import com.palantir.conjure.java.client.jaxrs.feign.Contract; -import com.palantir.conjure.java.client.jaxrs.feign.MethodMetadata; import java.lang.reflect.Method; /** Decorates a {@link Contract} and forces slashes to be encoded when they are part of a URL. */ -public final class SlashEncodingContract extends AbstractDelegatingContract { +final class SlashEncodingContract extends AbstractDelegatingContract { - public SlashEncodingContract(Contract delegate) { + SlashEncodingContract(Contract delegate) { super(delegate); } @Override - protected void processMetadata(Class _targetType, Method _method, MethodMetadata metadata) { + void processMetadata(Class _targetType, Method _method, MethodMetadata metadata) { metadata.template().decodeSlash(false); } } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/SynchronousMethodHandler.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/SynchronousMethodHandler.java similarity index 91% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/SynchronousMethodHandler.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/SynchronousMethodHandler.java index 67f46c74c..e5cc91100 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/SynchronousMethodHandler.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/SynchronousMethodHandler.java @@ -13,13 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feign; +package com.palantir.conjure.java.client.jaxrs; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.checkNotNull; -import static com.palantir.conjure.java.client.jaxrs.feign.Util.ensureClosed; - -import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; -import com.palantir.conjure.java.client.jaxrs.feign.codec.ErrorDecoder; +import static com.palantir.conjure.java.client.jaxrs.Util.checkNotNull; +import static com.palantir.conjure.java.client.jaxrs.Util.ensureClosed; final class SynchronousMethodHandler implements MethodHandler { diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Target.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Target.java similarity index 97% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Target.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Target.java index 18e67460a..a2472f56c 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Target.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Target.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feign; +package com.palantir.conjure.java.client.jaxrs; /** *

relationship to JAXRS 2.0

Similar to {@code diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateDecoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/TextDelegateDecoder.java similarity index 83% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateDecoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/TextDelegateDecoder.java index 6e4c14c24..272931cac 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateDecoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/TextDelegateDecoder.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,14 +14,11 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.net.HttpHeaders; -import com.palantir.conjure.java.client.jaxrs.feign.Response; -import com.palantir.conjure.java.client.jaxrs.feign.Util; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import com.palantir.logsafe.SafeArg; import com.palantir.logsafe.exceptions.SafeRuntimeException; import java.io.IOException; @@ -32,11 +29,11 @@ * Decodes the response as a string if the response has a Content-Type of text/plain, or falls back to the given * delegate otherwise. */ -public final class TextDelegateDecoder implements Decoder { +final class TextDelegateDecoder implements Decoder { private final Decoder delegate; - public TextDelegateDecoder(Decoder delegate) { + TextDelegateDecoder(Decoder delegate) { this.delegate = delegate; } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateEncoder.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/TextDelegateEncoder.java similarity index 85% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateEncoder.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/TextDelegateEncoder.java index 56d7df42f..adeda7695 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateEncoder.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/TextDelegateEncoder.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,11 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.net.HttpHeaders; -import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; import com.palantir.logsafe.SafeArg; import com.palantir.logsafe.exceptions.SafeRuntimeException; import java.lang.reflect.Type; @@ -31,11 +29,11 @@ * Encodes the value as a string if the request has a Content-Type of text/plain, or falls back to the given delegate * otherwise. */ -public final class TextDelegateEncoder implements Encoder { +final class TextDelegateEncoder implements Encoder { private final Encoder delegate; - public TextDelegateEncoder(Encoder delegate) { + TextDelegateEncoder(Encoder delegate) { this.delegate = delegate; } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Types.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Types.java similarity index 99% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Types.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Types.java index 5eb985271..cae7b8896 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Types.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Types.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feign; +package com.palantir.conjure.java.client.jaxrs; import java.lang.reflect.Array; import java.lang.reflect.GenericArrayType; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Util.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Util.java similarity index 86% rename from conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Util.java rename to conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Util.java index 6b4bcc4e0..fcfe0009e 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feign/Util.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Util.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feign; +package com.palantir.conjure.java.client.jaxrs; import static java.lang.String.format; @@ -45,23 +45,23 @@ /** * Utilities, typically copied in from guava, so as to avoid dependency conflicts. */ -public final class Util { +final class Util { /** * The HTTP Content-Length header field name. */ - public static final String CONTENT_LENGTH = "Content-Length"; + static final String CONTENT_LENGTH = "Content-Length"; /** * UTF-8: eight-bit UCS Transformation Format. */ - public static final Charset UTF_8 = Charset.forName("UTF-8"); + static final Charset UTF_8 = Charset.forName("UTF-8"); private static final int BUF_SIZE = 0x800; // 2K chars (4K bytes) /** * Type literal for {@code Map}. */ - public static final Type MAP_STRING_WILDCARD = new Types.ParameterizedTypeImpl( + static final Type MAP_STRING_WILDCARD = new Types.ParameterizedTypeImpl( null, Map.class, String.class, new Types.WildcardTypeImpl(new Type[] {Object.class}, new Type[0])); private Util() { // no instances @@ -71,7 +71,7 @@ private Util() { // no instances * Copy of {@code com.google.common.base.Preconditions#checkArgument}. */ @FormatMethod - public static void checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { + static void checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { if (!expression) { throw new IllegalArgumentException(format(errorMessageTemplate, errorMessageArgs)); } @@ -81,7 +81,7 @@ public static void checkArgument(boolean expression, String errorMessageTemplate * Copy of {@code com.google.common.base.Preconditions#checkNotNull}. */ @FormatMethod - public static T checkNotNull(T reference, String errorMessageTemplate, Object... errorMessageArgs) { + static T checkNotNull(T reference, String errorMessageTemplate, Object... errorMessageArgs) { if (reference == null) { // If either of these parameters is null, the right thing happens anyway throw new NullPointerException(format(errorMessageTemplate, errorMessageArgs)); @@ -93,7 +93,7 @@ public static T checkNotNull(T reference, String errorMessageTemplate, Objec * Copy of {@code com.google.common.base.Preconditions#checkState}. */ @FormatMethod - public static void checkState(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { + static void checkState(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { if (!expression) { throw new IllegalStateException(format(errorMessageTemplate, errorMessageArgs)); } @@ -102,8 +102,8 @@ public static void checkState(boolean expression, String errorMessageTemplate, O /** * Identifies a method as a default instance method. */ - public static boolean isDefault(Method method) { - // Default methods are public non-abstract, non-synthetic, and non-static instance methods + static boolean isDefault(Method method) { + // Default methods are non-abstract, non-synthetic, and non-static instance methods // declared in an interface. // method.isDefault() is not sufficient for our usage as it does not check // for synthetic methods. As a result, it picks up overridden methods as well as actual default methods. @@ -116,14 +116,14 @@ public static boolean isDefault(Method method) { /** * Adapted from {@code com.google.common.base.Strings#emptyToNull}. */ - public static String emptyToNull(String string) { + static String emptyToNull(String string) { return string == null || string.isEmpty() ? null : string; } /** * Adapted from {@code com.google.common.base.Strings#emptyToNull}. */ - public static T[] toArray(Iterable iterable, Class type) { + static T[] toArray(Iterable iterable, Class type) { Collection collection; if (iterable instanceof Collection) { collection = (Collection) iterable; @@ -140,11 +140,11 @@ public static T[] toArray(Iterable iterable, Class type) { /** * Returns an unmodifiable collection which may be empty, but is never null. */ - public static Collection valuesOrEmpty(Map> map, String key) { + static Collection valuesOrEmpty(Map> map, String key) { return map.containsKey(key) && map.get(key) != null ? map.get(key) : Collections.emptyList(); } - public static void ensureClosed(Closeable closeable) { + static void ensureClosed(Closeable closeable) { if (closeable != null) { try { closeable.close(); @@ -171,7 +171,7 @@ public static void ensureClosed(Closeable closeable) { * decoders a default empty value for a type. This method cheaply supports typical types by only * looking at the raw type (vs type hierarchy). Decorate for sophistication. */ - public static Object emptyValueOf(Type type) { + static Object emptyValueOf(Type type) { return EMPTIES.get(Types.getRawType(type)); } @@ -210,7 +210,7 @@ public void remove() { /** * Adapted from {@code com.google.common.io.CharStreams.toString()}. */ - public static String toString(Reader reader) throws IOException { + static String toString(Reader reader) throws IOException { if (reader == null) { return null; } @@ -231,7 +231,7 @@ public static String toString(Reader reader) throws IOException { /** * Adapted from {@code com.google.common.io.ByteStreams.toByteArray()}. */ - public static byte[] toByteArray(InputStream in) throws IOException { + static byte[] toByteArray(InputStream in) throws IOException { checkNotNull(in, "in"); try { ByteArrayOutputStream out = new ByteArrayOutputStream(); diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/BackoffStrategy.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/BackoffStrategy.java deleted file mode 100644 index 2775c8531..000000000 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/BackoffStrategy.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.palantir.conjure.java.client.jaxrs.feignimpl; - -/** Defines a strategy for waiting in between successive retries of an operation that is subject to failure. */ -public interface BackoffStrategy { - /** - * Blocks for an implementation-defined period of time and returns true iff the backed-off operation should be - * attempted again. - */ - boolean backoff(int numFailedAttempts); -} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverRetryingBackoffStrategy.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverRetryingBackoffStrategy.java deleted file mode 100644 index b06a84a52..000000000 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverRetryingBackoffStrategy.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.palantir.conjure.java.client.jaxrs.feignimpl; - -/** A {@link BackoffStrategy} that attempts the operation exactly once, i.e., returns false always. */ -public enum NeverRetryingBackoffStrategy implements BackoffStrategy { - INSTANCE; - - @Override - public boolean backoff(int _numFailedAttempts) { - return false; - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/PathTemplateHeaderEnrichmentContract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/PathTemplateHeaderEnrichmentContract.java deleted file mode 100644 index 963d57f8e..000000000 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/feignimpl/PathTemplateHeaderEnrichmentContract.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.palantir.conjure.java.client.jaxrs.feignimpl; - -import com.palantir.conjure.java.client.jaxrs.feign.Contract; -import com.palantir.conjure.java.client.jaxrs.feign.MethodMetadata; -import java.lang.reflect.Method; - -public final class PathTemplateHeaderEnrichmentContract extends AbstractDelegatingContract { - private static final String PATH_TEMPLATE_HEADER = "hr-path-template"; - /** - * No longer used. - * - * @deprecated no longer used - */ - @Deprecated - public static final char OPEN_BRACE_REPLACEMENT = '\0'; - /** - * No longer used. - * - * @deprecated no longer used - */ - @Deprecated - public static final char CLOSE_BRACE_REPLACEMENT = '\1'; - - public PathTemplateHeaderEnrichmentContract(Contract delegate) { - super(delegate); - } - - @Override - protected void processMetadata(Class _targetType, Method _method, MethodMetadata metadata) { - metadata.template() - .header( - PATH_TEMPLATE_HEADER, - metadata.template().method() - + " " - + metadata.template() - .url() - // escape from feign string interpolation - // See RequestTemplate.expand - .replace("{", "{{")); - } -} diff --git a/conjure-java-jaxrs-client/src/main/metrics/dangerous-buffering.yml b/conjure-java-jaxrs-client/src/main/metrics/dangerous-buffering.yml index e4a0b5ba7..cfcc43941 100644 --- a/conjure-java-jaxrs-client/src/main/metrics/dangerous-buffering.yml +++ b/conjure-java-jaxrs-client/src/main/metrics/dangerous-buffering.yml @@ -1,5 +1,5 @@ options: - javaPackage: com.palantir.conjure.java.client.jaxrs.feignimpl + javaPackage: com.palantir.conjure.java.client.jaxrs javaVisibility: packagePrivate namespaces: feign.client: diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CollectionDefaultDecodingTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/CollectionDefaultDecodingTest.java similarity index 88% rename from conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CollectionDefaultDecodingTest.java rename to conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/CollectionDefaultDecodingTest.java index b0dec49da..0565a7ab6 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/CollectionDefaultDecodingTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/CollectionDefaultDecodingTest.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,12 +14,10 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import static org.assertj.core.api.Assertions.assertThat; -import com.palantir.conjure.java.client.jaxrs.JaxRsClient; -import com.palantir.conjure.java.client.jaxrs.TestBase; import com.palantir.conjure.java.okhttp.HostMetricsRegistry; import com.palantir.undertest.UndertowServerExtension; import org.junit.jupiter.api.BeforeEach; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/EmptyContainerDecoderTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/EmptyContainerDecoderTest.java similarity index 97% rename from conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/EmptyContainerDecoderTest.java rename to conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/EmptyContainerDecoderTest.java index e1fa9b9d4..88696b163 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/EmptyContainerDecoderTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/EmptyContainerDecoderTest.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -30,8 +30,6 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.net.HttpHeaders; -import com.palantir.conjure.java.client.jaxrs.feign.Response; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import com.palantir.conjure.java.serialization.ObjectMappers; import jakarta.ws.rs.core.MediaType; import java.io.IOException; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalAwareDecoderTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/GuavaOptionalAwareDecoderTest.java similarity index 96% rename from conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalAwareDecoderTest.java rename to conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/GuavaOptionalAwareDecoderTest.java index b378904b6..ee73e8ec0 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalAwareDecoderTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/GuavaOptionalAwareDecoderTest.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,15 +14,13 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.google.common.collect.ImmutableMap; import com.palantir.conjure.java.api.errors.RemoteException; -import com.palantir.conjure.java.client.jaxrs.JaxRsClient; -import com.palantir.conjure.java.client.jaxrs.TestBase; import com.palantir.conjure.java.okhttp.HostMetricsRegistry; import com.palantir.undertest.UndertowServerExtension; import java.nio.file.Paths; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalComplexType.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/GuavaOptionalComplexType.java similarity index 95% rename from conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalComplexType.java rename to conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/GuavaOptionalComplexType.java index 65650e7ee..632d60ffa 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaOptionalComplexType.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/GuavaOptionalComplexType.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaTestServer.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/GuavaTestServer.java similarity index 98% rename from conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaTestServer.java rename to conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/GuavaTestServer.java index f71b58489..dfa5327ca 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/GuavaTestServer.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/GuavaTestServer.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import com.google.common.collect.ImmutableMap; import com.palantir.conjure.java.server.jersey.ConjureJerseyFeature; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/HeaderAccessUtilsTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/HeaderAccessUtilsTest.java similarity index 94% rename from conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/HeaderAccessUtilsTest.java rename to conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/HeaderAccessUtilsTest.java index 7916e7eaf..b353512f5 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/HeaderAccessUtilsTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/HeaderAccessUtilsTest.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import static org.assertj.core.api.Assertions.assertThat; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateDecoderTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/InputStreamDelegateDecoderTest.java similarity index 91% rename from conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateDecoderTest.java rename to conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/InputStreamDelegateDecoderTest.java index f7992f6a5..6dd7281bd 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateDecoderTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/InputStreamDelegateDecoderTest.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,17 +14,13 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableMap; -import com.palantir.conjure.java.client.jaxrs.JaxRsClient; -import com.palantir.conjure.java.client.jaxrs.TestBase; -import com.palantir.conjure.java.client.jaxrs.feign.Response; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import com.palantir.conjure.java.okhttp.HostMetricsRegistry; import com.palantir.undertest.UndertowServerExtension; import java.io.IOException; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateEncoderTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/InputStreamDelegateEncoderTest.java similarity index 88% rename from conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateEncoderTest.java rename to conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/InputStreamDelegateEncoderTest.java index 6078df73c..8707c1fa7 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/InputStreamDelegateEncoderTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/InputStreamDelegateEncoderTest.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,15 +14,11 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.verify; -import com.palantir.conjure.java.client.jaxrs.JaxRsClient; -import com.palantir.conjure.java.client.jaxrs.TestBase; -import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; import com.palantir.conjure.java.okhttp.HostMetricsRegistry; import com.palantir.undertest.UndertowServerExtension; import java.io.ByteArrayInputStream; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8ComplexType.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/Java8ComplexType.java similarity index 95% rename from conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8ComplexType.java rename to conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/Java8ComplexType.java index 9a19dec6e..d71a26d8f 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8ComplexType.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/Java8ComplexType.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareDecoderTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/Java8OptionalAwareDecoderTest.java similarity index 93% rename from conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareDecoderTest.java rename to conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/Java8OptionalAwareDecoderTest.java index c6ee2cbe4..6d394de50 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8OptionalAwareDecoderTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/Java8OptionalAwareDecoderTest.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,16 +14,14 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.google.common.collect.ImmutableMap; import com.palantir.conjure.java.api.errors.RemoteException; -import com.palantir.conjure.java.client.jaxrs.JaxRsClient; -import com.palantir.conjure.java.client.jaxrs.TestBase; -import com.palantir.conjure.java.client.jaxrs.feignimpl.Java8TestServer.TestService; +import com.palantir.conjure.java.client.jaxrs.Java8TestServer.TestService; import com.palantir.conjure.java.okhttp.HostMetricsRegistry; import com.palantir.undertest.UndertowServerExtension; import java.nio.file.Paths; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8TestServer.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/Java8TestServer.java similarity index 98% rename from conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8TestServer.java rename to conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/Java8TestServer.java index fa1639117..709b5e1bb 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/Java8TestServer.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/Java8TestServer.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverReturnNullDecoderTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/NeverReturnNullDecoderTest.java similarity index 90% rename from conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverReturnNullDecoderTest.java rename to conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/NeverReturnNullDecoderTest.java index 7f647d306..2a6572e26 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/NeverReturnNullDecoderTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/NeverReturnNullDecoderTest.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,14 +14,11 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import static org.assertj.core.api.Assertions.assertThat; import com.google.common.collect.ImmutableList; -import com.palantir.conjure.java.client.jaxrs.TestBase; -import com.palantir.conjure.java.client.jaxrs.feign.Response; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import com.palantir.conjure.java.serialization.ObjectMappers; import com.palantir.logsafe.SafeArg; import com.palantir.logsafe.testing.Assertions; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/QosErrorDecoderTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/QosErrorDecoderTest.java similarity index 96% rename from conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/QosErrorDecoderTest.java rename to conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/QosErrorDecoderTest.java index 66ee00a73..3b56ad29c 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/QosErrorDecoderTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/QosErrorDecoderTest.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import static org.assertj.core.api.Assertions.assertThat; @@ -28,7 +28,6 @@ import com.palantir.conjure.java.api.errors.QosReason.DueTo; import com.palantir.conjure.java.api.errors.QosReason.RetryHint; import com.palantir.conjure.java.api.errors.QosReasons; -import com.palantir.conjure.java.client.jaxrs.feign.Response; import jakarta.ws.rs.core.HttpHeaders; import java.time.Duration; import java.util.Collection; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/SlashEncodingContractTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/SlashEncodingContractTest.java similarity index 92% rename from conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/SlashEncodingContractTest.java rename to conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/SlashEncodingContractTest.java index cba7e736d..3f9004ec7 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/SlashEncodingContractTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/SlashEncodingContractTest.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -22,10 +22,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import com.palantir.conjure.java.client.jaxrs.ExtensionsWrapper; import com.palantir.conjure.java.client.jaxrs.ExtensionsWrapper.BeforeAndAfter; -import com.palantir.conjure.java.client.jaxrs.JaxRsClient; -import com.palantir.conjure.java.client.jaxrs.TestBase; import com.palantir.conjure.java.okhttp.HostMetricsRegistry; import com.palantir.undertest.UndertowServerExtension; import jakarta.ws.rs.GET; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateDecoderTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/TextDelegateDecoderTest.java similarity index 94% rename from conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateDecoderTest.java rename to conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/TextDelegateDecoderTest.java index 88606db25..9912cd68a 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateDecoderTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/TextDelegateDecoderTest.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -26,10 +26,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.net.HttpHeaders; -import com.palantir.conjure.java.client.jaxrs.JaxRsClient; -import com.palantir.conjure.java.client.jaxrs.TestBase; -import com.palantir.conjure.java.client.jaxrs.feign.Response; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Decoder; import com.palantir.conjure.java.okhttp.HostMetricsRegistry; import com.palantir.undertest.UndertowServerExtension; import jakarta.ws.rs.core.MediaType; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateEncoderTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/TextDelegateEncoderTest.java similarity index 91% rename from conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateEncoderTest.java rename to conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/TextDelegateEncoderTest.java index 87f78d800..17576353a 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextDelegateEncoderTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/TextDelegateEncoderTest.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,15 +14,13 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import com.google.common.net.HttpHeaders; -import com.palantir.conjure.java.client.jaxrs.feign.RequestTemplate; -import com.palantir.conjure.java.client.jaxrs.feign.codec.Encoder; import jakarta.ws.rs.core.MediaType; import java.util.Arrays; import java.util.Collection; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextEncoderTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/TextEncoderTest.java similarity index 88% rename from conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextEncoderTest.java rename to conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/TextEncoderTest.java index aad1f466c..c5d773f09 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/TextEncoderTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/TextEncoderTest.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,14 +14,11 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import static org.assertj.core.api.Assertions.assertThat; -import com.palantir.conjure.java.client.jaxrs.ExtensionsWrapper; import com.palantir.conjure.java.client.jaxrs.ExtensionsWrapper.BeforeAndAfter; -import com.palantir.conjure.java.client.jaxrs.JaxRsClient; -import com.palantir.conjure.java.client.jaxrs.TestBase; import com.palantir.conjure.java.okhttp.HostMetricsRegistry; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.POST; diff --git a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/UserAgentTest.java b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/UserAgentTest.java similarity index 90% rename from conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/UserAgentTest.java rename to conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/UserAgentTest.java index c44dba7df..672380626 100644 --- a/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/feignimpl/UserAgentTest.java +++ b/conjure-java-jaxrs-client/src/test/java/com/palantir/conjure/java/client/jaxrs/UserAgentTest.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,18 +14,14 @@ * limitations under the License. */ -package com.palantir.conjure.java.client.jaxrs.feignimpl; +package com.palantir.conjure.java.client.jaxrs; import static org.assertj.core.api.Assertions.assertThat; import com.google.common.base.MoreObjects; import com.palantir.conjure.java.api.config.service.UserAgent; import com.palantir.conjure.java.api.config.service.UserAgents; -import com.palantir.conjure.java.client.jaxrs.ExtensionsWrapper; import com.palantir.conjure.java.client.jaxrs.ExtensionsWrapper.BeforeAndAfter; -import com.palantir.conjure.java.client.jaxrs.JaxRsClient; -import com.palantir.conjure.java.client.jaxrs.TestBase; -import com.palantir.conjure.java.client.jaxrs.TestService; import com.palantir.conjure.java.okhttp.HostMetricsRegistry; import com.palantir.dialogue.Channel; import okhttp3.mockwebserver.MockResponse; diff --git a/conjure-scala-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/FeignJaxRsScalaClientBuilder.java b/conjure-scala-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/FeignJaxRsScalaClientBuilder.java index 5c8664649..830b33d86 100644 --- a/conjure-scala-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/FeignJaxRsScalaClientBuilder.java +++ b/conjure-scala-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/FeignJaxRsScalaClientBuilder.java @@ -22,7 +22,7 @@ import com.palantir.conjure.java.client.config.ClientConfiguration; import com.palantir.conjure.java.serialization.ScalaObjectMappers; -public final class FeignJaxRsScalaClientBuilder extends AbstractFeignJaxRsClientBuilder { +final class FeignJaxRsScalaClientBuilder extends AbstractFeignJaxRsClientBuilder { private static final JsonMapper JSON_MAPPER = ScalaObjectMappers.newClientJsonMapper(); private static final CBORMapper CBOR_MAPPER = ScalaObjectMappers.newClientCborMapper(); @@ -32,12 +32,12 @@ public final class FeignJaxRsScalaClientBuilder extends AbstractFeignJaxRsClient } @Override - protected ObjectMapper getObjectMapper() { + ObjectMapper getObjectMapper() { return JSON_MAPPER; } @Override - protected ObjectMapper getCborObjectMapper() { + ObjectMapper getCborObjectMapper() { return CBOR_MAPPER; } } From 18c41314c8e0b4aeca14f6dccf8761908683bcc5 Mon Sep 17 00:00:00 2001 From: Patrick Koenig Date: Fri, 27 Mar 2026 01:33:08 -0400 Subject: [PATCH 5/6] Fix checkstyle --- .../conjure/java/client/jaxrs/Client.java | 2 +- .../conjure/java/client/jaxrs/Contract.java | 31 +++++----- .../conjure/java/client/jaxrs/Feign.java | 2 + .../java/client/jaxrs/MethodHandler.java | 1 + .../java/client/jaxrs/MethodMetadata.java | 2 +- .../java/client/jaxrs/ReflectiveFeign.java | 34 +++++------ .../conjure/java/client/jaxrs/Request.java | 11 ++-- .../java/client/jaxrs/RequestTemplate.java | 60 +++++++------------ .../conjure/java/client/jaxrs/Response.java | 27 ++++----- .../jaxrs/SynchronousMethodHandler.java | 21 +++---- .../conjure/java/client/jaxrs/Types.java | 1 + .../conjure/java/client/jaxrs/Util.java | 20 +++---- 12 files changed, 95 insertions(+), 117 deletions(-) diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Client.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Client.java index fe73a7967..5f78c1997 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Client.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Client.java @@ -26,7 +26,7 @@ interface Client { * Executes a request against its {@link Request#url() url} and returns a response. * * @param request safe to replay. - * @return connected response, {@link Response.Body} is absent or unread. + * @return connected response, {@link Response.Body} is absent or unread * @throws IOException on a network error connecting to {@link Request#url()}. */ Response execute(Request request) throws IOException; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Contract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Contract.java index 60d17927a..3feb44d5c 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Contract.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Contract.java @@ -15,8 +15,6 @@ */ package com.palantir.conjure.java.client.jaxrs; -import static com.palantir.conjure.java.client.jaxrs.Util.checkState; - import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -44,16 +42,16 @@ abstract class BaseContract implements Contract { @Override public List parseAndValidateMetadata(Class targetType) { - checkState( + Util.checkState( targetType.getTypeParameters().length == 0, "Parameterized types unsupported: %s", targetType.getSimpleName()); - checkState( + Util.checkState( targetType.getInterfaces().length <= 1, "Only single inheritance supported: %s", targetType.getSimpleName()); if (targetType.getInterfaces().length == 1) { - checkState( + Util.checkState( targetType.getInterfaces()[0].getInterfaces().length == 0, "Only single-level inheritance supported: %s", targetType.getSimpleName()); @@ -66,7 +64,7 @@ public List parseAndValidateMetadata(Class targetType) { continue; } MethodMetadata metadata = parseAndValidateMetadata(targetType, method); - checkState( + Util.checkState( !result.containsKey(metadata.configKey()), "Overrides unsupported: %s", metadata.configKey()); result.put(metadata.configKey(), metadata); } @@ -89,7 +87,7 @@ MethodMetadata parseAndValidateMetadata(Class targetType, Method method) { for (Annotation methodAnnotation : method.getAnnotations()) { processAnnotationOnMethod(data, methodAnnotation, method); } - checkState( + Util.checkState( data.template().method() != null, "Method %s not annotated with HTTP method type (ex. GET, POST)", method.getName()); @@ -105,22 +103,23 @@ MethodMetadata parseAndValidateMetadata(Class targetType, Method method) { if (parameterTypes[i] == URI.class) { data.urlIndex(i); } else if (!isHttpAnnotation) { - checkState(data.formParams().isEmpty(), "Body parameters cannot be used with form parameters."); - checkState(data.bodyIndex() == null, "Method has too many Body parameters: %s", method); + Util.checkState( + data.formParams().isEmpty(), "Body parameters cannot be used with form parameters."); + Util.checkState(data.bodyIndex() == null, "Method has too many Body parameters: %s", method); data.bodyIndex(i); data.bodyType(Types.resolve(targetType, targetType, method.getGenericParameterTypes()[i])); } } if (data.headerMapIndex() != null) { - checkState( + Util.checkState( Map.class.isAssignableFrom(parameterTypes[data.headerMapIndex()]), "HeaderMap parameter must be a Map: %s", parameterTypes[data.headerMapIndex()]); } if (data.queryMapIndex() != null) { - checkState( + Util.checkState( Map.class.isAssignableFrom(parameterTypes[data.queryMapIndex()]), "QueryMap parameter must be a Map: %s", parameterTypes[data.queryMapIndex()]); @@ -153,9 +152,9 @@ MethodMetadata parseAndValidateMetadata(Class targetType, Method method) { * @return true if you called {@link #nameParam(MethodMetadata, String, int)} after finding an * http-relevant annotation. */ - abstract boolean processAnnotationsOnParameter( - MethodMetadata data, Annotation[] annotations, int paramIndex); + abstract boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex); + @SuppressWarnings("ParameterAssignment") Collection addTemplatedParam(Collection possiblyNull, String name) { if (possiblyNull == null) { possiblyNull = new ArrayList(); @@ -167,11 +166,11 @@ Collection addTemplatedParam(Collection possiblyNull, String nam /** * links a parameter name to its index in the method signature. */ - void nameParam(MethodMetadata data, String name, int i) { + void nameParam(MethodMetadata data, String name, int index) { Collection names = - data.indexToName().containsKey(i) ? data.indexToName().get(i) : new ArrayList(); + data.indexToName().containsKey(index) ? data.indexToName().get(index) : new ArrayList(); names.add(name); - data.indexToName().put(i, names); + data.indexToName().put(index, names); } } } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Feign.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Feign.java index cb3957e27..2d44dc05a 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Feign.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Feign.java @@ -43,6 +43,7 @@ static Builder builder() { * @param targetType {@link Target#type() type} of the Feign interface. * @param method invoked method, present on {@code type} or its super. */ + @SuppressWarnings("ModifiedControlVariable") static String configKey(Class targetType, Method method) { StringBuilder builder = new StringBuilder(); builder.append(targetType.getSimpleName()); @@ -63,6 +64,7 @@ static String configKey(Class targetType, Method method) { */ abstract T newInstance(Target target); + @SuppressWarnings("HiddenField") static final class Builder { private Contract contract; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/MethodHandler.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/MethodHandler.java index e2a3cf5b8..1c0d205d7 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/MethodHandler.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/MethodHandler.java @@ -25,5 +25,6 @@ */ public interface MethodHandler { + @SuppressWarnings("IllegalThrows") Object invoke(Object[] argv) throws Throwable; } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/MethodMetadata.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/MethodMetadata.java index a9ee8964d..86d049819 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/MethodMetadata.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/MethodMetadata.java @@ -22,7 +22,7 @@ import java.util.List; import java.util.Map; -@SuppressWarnings("MissingSummary") +@SuppressWarnings({"HiddenField", "MissingSummary"}) final class MethodMetadata { private String configKey; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/ReflectiveFeign.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/ReflectiveFeign.java index c1139d9b6..2971e35fa 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/ReflectiveFeign.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/ReflectiveFeign.java @@ -15,10 +15,6 @@ */ package com.palantir.conjure.java.client.jaxrs; -import static com.palantir.conjure.java.client.jaxrs.Util.checkArgument; -import static com.palantir.conjure.java.client.jaxrs.Util.checkNotNull; -import static com.palantir.conjure.java.client.jaxrs.Util.checkState; - import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; @@ -76,8 +72,8 @@ static class FeignInvocationHandler implements InvocationHandler { private final Map dispatch; FeignInvocationHandler(Target target, Map dispatch) { - this.target = checkNotNull(target, "target"); - this.dispatch = checkNotNull(dispatch, "dispatch for %s", target); + this.target = Util.checkNotNull(target, "target"); + this.dispatch = Util.checkNotNull(dispatch, "dispatch for %s", target); } @Override @@ -131,11 +127,11 @@ static final class ParseHandlersByName { Decoder decoder, ErrorDecoder errorDecoder, SynchronousMethodHandler.Factory factory) { - this.contract = checkNotNull(contract, "contract"); - this.factory = checkNotNull(factory, "factory"); - this.errorDecoder = checkNotNull(errorDecoder, "errorDecoder"); - this.encoder = checkNotNull(encoder, "encoder"); - this.decoder = checkNotNull(decoder, "decoder"); + this.contract = Util.checkNotNull(contract, "contract"); + this.factory = Util.checkNotNull(factory, "factory"); + this.errorDecoder = Util.checkNotNull(errorDecoder, "errorDecoder"); + this.encoder = Util.checkNotNull(encoder, "encoder"); + this.decoder = Util.checkNotNull(decoder, "decoder"); } Map apply(Target key) { @@ -158,7 +154,9 @@ Map apply(Target key) { private static class BuildTemplateByResolvingArgs implements RequestTemplate.Factory { + @SuppressWarnings("VisibilityModifier") final MethodMetadata metadata; + private final Map indexToExpander = new LinkedHashMap(); private BuildTemplateByResolvingArgs(MethodMetadata metadata) { @@ -190,17 +188,17 @@ public RequestTemplate create(Object[] argv) { RequestTemplate mutable = new RequestTemplate(metadata.template()); if (metadata.urlIndex() != null) { int urlIndex = metadata.urlIndex(); - checkArgument(argv[urlIndex] != null, "URI parameter %s was null", urlIndex); + Util.checkArgument(argv[urlIndex] != null, "URI parameter %s was null", urlIndex); mutable.insert(0, String.valueOf(argv[urlIndex])); } Map varBuilder = new LinkedHashMap(); for (Entry> entry : metadata.indexToName().entrySet()) { - int i = entry.getKey(); + int index = entry.getKey(); Object value = argv[entry.getKey()]; if (value != null) { // Null values are skipped. - if (indexToExpander.containsKey(i)) { - value = expandElements(indexToExpander.get(i), value); + if (indexToExpander.containsKey(index)) { + value = expandElements(indexToExpander.get(index), value); } for (String name : entry.getValue()) { varBuilder.put(name, value); @@ -242,7 +240,7 @@ private List expandIterable(Expander expander, Iterable value) { private RequestTemplate addHeaderMapHeaders(Object[] argv, RequestTemplate mutable) { Map headerMap = (Map) argv[metadata.headerMapIndex()]; for (Entry currEntry : headerMap.entrySet()) { - checkState( + Util.checkState( currEntry.getKey().getClass() == String.class, "HeaderMap key must be a String: %s", currEntry.getKey()); @@ -268,7 +266,7 @@ private RequestTemplate addHeaderMapHeaders(Object[] argv, RequestTemplate mutab private RequestTemplate addQueryMapQueryParameters(Object[] argv, RequestTemplate mutable) { Map queryMap = (Map) argv[metadata.queryMapIndex()]; for (Entry currEntry : queryMap.entrySet()) { - checkState( + Util.checkState( currEntry.getKey().getClass() == String.class, "QueryMap key must be a String: %s", currEntry.getKey()); @@ -330,7 +328,7 @@ private BuildEncodedTemplateFromArgs(MethodMetadata metadata, Encoder encoder) { @Override RequestTemplate resolve(Object[] argv, RequestTemplate mutable, Map variables) { Object body = argv[metadata.bodyIndex()]; - checkArgument(body != null, "Body parameter %s was null", metadata.bodyIndex()); + Util.checkArgument(body != null, "Body parameter %s was null", metadata.bodyIndex()); encoder.encode(body, metadata.bodyType(), mutable); return super.resolve(argv, mutable, variables); } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Request.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Request.java index c25085030..3a2dbefe3 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Request.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Request.java @@ -15,9 +15,6 @@ */ package com.palantir.conjure.java.client.jaxrs; -import static com.palantir.conjure.java.client.jaxrs.Util.checkNotNull; -import static com.palantir.conjure.java.client.jaxrs.Util.valuesOrEmpty; - import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.Map; @@ -41,9 +38,9 @@ static Request create(String method, String url, Map> private final byte[] body; Request(String method, String url, Map> headers, byte[] body) { - this.method = checkNotNull(method, "method of %s", url); - this.url = checkNotNull(url, "url"); - this.headers = checkNotNull(headers, "headers of %s %s", method, url); + this.method = Util.checkNotNull(method, "method of %s", url); + this.url = Util.checkNotNull(url, "url"); + this.headers = Util.checkNotNull(headers, "headers of %s %s", method, url); this.body = body; // nullable } @@ -75,7 +72,7 @@ public String toString() { StringBuilder builder = new StringBuilder(); builder.append(method).append(' ').append(url).append(" HTTP/1.1\n"); for (String field : headers.keySet()) { - for (String value : valuesOrEmpty(headers, field)) { + for (String value : Util.valuesOrEmpty(headers, field)) { builder.append(field).append(": ").append(value).append('\n'); } } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/RequestTemplate.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/RequestTemplate.java index c418a6414..7020e4388 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/RequestTemplate.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/RequestTemplate.java @@ -15,18 +15,9 @@ */ package com.palantir.conjure.java.client.jaxrs; -import static com.palantir.conjure.java.client.jaxrs.Util.CONTENT_LENGTH; -import static com.palantir.conjure.java.client.jaxrs.Util.UTF_8; -import static com.palantir.conjure.java.client.jaxrs.Util.checkArgument; -import static com.palantir.conjure.java.client.jaxrs.Util.checkNotNull; -import static com.palantir.conjure.java.client.jaxrs.Util.emptyToNull; -import static com.palantir.conjure.java.client.jaxrs.Util.toArray; -import static com.palantir.conjure.java.client.jaxrs.Util.valuesOrEmpty; - -import java.io.UncheckedIOException; -import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -43,7 +34,7 @@ * javax.ws.rs.client.Invocation.Builder}, ensuring you can modify any part of the request. However, * this object is mutable, so needs to be guarded with the copy constructor. */ -@SuppressWarnings("MissingSummary") +@SuppressWarnings({"HiddenField", "MissingSummary"}) final class RequestTemplate { private final Map> queries = new LinkedHashMap>(); @@ -59,7 +50,7 @@ final class RequestTemplate { /** Copy constructor. Use this when making templates. */ RequestTemplate(RequestTemplate toCopy) { - checkNotNull(toCopy, "toCopy"); + Util.checkNotNull(toCopy, "toCopy"); this.method = toCopy.method; this.url.append(toCopy.url); this.queries.putAll(toCopy.queries); @@ -69,19 +60,11 @@ final class RequestTemplate { } private static String urlDecode(String arg) { - try { - return URLDecoder.decode(arg, UTF_8.name()); - } catch (UnsupportedEncodingException e) { - throw new UncheckedIOException(e); - } + return URLDecoder.decode(arg, StandardCharsets.UTF_8); } private static String urlEncode(Object arg) { - try { - return URLEncoder.encode(String.valueOf(arg), UTF_8.name()); - } catch (UnsupportedEncodingException e) { - throw new UncheckedIOException(e); - } + return URLEncoder.encode(String.valueOf(arg), StandardCharsets.UTF_8); } /** @@ -94,13 +77,14 @@ private static String urlEncode(Object arg) { * @param variables to the URI template * @return expanded template, leaving any unresolved parameters literal */ + @SuppressWarnings("LocalVariableName") static String expand(String template, Map variables) { // skip expansion if there's no valid variables set. ex. {a} is the // first valid - if (checkNotNull(template, "template").length() < 3) { + if (Util.checkNotNull(template, "template").length() < 3) { return template.toString(); } - checkNotNull(variables, "variables for %s", template); + Util.checkNotNull(variables, "variables for %s", template); boolean inVar = false; StringBuilder var = new StringBuilder(); @@ -144,9 +128,10 @@ static String expand(String template, Map variables) { return builder.toString(); } + @SuppressWarnings("LocalVariableName") private static Map> parseAndDecodeQueries(String queryLine) { Map> map = new LinkedHashMap>(); - if (emptyToNull(queryLine) == null) { + if (Util.emptyToNull(queryLine) == null) { return map; } if (queryLine.indexOf('&') == -1) { @@ -204,7 +189,7 @@ RequestTemplate resolve(Map unencoded) { Map> resolvedHeaders = new LinkedHashMap>(); for (String field : headers.keySet()) { Collection resolvedValues = new ArrayList(); - for (String value : valuesOrEmpty(headers, field)) { + for (String value : Util.valuesOrEmpty(headers, field)) { String resolved = expand(value, unencoded); resolvedValues.add(resolved); } @@ -224,8 +209,8 @@ Request request() { /** @see Request#method() */ RequestTemplate method(String method) { - this.method = checkNotNull(method, "method"); - checkArgument(method.matches("^[A-Z]+$"), "Invalid HTTP Method: %s", method); + this.method = Util.checkNotNull(method, "method"); + Util.checkArgument(method.matches("^[A-Z]+$"), "Invalid HTTP Method: %s", method); return this; } @@ -251,6 +236,7 @@ RequestTemplate append(String value) { } /** @see #url() */ + @SuppressWarnings("ParameterAssignment") RequestTemplate insert(int pos, String value) { if (value.startsWith("http")) { if (value.endsWith("/")) { @@ -318,11 +304,11 @@ RequestTemplate query(String name, Iterable values) { } private RequestTemplate doQuery(boolean encoded, String name, String... values) { - checkNotNull(name, "name"); + Util.checkNotNull(name, "name"); String paramName = encoded ? name : encodeIfNotVariable(name); queries.remove(paramName); if (values != null && values.length > 0 && values[0] != null) { - ArrayList paramValues = new ArrayList(); + List paramValues = new ArrayList(); for (String value : values) { paramValues.add(encoded ? value : encodeIfNotVariable(value)); } @@ -333,7 +319,7 @@ private RequestTemplate doQuery(boolean encoded, String name, String... values) private RequestTemplate doQuery(boolean encoded, String name, Iterable values) { if (values != null) { - return doQuery(encoded, name, toArray(values, String.class)); + return doQuery(encoded, name, Util.toArray(values, String.class)); } return doQuery(encoded, name, (String[]) null); } @@ -361,7 +347,7 @@ RequestTemplate queries(Map> queries) { this.queries.clear(); } else { for (Entry> entry : queries.entrySet()) { - query(entry.getKey(), toArray(entry.getValue(), String.class)); + query(entry.getKey(), Util.toArray(entry.getValue(), String.class)); } } return this; @@ -376,7 +362,7 @@ Map> queries() { Map> decoded = new LinkedHashMap>(); for (String field : queries.keySet()) { Collection decodedValues = new ArrayList(); - for (String value : valuesOrEmpty(queries, field)) { + for (String value : Util.valuesOrEmpty(queries, field)) { if (value != null) { decodedValues.add(urlDecode(value)); } else { @@ -404,7 +390,7 @@ Map> queries() { * @see #headers() */ RequestTemplate header(String name, String... values) { - checkNotNull(name, "header name"); + Util.checkNotNull(name, "header name"); if (values == null || (values.length == 1 && values[0] == null)) { headers.remove(name); } else { @@ -418,7 +404,7 @@ RequestTemplate header(String name, String... values) { /** @see #header(String, String...) */ RequestTemplate header(String name, Iterable values) { if (values != null) { - return header(name, toArray(values, String.class)); + return header(name, Util.toArray(values, String.class)); } return header(name, (String[]) null); } @@ -461,7 +447,7 @@ Map> headers() { RequestTemplate body(byte[] bodyData) { this.body = bodyData; int bodyLength = bodyData != null ? bodyData.length : 0; - header(CONTENT_LENGTH, String.valueOf(bodyLength)); + header(Util.CONTENT_LENGTH, String.valueOf(bodyLength)); return this; } @@ -568,7 +554,7 @@ String queryLine() { } StringBuilder queryBuilder = new StringBuilder(); for (String field : queries.keySet()) { - for (String value : valuesOrEmpty(queries, field)) { + for (String value : Util.valuesOrEmpty(queries, field)) { queryBuilder.append('&'); queryBuilder.append(field); if (value != null) { diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Response.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Response.java index abb14f99c..e08bd3a10 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Response.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Response.java @@ -15,12 +15,6 @@ */ package com.palantir.conjure.java.client.jaxrs; -import static com.palantir.conjure.java.client.jaxrs.Util.UTF_8; -import static com.palantir.conjure.java.client.jaxrs.Util.checkNotNull; -import static com.palantir.conjure.java.client.jaxrs.Util.checkState; -import static com.palantir.conjure.java.client.jaxrs.Util.decodeOrDefault; -import static com.palantir.conjure.java.client.jaxrs.Util.valuesOrEmpty; - import java.io.ByteArrayInputStream; import java.io.Closeable; import java.io.IOException; @@ -28,6 +22,7 @@ import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -46,7 +41,7 @@ final class Response implements Closeable { private final Body body; private Response(int status, String reason, Map> headers, Body body) { - checkState(status >= 200, "Invalid status code: %s", status); + Util.checkState(status >= 200, "Invalid status code: %s", status); this.status = status; this.reason = reason; // nullable this.headers = Collections.unmodifiableMap(caseInsensitiveCopyOf(headers)); @@ -110,14 +105,18 @@ Body body() { @Override public String toString() { StringBuilder builder = new StringBuilder("HTTP/1.1 ").append(status); - if (reason != null) builder.append(' ').append(reason); + if (reason != null) { + builder.append(' ').append(reason); + } builder.append('\n'); for (String field : headers.keySet()) { - for (String value : valuesOrEmpty(headers, field)) { + for (String value : Util.valuesOrEmpty(headers, field)) { builder.append(field).append(": ").append(value).append('\n'); } } - if (body != null) builder.append('\n').append(body); + if (body != null) { + builder.append('\n').append(body); + } return builder.toString(); } @@ -186,7 +185,7 @@ public InputStream asInputStream() throws IOException { @Override public Reader asReader() throws IOException { - return new InputStreamReader(inputStream, UTF_8); + return new InputStreamReader(inputStream, StandardCharsets.UTF_8); } @Override @@ -214,7 +213,7 @@ private static Body orNull(String text, Charset charset) { if (text == null) { return null; } - checkNotNull(charset, "charset"); + Util.checkNotNull(charset, "charset"); return new ByteArrayBody(text.getBytes(charset)); } @@ -235,7 +234,7 @@ public InputStream asInputStream() throws IOException { @Override public Reader asReader() throws IOException { - return new InputStreamReader(asInputStream(), UTF_8); + return new InputStreamReader(asInputStream(), StandardCharsets.UTF_8); } @Override @@ -243,7 +242,7 @@ public void close() throws IOException {} @Override public String toString() { - return decodeOrDefault(data, UTF_8, "Binary data"); + return Util.decodeOrDefault(data, StandardCharsets.UTF_8, "Binary data"); } } diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/SynchronousMethodHandler.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/SynchronousMethodHandler.java index e5cc91100..15d010282 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/SynchronousMethodHandler.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/SynchronousMethodHandler.java @@ -15,9 +15,6 @@ */ package com.palantir.conjure.java.client.jaxrs; -import static com.palantir.conjure.java.client.jaxrs.Util.checkNotNull; -import static com.palantir.conjure.java.client.jaxrs.Util.ensureClosed; - final class SynchronousMethodHandler implements MethodHandler { private static final long MAX_RESPONSE_BUFFER_SIZE = 8192L; @@ -36,12 +33,12 @@ private SynchronousMethodHandler( RequestTemplate.Factory buildTemplateFromArgs, Decoder decoder, ErrorDecoder errorDecoder) { - this.target = checkNotNull(target, "target"); - this.client = checkNotNull(client, "client for %s", target); - this.metadata = checkNotNull(metadata, "metadata for %s", target); - this.buildTemplateFromArgs = checkNotNull(buildTemplateFromArgs, "metadata for %s", target); - this.errorDecoder = checkNotNull(errorDecoder, "errorDecoder for %s", target); - this.decoder = checkNotNull(decoder, "decoder for %s", target); + this.target = Util.checkNotNull(target, "target"); + this.client = Util.checkNotNull(client, "client for %s", target); + this.metadata = Util.checkNotNull(metadata, "metadata for %s", target); + this.buildTemplateFromArgs = Util.checkNotNull(buildTemplateFromArgs, "metadata for %s", target); + this.errorDecoder = Util.checkNotNull(errorDecoder, "errorDecoder for %s", target); + this.decoder = Util.checkNotNull(decoder, "decoder for %s", target); } @Override @@ -50,7 +47,7 @@ public Object invoke(Object[] argv) throws Throwable { return executeAndDecode(template); } - Object executeAndDecode(RequestTemplate template) throws Throwable { + Object executeAndDecode(RequestTemplate template) throws Exception { Request request = target.apply(new RequestTemplate(template)); Response response = client.execute(request); @@ -80,7 +77,7 @@ Object executeAndDecode(RequestTemplate template) throws Throwable { } } finally { if (shouldClose) { - ensureClosed(response.body()); + Util.ensureClosed(response.body()); } } } @@ -90,7 +87,7 @@ static class Factory { private final Client client; Factory(Client client) { - this.client = checkNotNull(client, "client"); + this.client = Util.checkNotNull(client, "client"); } public MethodHandler create( diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Types.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Types.java index cae7b8896..266a25a0f 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Types.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Types.java @@ -31,6 +31,7 @@ * @author Bob Lee * @author Jesse Wilson */ +@SuppressWarnings({"CyclomaticComplexity", "ParameterAssignment", "ParameterName"}) final class Types { private static final Type[] EMPTY_TYPE_ARRAY = new Type[0]; diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Util.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Util.java index fcfe0009e..1e917cf80 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Util.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Util.java @@ -15,8 +15,6 @@ */ package com.palantir.conjure.java.client.jaxrs; -import static java.lang.String.format; - import com.google.errorprone.annotations.FormatMethod; import java.io.ByteArrayOutputStream; import java.io.Closeable; @@ -73,7 +71,7 @@ private Util() { // no instances @FormatMethod static void checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { if (!expression) { - throw new IllegalArgumentException(format(errorMessageTemplate, errorMessageArgs)); + throw new IllegalArgumentException(String.format(errorMessageTemplate, errorMessageArgs)); } } @@ -84,7 +82,7 @@ static void checkArgument(boolean expression, String errorMessageTemplate, Objec static T checkNotNull(T reference, String errorMessageTemplate, Object... errorMessageArgs) { if (reference == null) { // If either of these parameters is null, the right thing happens anyway - throw new NullPointerException(format(errorMessageTemplate, errorMessageArgs)); + throw new NullPointerException(String.format(errorMessageTemplate, errorMessageArgs)); } return reference; } @@ -95,7 +93,7 @@ static T checkNotNull(T reference, String errorMessageTemplate, Object... er @FormatMethod static void checkState(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { if (!expression) { - throw new IllegalStateException(format(errorMessageTemplate, errorMessageArgs)); + throw new IllegalStateException(String.format(errorMessageTemplate, errorMessageArgs)); } } @@ -107,8 +105,8 @@ static boolean isDefault(Method method) { // declared in an interface. // method.isDefault() is not sufficient for our usage as it does not check // for synthetic methods. As a result, it picks up overridden methods as well as actual default methods. - final int SYNTHETIC = 0x00001000; - return ((method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC | SYNTHETIC)) + final int synthetic = 0x00001000; + return ((method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC | synthetic)) == Modifier.PUBLIC) && method.getDeclaringClass().isInterface(); } @@ -251,12 +249,12 @@ private static long copy(InputStream from, OutputStream to) throws IOException { byte[] buf = new byte[BUF_SIZE]; long total = 0; while (true) { - int r = from.read(buf); - if (r == -1) { + int read = from.read(buf); + if (read == -1) { break; } - to.write(buf, 0, r); - total += r; + to.write(buf, 0, read); + total += read; } return total; } From f20b0df318139ffa93b535bba7ddb8c6384538c8 Mon Sep 17 00:00:00 2001 From: Patrick Koenig Date: Fri, 27 Mar 2026 12:27:51 -0400 Subject: [PATCH 6/6] Eliminate DefaultMethodHandler and opens --- conjure-java-jaxrs-client/build.gradle | 8 -- .../conjure/java/client/jaxrs/Contract.java | 2 +- .../client/jaxrs/DefaultMethodHandler.java | 75 ------------------- .../java/client/jaxrs/ReflectiveFeign.java | 21 ++---- .../conjure/java/client/jaxrs/Util.java | 16 ---- 5 files changed, 9 insertions(+), 113 deletions(-) delete mode 100644 conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/DefaultMethodHandler.java diff --git a/conjure-java-jaxrs-client/build.gradle b/conjure-java-jaxrs-client/build.gradle index 39c9babfa..93e7417e5 100644 --- a/conjure-java-jaxrs-client/build.gradle +++ b/conjure-java-jaxrs-client/build.gradle @@ -41,11 +41,3 @@ dependencies { testImplementation "org.mockito:mockito-junit-jupiter" testImplementation "com.palantir.safe-logging:preconditions-assertj" } - -// feign.DefaultMethodHandler sets Lookup.IMPL_LOOKUP accessible resulting in -// InaccessibleObjectException: Unable to make field static final java.lang.invoke.MethodHandles$Lookup -// java.lang.invoke.MethodHandles$Lookup.IMPL_LOOKUP accessible: module java.base does not -// "opens java.lang.invoke" to unnamed module -moduleJvmArgs { - opens 'java.base/java.lang.invoke' -} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Contract.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Contract.java index 3feb44d5c..8f6c8bc78 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Contract.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Contract.java @@ -60,7 +60,7 @@ public List parseAndValidateMetadata(Class targetType) { for (Method method : targetType.getMethods()) { if (method.getDeclaringClass() == Object.class || (method.getModifiers() & Modifier.STATIC) != 0 - || Util.isDefault(method)) { + || method.isDefault()) { continue; } MethodMetadata metadata = parseAndValidateMetadata(targetType, method); diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/DefaultMethodHandler.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/DefaultMethodHandler.java deleted file mode 100644 index 71048cd3d..000000000 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/DefaultMethodHandler.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.palantir.conjure.java.client.jaxrs; - -import com.palantir.logsafe.exceptions.SafeIllegalStateException; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles.Lookup; -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -/** - * Handles default methods by directly invoking the default method code on the interface. - * The bindTo method must be called on the result before invoke is called. - */ -final class DefaultMethodHandler implements MethodHandler { - // Uses Java 7 MethodHandle based reflection. As default methods will only exist when - // run on a Java 8 JVM this will not affect use on legacy JVMs. - // When Feign upgrades to Java 7, remove the @IgnoreJRERequirement annotation. - private final MethodHandle unboundHandle; - - // handle is effectively final after bindTo has been called. - private MethodHandle handle; - - DefaultMethodHandler(Method defaultMethod) { - try { - Class declaringClass = defaultMethod.getDeclaringClass(); - Field field = Lookup.class.getDeclaredField("IMPL_LOOKUP"); - field.setAccessible(true); - Lookup lookup = (Lookup) field.get(null); - - this.unboundHandle = lookup.unreflectSpecial(defaultMethod, declaringClass); - } catch (NoSuchFieldException ex) { - throw new IllegalStateException(ex); - } catch (IllegalAccessException ex) { - throw new IllegalStateException(ex); - } - } - - /** - * Bind this handler to a proxy object. After bound, DefaultMethodHandler#invoke will act as if it was called - * on the proxy object. Must be called once and only once for a given instance of DefaultMethodHandler - */ - void bindTo(Object proxy) { - if (handle != null) { - throw new SafeIllegalStateException("Attempted to rebind a default method handler that was already bound"); - } - handle = unboundHandle.bindTo(proxy); - } - - /** - * Invoke this method. DefaultMethodHandler#bindTo must be called before the first - * time invoke is called. - */ - @Override - public Object invoke(Object[] argv) throws Throwable { - if (handle == null) { - throw new SafeIllegalStateException("Default method handler invoked before proxy has been bound."); - } - return handle.invokeWithArguments(argv); - } -} diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/ReflectiveFeign.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/ReflectiveFeign.java index 2971e35fa..8ec1a655d 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/ReflectiveFeign.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/ReflectiveFeign.java @@ -40,29 +40,20 @@ final class ReflectiveFeign extends Feign { * to cache the result. */ @Override - @SuppressWarnings({"ProxyNonConstantType", "RedundantControlFlow"}) + @SuppressWarnings("ProxyNonConstantType") T newInstance(Target target) { Map nameToHandler = targetToHandlersByName.apply(target); Map methodToHandler = new HashMap<>(); - List defaultMethodHandlers = new ArrayList<>(); for (Method method : target.type().getMethods()) { - if (method.getDeclaringClass() == Object.class) { + if (method.getDeclaringClass() == Object.class || method.isDefault()) { continue; - } else if (Util.isDefault(method)) { - DefaultMethodHandler handler = new DefaultMethodHandler(method); - defaultMethodHandlers.add(handler); - methodToHandler.put(method, handler); - } else { - methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method))); } + methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method))); } InvocationHandler handler = new FeignInvocationHandler(target, methodToHandler); T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class[] {target.type()}, handler); - for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) { - defaultMethodHandler.bindTo(proxy); - } return proxy; } @@ -77,7 +68,11 @@ static class FeignInvocationHandler implements InvocationHandler { } @Override - public Object invoke(Object _proxy, Method method, Object[] args) throws Throwable { + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (method.isDefault()) { + return InvocationHandler.invokeDefault(proxy, method, args); + } + if ("equals".equals(method.getName())) { try { Object otherHandler = diff --git a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Util.java b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Util.java index 1e917cf80..8d4fd0ca9 100644 --- a/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Util.java +++ b/conjure-java-jaxrs-client/src/main/java/com/palantir/conjure/java/client/jaxrs/Util.java @@ -23,8 +23,6 @@ import java.io.OutputStream; import java.io.Reader; import java.lang.reflect.Array; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.nio.ByteBuffer; import java.nio.CharBuffer; @@ -97,20 +95,6 @@ static void checkState(boolean expression, String errorMessageTemplate, Object.. } } - /** - * Identifies a method as a default instance method. - */ - static boolean isDefault(Method method) { - // Default methods are non-abstract, non-synthetic, and non-static instance methods - // declared in an interface. - // method.isDefault() is not sufficient for our usage as it does not check - // for synthetic methods. As a result, it picks up overridden methods as well as actual default methods. - final int synthetic = 0x00001000; - return ((method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC | synthetic)) - == Modifier.PUBLIC) - && method.getDeclaringClass().isInterface(); - } - /** * Adapted from {@code com.google.common.base.Strings#emptyToNull}. */