From 1d539d67ad4b290f6e4399f50841ab2db2051703 Mon Sep 17 00:00:00 2001 From: Sylvain Wallez Date: Tue, 17 Oct 2023 18:22:20 +0200 Subject: [PATCH] Add duplicate class check --- .../elasticsearch/ElasticsearchClient.java | 8 ++ .../clients/util/DuplicateResourceFinder.java | 80 +++++++++++++++++++ .../util/DuplicateResourceFinderTest.java | 40 ++++++++++ .../databind/ext/CoreXMLSerializers.java | 25 ++++++ 4 files changed, 153 insertions(+) create mode 100644 java-client/src/main/java/co/elastic/clients/util/DuplicateResourceFinder.java create mode 100644 java-client/src/test/java/co/elastic/clients/util/DuplicateResourceFinderTest.java create mode 100644 java-client/src/test/java/com/fasterxml/jackson/databind/ext/CoreXMLSerializers.java diff --git a/java-client-serverless/src/main/java/co/elastic/clients/elasticsearch/ElasticsearchClient.java b/java-client-serverless/src/main/java/co/elastic/clients/elasticsearch/ElasticsearchClient.java index 3976f933b..fde39006f 100644 --- a/java-client-serverless/src/main/java/co/elastic/clients/elasticsearch/ElasticsearchClient.java +++ b/java-client-serverless/src/main/java/co/elastic/clients/elasticsearch/ElasticsearchClient.java @@ -112,7 +112,9 @@ import co.elastic.clients.transport.endpoints.BinaryResponse; import co.elastic.clients.transport.endpoints.BooleanResponse; import co.elastic.clients.transport.endpoints.EndpointWithResponseMapperAttr; +import co.elastic.clients.util.DuplicateResourceFinder; import co.elastic.clients.util.ObjectBuilder; + import java.io.IOException; import java.lang.reflect.Type; import java.util.function.Function; @@ -123,6 +125,12 @@ */ public class ElasticsearchClient extends ApiClient { + static { + // Make sure we don't have several versions of this class. This may happen if + // several flavors of the client (Stack and Serverless) are present in the classpath. + DuplicateResourceFinder.ensureClassUniqueness(ElasticsearchClient.class); + } + public ElasticsearchClient(ElasticsearchTransport transport) { super(transport, null); } diff --git a/java-client/src/main/java/co/elastic/clients/util/DuplicateResourceFinder.java b/java-client/src/main/java/co/elastic/clients/util/DuplicateResourceFinder.java new file mode 100644 index 000000000..8da11ab92 --- /dev/null +++ b/java-client/src/main/java/co/elastic/clients/util/DuplicateResourceFinder.java @@ -0,0 +1,80 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you 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 co.elastic.clients.util; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +public class DuplicateResourceFinder { + + private static volatile boolean ENABLED = true; + + /** + * Disables the resource uniqueness checks. Use with caution, as it will mask problems that may hit later. + */ + public static void enableCheck(boolean enabled) { + ENABLED = enabled; + } + + /** + * Ensure a class is only defined once in this class' classpath + */ + public static void ensureClassUniqueness(Class clazz) { + String name = clazz.getName(); + String resource = clazz.getName().replace('.', '/') + ".class"; + ensureResourceUniqueness(resource, name, DuplicateResourceFinder.class.getClassLoader()); + } + + public static void ensureResourceUniqueness(String path) { + ensureResourceUniqueness(path, path, DuplicateResourceFinder.class.getClassLoader()); + } + + private static void ensureResourceUniqueness(String path, String name, ClassLoader classLoader) { + if (!ENABLED) { + return; + } + + // With Java9 modules, will work only with exported classes/resources. This is actually + // what we want, as non-exported classes/resources will not conflict. + List list = new ArrayList<>(); + try { + Enumeration resources = classLoader.getResources(path); + while (resources.hasMoreElements()) { + list.add(resources.nextElement()); + } + } catch (IOException ioe) { + // Ignore + } + + if (list.size() > 1) { + StringBuilder sb = new StringBuilder("Several versions of ") + .append(name) + .append(" were found. This can cause conflicts, please fix the classpath:\n"); + for (URL url: list) { + sb.append(" ").append(url.toString()).append("\n"); + } + sb.append(" See the Java API client's troubleshooting documentation for more information.\n"); + throw new RuntimeException(sb.toString()); + } + } +} diff --git a/java-client/src/test/java/co/elastic/clients/util/DuplicateResourceFinderTest.java b/java-client/src/test/java/co/elastic/clients/util/DuplicateResourceFinderTest.java new file mode 100644 index 000000000..3b7f7ef40 --- /dev/null +++ b/java-client/src/test/java/co/elastic/clients/util/DuplicateResourceFinderTest.java @@ -0,0 +1,40 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you 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 co.elastic.clients.util; + +import com.fasterxml.jackson.databind.ext.CoreXMLSerializers; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class DuplicateResourceFinderTest extends Assertions { + + @Test + public void testDuplicateCheck() { + + Exception e = assertThrows(RuntimeException.class, () -> { + DuplicateResourceFinder.ensureClassUniqueness(CoreXMLSerializers.class); + }); + assertTrue(e.getMessage().contains("Several versions of")); + + // Disabling the test should not throw an exception + DuplicateResourceFinder.enableCheck(false); + DuplicateResourceFinder.ensureClassUniqueness(CoreXMLSerializers.class); + } +} diff --git a/java-client/src/test/java/com/fasterxml/jackson/databind/ext/CoreXMLSerializers.java b/java-client/src/test/java/com/fasterxml/jackson/databind/ext/CoreXMLSerializers.java new file mode 100644 index 000000000..bf4a2d62b --- /dev/null +++ b/java-client/src/test/java/com/fasterxml/jackson/databind/ext/CoreXMLSerializers.java @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you 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.fasterxml.jackson.databind.ext; + +// A "duplicate" of a class that exists in the project's dependencies. +// It's not actually used by the project, so the duplicate won't harm and is used in DuplicateResourceFinderTest +public class CoreXMLSerializers { +}