diff --git a/jaxws-ri/runtime/rt/src/main/java/com/sun/xml/ws/assembler/MetroConfigLoader.java b/jaxws-ri/runtime/rt/src/main/java/com/sun/xml/ws/assembler/MetroConfigLoader.java index 4e7558e90..b0b55e61a 100644 --- a/jaxws-ri/runtime/rt/src/main/java/com/sun/xml/ws/assembler/MetroConfigLoader.java +++ b/jaxws-ri/runtime/rt/src/main/java/com/sun/xml/ws/assembler/MetroConfigLoader.java @@ -32,6 +32,8 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; /** @@ -53,6 +55,9 @@ class MetroConfigLoader { private MetroConfigName defaultTubesConfigNames; + private final static Lock jaxbContextLock = new ReentrantLock(); + private static volatile JAXBContext jaxbContext; + private interface TubeFactoryListResolver { TubeFactoryList getFactories(TubelineDefinition td); @@ -259,8 +264,20 @@ private static InputStream getConfigInputStream(URL resourceUrl) throws IOExcept } private static JAXBContext createJAXBContext() throws Exception { - // usage from JAX-WS/Metro/Glassfish - return JAXBContext.newInstance(MetroConfig.class.getPackage().getName()); + // JAXBContext is thread-safe and expensive to create, so caching it for re-use + if (jaxbContext != null) { + return jaxbContext; + } + jaxbContextLock.lock(); + try { + if (jaxbContext == null) { + // usage from JAX-WS/Metro/Glassfish + jaxbContext = JAXBContext.newInstance(MetroConfig.class.getPackage().getName()); + } + return jaxbContext; + } finally { + jaxbContextLock.unlock(); + } } diff --git a/jaxws-ri/runtime/rt/src/main/java/com/sun/xml/ws/assembler/TubelineAssemblyController.java b/jaxws-ri/runtime/rt/src/main/java/com/sun/xml/ws/assembler/TubelineAssemblyController.java index dbc707b12..793808db4 100644 --- a/jaxws-ri/runtime/rt/src/main/java/com/sun/xml/ws/assembler/TubelineAssemblyController.java +++ b/jaxws-ri/runtime/rt/src/main/java/com/sun/xml/ws/assembler/TubelineAssemblyController.java @@ -12,6 +12,7 @@ import com.sun.istack.NotNull; import com.sun.istack.logging.Logger; +import com.sun.xml.ws.api.server.Container; import com.sun.xml.ws.assembler.dev.ClientTubelineAssemblyContext; import com.sun.xml.ws.assembler.dev.ServerTubelineAssemblyContext; import com.sun.xml.ws.resources.TubelineassemblyMessages; @@ -23,6 +24,9 @@ import java.net.URISyntaxException; import java.util.Collection; import java.util.LinkedList; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantLock; /** * @@ -32,6 +36,11 @@ final class TubelineAssemblyController { private final MetroConfigName metroConfigName; + private static final Map tubeFactoryListCache = new ConcurrentHashMap<>(); + private static final ReentrantLock cacheLock = new ReentrantLock(); + private static final int MAX_CACHE_SIZE = 100; + + TubelineAssemblyController(MetroConfigName metroConfigName) { this.metroConfigName = metroConfigName; } @@ -59,8 +68,21 @@ Collection getTubeCreators(ClientTubelineAssemblyContext context) { endpointUri = null; } - MetroConfigLoader configLoader = new MetroConfigLoader(context.getContainer(), metroConfigName); - return initializeTubeCreators(configLoader.getClientSideTubeFactories(endpointUri)); + TubeFactoryList tubeFactoryList = tubeFactoryListCache.get(context.getContainer()); + if (tubeFactoryList == null) { + MetroConfigLoader configLoader = new MetroConfigLoader(context.getContainer(), metroConfigName); + tubeFactoryList = configLoader.getClientSideTubeFactories(endpointUri); + cacheLock.lock(); + try { + if (tubeFactoryListCache.size() >= MAX_CACHE_SIZE) { + tubeFactoryListCache.remove(tubeFactoryListCache.keySet().iterator().next()); + } + tubeFactoryListCache.put(context.getContainer(), tubeFactoryList); + } finally { + cacheLock.unlock(); + } + } + return initializeTubeCreators(tubeFactoryList); } /** diff --git a/jaxws-ri/runtime/rt/src/main/java/com/sun/xml/ws/util/ServiceClassLoaderCacheKey.java b/jaxws-ri/runtime/rt/src/main/java/com/sun/xml/ws/util/ServiceClassLoaderCacheKey.java new file mode 100644 index 000000000..55d66ade2 --- /dev/null +++ b/jaxws-ri/runtime/rt/src/main/java/com/sun/xml/ws/util/ServiceClassLoaderCacheKey.java @@ -0,0 +1,34 @@ +package com.sun.xml.ws.util; + +import java.util.Objects; + +public class ServiceClassLoaderCacheKey { + private final Class service; + private final ClassLoader loader; + + public ServiceClassLoaderCacheKey(Class service, ClassLoader loader) { + this.service = service; + this.loader = loader; + } + + public Class getService() { + return service; + } + + public ClassLoader getLoader() { + return loader; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ServiceClassLoaderCacheKey)) return false; + ServiceClassLoaderCacheKey that = (ServiceClassLoaderCacheKey) o; + return Objects.equals(service, that.service) && Objects.equals(loader, that.loader); + } + + @Override + public int hashCode() { + return Objects.hash(service, loader); + } +} diff --git a/jaxws-ri/runtime/rt/src/main/java/com/sun/xml/ws/util/ServiceFinder.java b/jaxws-ri/runtime/rt/src/main/java/com/sun/xml/ws/util/ServiceFinder.java index 5a16a6f36..165dd8ac6 100644 --- a/jaxws-ri/runtime/rt/src/main/java/com/sun/xml/ws/util/ServiceFinder.java +++ b/jaxws-ri/runtime/rt/src/main/java/com/sun/xml/ws/util/ServiceFinder.java @@ -23,9 +23,12 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.ServiceLoader; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantLock; /** @@ -116,22 +119,26 @@ public final class ServiceFinder implements Iterable { private final @NotNull Class serviceClass; - private final @NotNull ServiceLoader serviceLoader; + private final @NotNull Iterable serviceLoaderIterable; private final @Nullable ComponentEx component; + private static final Map noResultServiceLoaderCache = new ConcurrentHashMap<>(); + private static final ReentrantLock cacheLock = new ReentrantLock(); + private static final int MAX_CACHE_SIZE = 100; + public static ServiceFinder find(@NotNull Class service, @Nullable ClassLoader loader, Component component) { ClassLoader cl = loader == null ? Thread.currentThread().getContextClassLoader() : loader; - return find(service, component, ServiceLoader.load(service, cl)); + return find(service, component, retrieveServiceLoaderFromCacheOrCreateNew(service, cl)); } - public static ServiceFinder find(@NotNull Class service, Component component, @NotNull ServiceLoader serviceLoader) { + public static ServiceFinder find(@NotNull Class service, Component component, @NotNull Iterable serviceLoaderIterable) { Class svc = Objects.requireNonNull(service); - ServiceLoader sl = Objects.requireNonNull(serviceLoader); + Iterable sl = Objects.requireNonNull(serviceLoaderIterable); return new ServiceFinder<>(svc, component, sl); } public static ServiceFinder find(@NotNull Class service, Component component) { - return find(service, component, ServiceLoader.load(service, Thread.currentThread().getContextClassLoader())); + return find(service, component, retrieveServiceLoaderFromCacheOrCreateNew(service, Thread.currentThread().getContextClassLoader())); } /** @@ -184,17 +191,17 @@ public static ServiceFinder find(@NotNull Class service, @Nullable Cla * @see #find(Class, ClassLoader) */ public static ServiceFinder find(@NotNull Class service) { - return find(service, ServiceLoader.load(service, Thread.currentThread().getContextClassLoader())); + return find(service, retrieveServiceLoaderFromCacheOrCreateNew(service, Thread.currentThread().getContextClassLoader())); } - public static ServiceFinder find(@NotNull Class service, @NotNull ServiceLoader serviceLoader) { - return find(service, ContainerResolver.getInstance().getContainer(), serviceLoader); + public static ServiceFinder find(@NotNull Class service, @NotNull Iterable serviceLoaderIterable) { + return find(service, ContainerResolver.getInstance().getContainer(), serviceLoaderIterable); } - private ServiceFinder(Class service, Component component, ServiceLoader serviceLoader) { + private ServiceFinder(Class service, Component component, Iterable serviceLoaderIterable) { this.serviceClass = service; this.component = getComponentEx(component); - this.serviceLoader = serviceLoader; + this.serviceLoaderIterable = serviceLoaderIterable; } /** @@ -209,7 +216,7 @@ private ServiceFinder(Class service, Component component, ServiceLoader se @Override @SuppressWarnings("unchecked") public Iterator iterator() { - Iterator it = serviceLoader.iterator(); + Iterator it = serviceLoaderIterable.iterator(); return component != null ? new CompositeIterator<>(component.getIterableSPI(serviceClass).iterator(), it) : it; @@ -238,6 +245,28 @@ private static ComponentEx getComponentEx(Component component) { return component != null ? new ComponentExWrapper(component) : null; } + private static Iterable retrieveServiceLoaderFromCacheOrCreateNew(Class service, ClassLoader cl) { + ServiceClassLoaderCacheKey cacheKey = new ServiceClassLoaderCacheKey<>(service, cl); + Iterable cachedServiceLoaderIterable = noResultServiceLoaderCache.get(cacheKey); + if (cachedServiceLoaderIterable != null) { + return cachedServiceLoaderIterable; + } + ServiceLoader serviceLoader = ServiceLoader.load(service, cl); + if (!serviceLoader.iterator().hasNext()) { + cacheLock.lock(); + try { + if (noResultServiceLoaderCache.size() >= MAX_CACHE_SIZE) { + noResultServiceLoaderCache.remove(noResultServiceLoaderCache.keySet().iterator().next()); + } + noResultServiceLoaderCache.put(cacheKey, Collections.emptyList()); + } finally { + cacheLock.unlock(); + } + return Collections.emptyList(); + } + return serviceLoader; + } + private static class ComponentExWrapper implements ComponentEx { private final Component component; diff --git a/jaxws-ri/runtime/rt/src/test/java/com/sun/xml/ws/streaming/XMLReaderCompositeTest.java b/jaxws-ri/runtime/rt/src/test/java/com/sun/xml/ws/streaming/XMLReaderCompositeTest.java index acfd36520..c908649d7 100644 --- a/jaxws-ri/runtime/rt/src/test/java/com/sun/xml/ws/streaming/XMLReaderCompositeTest.java +++ b/jaxws-ri/runtime/rt/src/test/java/com/sun/xml/ws/streaming/XMLReaderCompositeTest.java @@ -13,6 +13,7 @@ //import static org.junit.Assert.*; import java.io.ByteArrayInputStream; +import java.util.Locale; import javax.xml.namespace.QName; import javax.xml.stream.XMLInputFactory; @@ -67,6 +68,7 @@ public void testComposite() throws Exception { } public void testInvalidXML() throws Exception { + Locale.setDefault(Locale.ENGLISH); XMLInputFactory fac = XMLInputFactory.newFactory(); ByteArrayInputStream in = new ByteArrayInputStream("test".getBytes()); XMLStreamReader r = fac.createXMLStreamReader(in); @@ -74,8 +76,8 @@ public void testInvalidXML() throws Exception { XMLStreamReaderUtil.readRest(r); fail("Exception should be thrown"); } catch (Exception e) { - assertEquals("XML reader error: Unexpected character 't' (code 116) in prolog; expected '<'\n" + - " at [row,col {unknown-source}]: [1,1]", e.getMessage()); + assertEquals("XML reader error: Unexpected character 't' (code 116) in prolog; expected '<'" + + System.lineSeparator() + " at [row,col {unknown-source}]: [1,1]", e.getMessage()); } } diff --git a/jaxws-ri/runtime/rt/src/test/java/com/sun/xml/ws/util/pipe/AbstractSchemaValidationTubeTest.java b/jaxws-ri/runtime/rt/src/test/java/com/sun/xml/ws/util/pipe/AbstractSchemaValidationTubeTest.java index 46ce4c9bb..b36e4aa5b 100644 --- a/jaxws-ri/runtime/rt/src/test/java/com/sun/xml/ws/util/pipe/AbstractSchemaValidationTubeTest.java +++ b/jaxws-ri/runtime/rt/src/test/java/com/sun/xml/ws/util/pipe/AbstractSchemaValidationTubeTest.java @@ -45,11 +45,14 @@ protected void setUp() throws Exception { protected void tearDown() throws Exception { super.tearDown(); } - + public void testCreateSameTnsPseudoSchema() throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException, TransformerException { - StringBuilder sb = new StringBuilder("\n"); - sb.append("\n"); - sb.append("\n"); + StringBuilder sb = new StringBuilder(""); + sb.append(System.lineSeparator()); + sb.append(""); + sb.append(System.lineSeparator()); + sb.append(""); + sb.append(System.lineSeparator()); sb.append(""); String strResult_null = runCreateSameTnsPseudoSchema(null); diff --git a/jaxws-ri/tests/unit/testcases/whitebox/exception_mapping/client/DynamicExceptionMappingTest.java b/jaxws-ri/tests/unit/testcases/whitebox/exception_mapping/client/DynamicExceptionMappingTest.java index d567e4663..57ac49b87 100644 --- a/jaxws-ri/tests/unit/testcases/whitebox/exception_mapping/client/DynamicExceptionMappingTest.java +++ b/jaxws-ri/tests/unit/testcases/whitebox/exception_mapping/client/DynamicExceptionMappingTest.java @@ -12,6 +12,7 @@ import junit.framework.TestCase; +import java.util.Locale; import jakarta.xml.ws.Endpoint; import com.sun.xml.ws.model.RuntimeModelerException; @@ -31,6 +32,7 @@ public void testDynamicExceptionMapping() throws Exception { } public void testNegativeDynamicExceptionMapping() throws Exception { + Locale.setDefault(Locale.ENGLISH); int port = Util.getFreePort(); String address = "http://localhost:" + port + "/hello"; Endpoint endpoint = Endpoint.create(new NegativeEchoImpl()); diff --git a/jaxws-ri/tests/unit/testcases/wsa/w3c/fromwsdl/crinterop_s11/client/NonAnonymousClientTest.java b/jaxws-ri/tests/unit/testcases/wsa/w3c/fromwsdl/crinterop_s11/client/NonAnonymousClientTest.java index b7aef5b0e..0643f99d4 100644 --- a/jaxws-ri/tests/unit/testcases/wsa/w3c/fromwsdl/crinterop_s11/client/NonAnonymousClientTest.java +++ b/jaxws-ri/tests/unit/testcases/wsa/w3c/fromwsdl/crinterop_s11/client/NonAnonymousClientTest.java @@ -13,6 +13,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.Exchanger; import java.util.Iterator; +import java.util.Locale; import jakarta.xml.soap.SOAPMessage; import jakarta.xml.soap.SOAPFault; import jakarta.xml.soap.SOAPBody; @@ -85,6 +86,7 @@ public void test1150() throws Exception { * SOAP 1.1 two-way message with a non-anonymous ReplyTo address and a none FaultTo. */ public void test1152() throws Exception { + Locale.setDefault(Locale.ENGLISH); try { invoke(createDispatchForNonAnonymousWithWSDLWithoutAddressing(), MESSAGES.getNonAnonymousReplyToNoneFaultToMessage(), diff --git a/jaxws-ri/tools/wscompile/src/test/java/com/sun/tools/ws/test/wsdl/parser/WSImportSecTestBase.java b/jaxws-ri/tools/wscompile/src/test/java/com/sun/tools/ws/test/wsdl/parser/WSImportSecTestBase.java index 1a8166efb..eb890b705 100644 --- a/jaxws-ri/tools/wscompile/src/test/java/com/sun/tools/ws/test/wsdl/parser/WSImportSecTestBase.java +++ b/jaxws-ri/tools/wscompile/src/test/java/com/sun/tools/ws/test/wsdl/parser/WSImportSecTestBase.java @@ -13,6 +13,7 @@ import com.sun.tools.ws.wscompile.WsimportTool; import java.io.ByteArrayOutputStream; import java.io.PrintStream; +import java.util.Locale; import java.util.Objects; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -76,6 +77,7 @@ public WSImportTest(String[] toolArgs, String expectedMessage, String unexpected public void test() throws Exception { + Locale.setDefault(Locale.ENGLISH); System.out.println( "Launching wsimport tool with the following args:"); for(String arg:toolArgs) { System.out.println("\t"+arg);