Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce cpu usage for proxy creation #687

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -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);
Expand Down Expand Up @@ -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();
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

/**
*
Expand All @@ -32,6 +36,11 @@ final class TubelineAssemblyController {

private final MetroConfigName metroConfigName;

private static final Map<Container, TubeFactoryList> tubeFactoryListCache = new ConcurrentHashMap<>();
private static final ReentrantLock cacheLock = new ReentrantLock();
private static final int MAX_CACHE_SIZE = 100;


TubelineAssemblyController(MetroConfigName metroConfigName) {
this.metroConfigName = metroConfigName;
}
Expand Down Expand Up @@ -59,8 +68,21 @@ Collection<TubeCreator> 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);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.sun.xml.ws.util;

import java.util.Objects;

public class ServiceClassLoaderCacheKey<T> {
private final Class<T> service;
private final ClassLoader loader;

public ServiceClassLoaderCacheKey(Class<T> service, ClassLoader loader) {
this.service = service;
this.loader = loader;
}

public Class<T> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;


/**
Expand Down Expand Up @@ -116,22 +119,26 @@
public final class ServiceFinder<T> implements Iterable<T> {

private final @NotNull Class<T> serviceClass;
private final @NotNull ServiceLoader<T> serviceLoader;
private final @NotNull Iterable<T> serviceLoaderIterable;
private final @Nullable ComponentEx component;

private static final Map<ServiceClassLoaderCacheKey, Iterable> noResultServiceLoaderCache = new ConcurrentHashMap<>();
private static final ReentrantLock cacheLock = new ReentrantLock();
private static final int MAX_CACHE_SIZE = 100;

public static <T> ServiceFinder<T> find(@NotNull Class<T> 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 <T> ServiceFinder<T> find(@NotNull Class<T> service, Component component, @NotNull ServiceLoader<T> serviceLoader) {
public static <T> ServiceFinder<T> find(@NotNull Class<T> service, Component component, @NotNull Iterable<T> serviceLoaderIterable) {
Class<T> svc = Objects.requireNonNull(service);
ServiceLoader<T> sl = Objects.requireNonNull(serviceLoader);
Iterable<T> sl = Objects.requireNonNull(serviceLoaderIterable);
return new ServiceFinder<>(svc, component, sl);
}

public static <T> ServiceFinder<T> find(@NotNull Class<T> service, Component component) {
return find(service, component, ServiceLoader.load(service, Thread.currentThread().getContextClassLoader()));
return find(service, component, retrieveServiceLoaderFromCacheOrCreateNew(service, Thread.currentThread().getContextClassLoader()));
}

/**
Expand Down Expand Up @@ -184,17 +191,17 @@ public static <T> ServiceFinder<T> find(@NotNull Class<T> service, @Nullable Cla
* @see #find(Class, ClassLoader)
*/
public static <T> ServiceFinder<T> find(@NotNull Class<T> service) {
return find(service, ServiceLoader.load(service, Thread.currentThread().getContextClassLoader()));
return find(service, retrieveServiceLoaderFromCacheOrCreateNew(service, Thread.currentThread().getContextClassLoader()));
}

public static <T> ServiceFinder<T> find(@NotNull Class<T> service, @NotNull ServiceLoader<T> serviceLoader) {
return find(service, ContainerResolver.getInstance().getContainer(), serviceLoader);
public static <T> ServiceFinder<T> find(@NotNull Class<T> service, @NotNull Iterable<T> serviceLoaderIterable) {
return find(service, ContainerResolver.getInstance().getContainer(), serviceLoaderIterable);
}

private ServiceFinder(Class<T> service, Component component, ServiceLoader<T> serviceLoader) {
private ServiceFinder(Class<T> service, Component component, Iterable<T> serviceLoaderIterable) {
this.serviceClass = service;
this.component = getComponentEx(component);
this.serviceLoader = serviceLoader;
this.serviceLoaderIterable = serviceLoaderIterable;
}

/**
Expand All @@ -209,7 +216,7 @@ private ServiceFinder(Class<T> service, Component component, ServiceLoader<T> se
@Override
@SuppressWarnings("unchecked")
public Iterator<T> iterator() {
Iterator<T> it = serviceLoader.iterator();
Iterator<T> it = serviceLoaderIterable.iterator();
return component != null
? new CompositeIterator<>(component.getIterableSPI(serviceClass).iterator(), it)
: it;
Expand Down Expand Up @@ -238,6 +245,28 @@ private static ComponentEx getComponentEx(Component component) {
return component != null ? new ComponentExWrapper(component) : null;
}

private static <T> Iterable<T> retrieveServiceLoaderFromCacheOrCreateNew(Class<T> service, ClassLoader cl) {
ServiceClassLoaderCacheKey<T> cacheKey = new ServiceClassLoaderCacheKey<>(service, cl);
Iterable<T> cachedServiceLoaderIterable = noResultServiceLoaderCache.get(cacheKey);
if (cachedServiceLoaderIterable != null) {
return cachedServiceLoaderIterable;
}
ServiceLoader<T> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -67,15 +68,16 @@ 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);
try {
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());
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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("<?xml version=\"1.0\" encoding=\"UTF-8\"?><xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\n");
sb.append("<xsd:include schemaLocation=\"a.xsd\"/>\n");
sb.append("<xsd:include schemaLocation=\"b.xsd\"/>\n");
StringBuilder sb = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?><xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">");
sb.append(System.lineSeparator());
sb.append("<xsd:include schemaLocation=\"a.xsd\"/>");
sb.append(System.lineSeparator());
sb.append("<xsd:include schemaLocation=\"b.xsd\"/>");
sb.append(System.lineSeparator());
sb.append("</xsd:schema>");

String strResult_null = runCreateSameTnsPseudoSchema(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import junit.framework.TestCase;

import java.util.Locale;
import jakarta.xml.ws.Endpoint;

import com.sun.xml.ws.model.RuntimeModelerException;
Expand All @@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down