Author: rmannibucau
Date: Mon Sep 12 10:32:14 2016
New Revision: 1760332
URL: http://svn.apache.org/viewvc?rev=1760332&view=rev
Log:
ensuring using Json class doesn't lead to perf issues caching the provider per classloader
(we can't do better without being specific)
Added:
geronimo/specs/trunk/geronimo-json_1.0_spec/src/test/
geronimo/specs/trunk/geronimo-json_1.0_spec/src/test/java/
geronimo/specs/trunk/geronimo-json_1.0_spec/src/test/java/javax/
geronimo/specs/trunk/geronimo-json_1.0_spec/src/test/java/javax/json/
geronimo/specs/trunk/geronimo-json_1.0_spec/src/test/java/javax/json/spi/
geronimo/specs/trunk/geronimo-json_1.0_spec/src/test/java/javax/json/spi/JsonProviderTest.java
geronimo/specs/trunk/geronimo-json_1.0_spec/src/test/resources/
geronimo/specs/trunk/geronimo-json_1.0_spec/src/test/resources/META-INF/
geronimo/specs/trunk/geronimo-json_1.0_spec/src/test/resources/META-INF/services/
geronimo/specs/trunk/geronimo-json_1.0_spec/src/test/resources/META-INF/services/javax.json.spi.JsonProvider
Modified:
geronimo/specs/trunk/geronimo-json_1.0_spec/pom.xml
geronimo/specs/trunk/geronimo-json_1.0_spec/src/main/java/javax/json/spi/JsonProvider.java
Modified: geronimo/specs/trunk/geronimo-json_1.0_spec/pom.xml
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-json_1.0_spec/pom.xml?rev=1760332&r1=1760331&r2=1760332&view=diff
==============================================================================
--- geronimo/specs/trunk/geronimo-json_1.0_spec/pom.xml (original)
+++ geronimo/specs/trunk/geronimo-json_1.0_spec/pom.xml Mon Sep 12 10:32:14 2016
@@ -68,6 +68,15 @@
</repository>
</repositories>
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.12</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
<build>
<plugins>
<plugin>
Modified: geronimo/specs/trunk/geronimo-json_1.0_spec/src/main/java/javax/json/spi/JsonProvider.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-json_1.0_spec/src/main/java/javax/json/spi/JsonProvider.java?rev=1760332&r1=1760331&r2=1760332&view=diff
==============================================================================
--- geronimo/specs/trunk/geronimo-json_1.0_spec/src/main/java/javax/json/spi/JsonProvider.java
(original)
+++ geronimo/specs/trunk/geronimo-json_1.0_spec/src/main/java/javax/json/spi/JsonProvider.java
Mon Sep 12 10:32:14 2016
@@ -35,102 +35,30 @@ import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
+import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
-import java.util.ArrayList;
import java.util.Enumeration;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.ServiceLoader;
+import java.util.WeakHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
public abstract class JsonProvider {
private static final String DEFAULT_PROVIDER = "org.apache.johnzon.core.JsonProviderImpl";
+ private static final Cache CACHE = new Cache();
+
protected JsonProvider() {
// no-op
}
public static JsonProvider provider() {
- if (System.getSecurityManager() != null) {
- return AccessController.doPrivileged(new PrivilegedAction<JsonProvider>()
{
- public JsonProvider run() {
- return doLoadProvider();
- }
- });
- }
- return doLoadProvider();
- }
-
- private static JsonProvider doLoadProvider() throws JsonException {
- final ClassLoader tccl = Thread.currentThread().getContextClassLoader();
- try {
- final Class<?> clazz = Class.forName("org.apache.geronimo.osgi.locator.ProviderLocator");
- final Method getServices = clazz.getDeclaredMethod("getServices", String.class,
Class.class, ClassLoader.class);
- final List<JsonProvider> osgiProviders = (List<JsonProvider>) getServices.invoke(null,
JsonProvider.class.getName(), JsonProvider.class, tccl);
- if (osgiProviders != null && !osgiProviders.isEmpty()) {
- return osgiProviders.iterator().next();
- }
- } catch (final Throwable e) {
- // locator not available, try normal mode
- }
-
- // don't use Class.forName() to avoid to bind class to tccl if thats a classloader
facade
- // so implementing a simple SPI when ProviderLocator is not here
- final String name = "META-INF/services/" + JsonProvider.class.getName();
- try {
- Enumeration<URL> configs;
- if (tccl == null) {
- configs = ClassLoader.getSystemResources(name);
- } else {
- configs = tccl.getResources(name);
- }
-
- if (configs.hasMoreElements()) {
- InputStream in = null;
- BufferedReader r = null;
- final List<String> names = new ArrayList<String>();
- try {
- in = configs.nextElement().openStream();
- r = new BufferedReader(new InputStreamReader(in, "utf-8"));
- String l;
- while ((l = r.readLine()) != null) {
- if (l.startsWith("#")) {
- continue;
- }
- return JsonProvider.class.cast(tccl.loadClass(l).newInstance());
- }
- } catch (final IOException x) {
- // no-op
- } finally {
- try {
- if (r != null) {
- r.close();
- }
- } catch (final IOException y) {
- // no-op
- }
- try {
- if (in != null) {
- in.close();
- }
- } catch (final IOException y) {
- // no-op
- }
- }
- }
- } catch (final Exception ex) {
- // no-op
- }
-
- try {
- final Class<?> clazz = tccl.loadClass(DEFAULT_PROVIDER);
- return JsonProvider.class.cast(clazz.newInstance());
- } catch (final Throwable cnfe) {
- throw new JsonException(DEFAULT_PROVIDER + " not found", cnfe);
- }
+ return CACHE.get();
}
public abstract JsonParser createParser(Reader reader);
@@ -162,5 +90,138 @@ public abstract class JsonProvider {
public abstract JsonArrayBuilder createArrayBuilder();
public abstract JsonBuilderFactory createBuilderFactory(Map<String, ?> config);
+
+ private static class Cache {
+ private final WeakHashMap<ClassLoader, WeakReference<JsonProvider>> cachingProviders
= new WeakHashMap<ClassLoader, WeakReference<JsonProvider>>();
+ private final ReadWriteLock lock = new ReentrantReadWriteLock();
+
+ private JsonProvider get() { // in term of synchro we don't prevent to load multiple
times the provider
+ ClassLoader key = Thread.currentThread().getContextClassLoader();
+ if (key == null) {
+ key = ClassLoader.getSystemClassLoader();
+ }
+
+ final WeakReference<JsonProvider> reference;
+ final Lock readLock = this.lock.readLock();
+ readLock.lock();
+ try {
+ reference = cachingProviders.get(key);
+ } finally {
+ readLock.unlock();
+ }
+
+ JsonProvider provider = null;
+ if (reference != null) {
+ provider = reference.get();
+ }
+ if (provider != null) {
+ return provider;
+ }
+
+ if (System.getSecurityManager() != null) {
+ provider = AccessController.doPrivileged(new PrivilegedAction<JsonProvider>()
{
+ public JsonProvider run() {
+ return doLoadProvider();
+ }
+ });
+ } else {
+ provider = doLoadProvider();
+ }
+
+ final Lock writeLock = this.lock.writeLock();
+ writeLock.lock();
+ try {
+ boolean put = true;
+ final WeakReference<JsonProvider> existing = cachingProviders.get(key);
+ if (existing != null) {
+ final JsonProvider p = existing.get();
+ if (p != null) {
+ provider = p;
+ put = false;
+ }
+ }
+ if (put) {
+ cachingProviders.put(key, new WeakReference<JsonProvider>(provider));
+ }
+ } finally {
+ writeLock.unlock();
+ }
+
+ return provider;
+ }
+
+ private static JsonProvider doLoadProvider() throws JsonException {
+ ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+ if (tccl == null) {
+ tccl = ClassLoader.getSystemClassLoader();
+ }
+ try {
+ final Class<?> clazz = Class.forName("org.apache.geronimo.osgi.locator.ProviderLocator");
+ final Method getServices = clazz.getDeclaredMethod("getServices", String.class,
Class.class, ClassLoader.class);
+ final List<JsonProvider> osgiProviders = (List<JsonProvider>)
getServices.invoke(null, JsonProvider.class.getName(), JsonProvider.class, tccl);
+ if (osgiProviders != null && !osgiProviders.isEmpty()) {
+ return osgiProviders.iterator().next();
+ }
+ } catch (final Throwable e) {
+ // locator not available, try normal mode
+ }
+
+ final String className = System.getProperty(JsonProvider.class.getName());
+ if (className != null) {
+ try {
+ return JsonProvider.class.cast(tccl.loadClass(className.trim()).newInstance());
+ } catch (final Exception e) {
+ throw new JsonException("Specified provider as system property can't
be loaded: " + className, e);
+ }
+ }
+
+ // don't use Class.forName() to avoid to bind class to tccl if thats a classloader
facade
+ // so implementing a simple SPI when ProviderLocator is not here
+ final String name = "META-INF/services/" + JsonProvider.class.getName();
+ try {
+ final Enumeration<URL> configs = tccl.getResources(name);
+ if (configs.hasMoreElements()) {
+ InputStream in = null;
+ BufferedReader r = null;
+ try {
+ in = configs.nextElement().openStream();
+ r = new BufferedReader(new InputStreamReader(in, "utf-8"));
+ String l;
+ while ((l = r.readLine()) != null) {
+ if (l.startsWith("#")) {
+ continue;
+ }
+ return JsonProvider.class.cast(tccl.loadClass(l).newInstance());
+ }
+ } catch (final IOException x) {
+ // no-op
+ } finally {
+ try {
+ if (r != null) {
+ r.close();
+ }
+ } catch (final IOException y) {
+ // no-op
+ }
+ try {
+ if (in != null) {
+ in.close();
+ }
+ } catch (final IOException y) {
+ // no-op
+ }
+ }
+ }
+ } catch (final Exception ex) {
+ // no-op
+ }
+
+ try {
+ return JsonProvider.class.cast(tccl.loadClass(DEFAULT_PROVIDER).newInstance());
+ } catch (final Throwable cnfe) {
+ throw new JsonException(DEFAULT_PROVIDER + " not found", cnfe);
+ }
+ }
+ }
}
Added: geronimo/specs/trunk/geronimo-json_1.0_spec/src/test/java/javax/json/spi/JsonProviderTest.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-json_1.0_spec/src/test/java/javax/json/spi/JsonProviderTest.java?rev=1760332&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-json_1.0_spec/src/test/java/javax/json/spi/JsonProviderTest.java
(added)
+++ geronimo/specs/trunk/geronimo-json_1.0_spec/src/test/java/javax/json/spi/JsonProviderTest.java
Mon Sep 12 10:32:14 2016
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 javax.json.spi;
+
+import org.junit.Test;
+
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonBuilderFactory;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonReader;
+import javax.json.JsonReaderFactory;
+import javax.json.JsonWriter;
+import javax.json.JsonWriterFactory;
+import javax.json.stream.JsonGenerator;
+import javax.json.stream.JsonGeneratorFactory;
+import javax.json.stream.JsonParser;
+import javax.json.stream.JsonParserFactory;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Map;
+
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+
+public class JsonProviderTest {
+ @Test
+ public void cache() {
+ final JsonProvider provider = JsonProvider.provider();
+ for (int i = 0; i < 10; i++) {
+ final JsonProvider reload = JsonProvider.provider();
+ assertSame(provider, reload);
+ }
+ }
+
+ @Test
+ public void cacheByClassLoader() {
+ final JsonProvider provider = JsonProvider.provider();
+
+ final Thread thread = Thread.currentThread();
+ final URLClassLoader loader = new URLClassLoader(new URL[0], thread.getContextClassLoader());
+ thread.setContextClassLoader(loader);
+ try {
+ final JsonProvider subLoaderProvider = JsonProvider.provider();
+ for (int i = 0; i < 10; i++) {
+ final JsonProvider reload = JsonProvider.provider();
+ assertNotSame(provider, reload);
+ assertSame(subLoaderProvider, reload);
+ }
+ } finally {
+ thread.setContextClassLoader(loader.getParent());
+ }
+ }
+
+ public static class AProvider extends JsonProvider {
+ @Override
+ public JsonParser createParser(final Reader reader) {
+ return null;
+ }
+
+ @Override
+ public JsonParser createParser(final InputStream in) {
+ return null;
+ }
+
+ @Override
+ public JsonParserFactory createParserFactory(final Map<String, ?> config) {
+ return null;
+ }
+
+ @Override
+ public JsonGenerator createGenerator(final Writer writer) {
+ return null;
+ }
+
+ @Override
+ public JsonGenerator createGenerator(final OutputStream out) {
+ return null;
+ }
+
+ @Override
+ public JsonGeneratorFactory createGeneratorFactory(final Map<String, ?> config)
{
+ return null;
+ }
+
+ @Override
+ public JsonReader createReader(final Reader reader) {
+ return null;
+ }
+
+ @Override
+ public JsonReader createReader(final InputStream in) {
+ return null;
+ }
+
+ @Override
+ public JsonWriter createWriter(final Writer writer) {
+ return null;
+ }
+
+ @Override
+ public JsonWriter createWriter(final OutputStream out) {
+ return null;
+ }
+
+ @Override
+ public JsonWriterFactory createWriterFactory(final Map<String, ?> config) {
+ return null;
+ }
+
+ @Override
+ public JsonReaderFactory createReaderFactory(final Map<String, ?> config) {
+ return null;
+ }
+
+ @Override
+ public JsonObjectBuilder createObjectBuilder() {
+ return null;
+ }
+
+ @Override
+ public JsonArrayBuilder createArrayBuilder() {
+ return null;
+ }
+
+ @Override
+ public JsonBuilderFactory createBuilderFactory(final Map<String, ?> config)
{
+ return null;
+ }
+ }
+}
Added: geronimo/specs/trunk/geronimo-json_1.0_spec/src/test/resources/META-INF/services/javax.json.spi.JsonProvider
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-json_1.0_spec/src/test/resources/META-INF/services/javax.json.spi.JsonProvider?rev=1760332&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-json_1.0_spec/src/test/resources/META-INF/services/javax.json.spi.JsonProvider
(added)
+++ geronimo/specs/trunk/geronimo-json_1.0_spec/src/test/resources/META-INF/services/javax.json.spi.JsonProvider
Mon Sep 12 10:32:14 2016
@@ -0,0 +1 @@
+javax.json.spi.JsonProviderTest$AProvider
|