Skip to content

Commit

Permalink
Json factory methods are very inefficient #154
Browse files Browse the repository at this point in the history
Signed-off-by: Jorge Bescos Gascon <jorge.bescos.gascon@oracle.com>
  • Loading branch information
jbescos authored and lukasj committed Nov 3, 2021
1 parent e621909 commit 28a5ec5
Show file tree
Hide file tree
Showing 3 changed files with 242 additions and 11 deletions.
115 changes: 104 additions & 11 deletions api/src/main/java/jakarta/json/spi/JsonProvider.java
Expand Up @@ -16,22 +16,40 @@

package jakarta.json.spi;

import jakarta.json.*;
import jakarta.json.stream.JsonGenerator;
import jakarta.json.stream.JsonGeneratorFactory;
import jakarta.json.stream.JsonParser;
import jakarta.json.stream.JsonParserFactory;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.ServiceLoader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Optional;
import java.util.ServiceLoader;

import jakarta.json.JsonArray;
import jakarta.json.JsonArrayBuilder;
import jakarta.json.JsonBuilderFactory;
import jakarta.json.JsonException;
import jakarta.json.JsonMergePatch;
import jakarta.json.JsonNumber;
import jakarta.json.JsonObject;
import jakarta.json.JsonObjectBuilder;
import jakarta.json.JsonPatch;
import jakarta.json.JsonPatchBuilder;
import jakarta.json.JsonPointer;
import jakarta.json.JsonReader;
import jakarta.json.JsonReaderFactory;
import jakarta.json.JsonString;
import jakarta.json.JsonStructure;
import jakarta.json.JsonValue;
import jakarta.json.JsonWriter;
import jakarta.json.JsonWriterFactory;
import jakarta.json.stream.JsonGenerator;
import jakarta.json.stream.JsonGeneratorFactory;
import jakarta.json.stream.JsonParser;
import jakarta.json.stream.JsonParserFactory;

/**
* Service provider for JSON processing objects.
Expand All @@ -43,6 +61,10 @@
*/
public abstract class JsonProvider {

/**
* The name of the property that contains the name of the class capable of creating new JsonProvider objects.
*/
public static final String JSONP_PROVIDER_FACTORY = "jakarta.json.spi.JsonProvider";
/**
* A constant representing the name of the default
* {@code JsonProvider} implementation class.
Expand All @@ -57,15 +79,29 @@ protected JsonProvider() {
}

/**
* Creates a JSON provider object. The provider is loaded using the
* {@link ServiceLoader#load(Class)} method. If there are no available
* service providers, this method returns the default service provider.
* Creates a JSON provider object.
*
* Implementation discovery consists of following steps:
* <ol>
* <li>If the system property {@value #JSONP_PROVIDER_FACTORY} exists,
* then its value is assumed to be the provider factory class.
* This phase of the look up enables per-JVM override of the JsonProvider implementation.</li>
* <li>The provider is loaded using the {@link ServiceLoader#load(Class)} method.</li>
* <li>If all the steps above fail, then the rest of the look up is unspecified. That said,
* the recommended behavior is to simply look for some hard-coded platform default Jakarta
* JSON Processing implementation. This phase of the look up is so that a platform can have
* its own Jakarta JSON Processing implementation as the last resort.</li>
* </ol>
* Users are recommended to cache the result of this method.
*
* @see ServiceLoader
* @return a JSON provider
*/
public static JsonProvider provider() {
String factory = System.getProperty(JsonProvider.class.getName());
if (factory != null) {
return newInstance(LazyFactoryLoader.JSON_PROVIDER);
}
ServiceLoader<JsonProvider> loader = ServiceLoader.load(JsonProvider.class);
Iterator<JsonProvider> it = loader.iterator();
if (it.hasNext()) {
Expand All @@ -84,6 +120,35 @@ public static JsonProvider provider() {
}
}

/**
* Creates a new instance from the specified class
* @param clazz class to instance
* @return the JsonProvider instance
* @throws IllegalArgumentException for reflection issues
*/
private static JsonProvider newInstance(Class<JsonProvider> clazz) {
checkPackageAccess(clazz.getName());
try {
return clazz.getConstructor().newInstance();
} catch (ReflectiveOperationException e) {
throw new IllegalArgumentException("Cannot instance " + clazz.getName(), e);
}
}

/**
* Make sure that the current thread has an access to the package of the given name.
* @param className The class name to check.
*/
private static void checkPackageAccess(String className) {
SecurityManager s = System.getSecurityManager();
if (s != null) {
int i = className.lastIndexOf('.');
if (i != -1) {
s.checkPackageAccess(className.substring(0, i));
}
}
}

/**
* Creates a JSON parser from a character stream.
*
Expand Down Expand Up @@ -508,4 +573,32 @@ public JsonNumber createValue(Number number) {
throw new UnsupportedOperationException(number + " type is not known");
}
}

/**
* Lazy loads the class specified in System property with the key JSONP_PROVIDER_FACTORY.
* If no property is set, the value of {@link #JSON_PROVIDER} will be null.
* In case of errors an IllegalStateException is thrown.
*
*/
@SuppressWarnings("unchecked")
private static class LazyFactoryLoader {

/**
* JSON provider class
*/
private static final Class<JsonProvider> JSON_PROVIDER;

static {
String className = System.getProperty(JSONP_PROVIDER_FACTORY);
if (className != null) {
try {
JSON_PROVIDER = (Class<JsonProvider>) Class.forName(className);
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("Cannot instance " + className, e);
}
} else {
JSON_PROVIDER = null;
}
}
}
}
2 changes: 2 additions & 0 deletions impl-tck/tck-tests/pom.xml
Expand Up @@ -57,6 +57,8 @@
<trimStackTrace>false</trimStackTrace>
<failIfNoTests>true</failIfNoTests>
<argLine>--add-opens java.base/java.lang=ALL-UNNAMED</argLine>
<forkCount>1</forkCount>
<reuseForks>false</reuseForks>
</configuration>
</plugin>
</plugins>
Expand Down
@@ -0,0 +1,136 @@
/*
* Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package jakarta.jsonp.tck.api.provider;

import static org.junit.Assert.assertEquals;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.util.Map;

import org.junit.Test;

import jakarta.json.JsonArrayBuilder;
import jakarta.json.JsonBuilderFactory;
import jakarta.json.JsonObjectBuilder;
import jakarta.json.JsonReader;
import jakarta.json.JsonReaderFactory;
import jakarta.json.JsonWriter;
import jakarta.json.JsonWriterFactory;
import jakarta.json.spi.JsonProvider;
import jakarta.json.stream.JsonGenerator;
import jakarta.json.stream.JsonGeneratorFactory;
import jakarta.json.stream.JsonParser;
import jakarta.json.stream.JsonParserFactory;

/**
* Tests related to JsonProvider.
*
*/
public class JsonProviderTest {

/**
* Verifies it is possible to obtain the JsonProvider implementation from a System property.
*/
@Test
public void systemProperty() {
System.setProperty(JsonProvider.JSONP_PROVIDER_FACTORY, DummyJsonProvider.class.getName());
JsonProvider provider = JsonProvider.provider();
assertEquals(DummyJsonProvider.class, provider.getClass());
}

public static class DummyJsonProvider extends JsonProvider {

@Override
public JsonParser createParser(Reader reader) {
return null;
}

@Override
public JsonParser createParser(InputStream in) {
return null;
}

@Override
public JsonParserFactory createParserFactory(Map<String, ?> config) {
return null;
}

@Override
public JsonGenerator createGenerator(Writer writer) {
return null;
}

@Override
public JsonGenerator createGenerator(OutputStream out) {
return null;
}

@Override
public JsonGeneratorFactory createGeneratorFactory(Map<String, ?> config) {
return null;
}

@Override
public JsonReader createReader(Reader reader) {
return null;
}

@Override
public JsonReader createReader(InputStream in) {
return null;
}

@Override
public JsonWriter createWriter(Writer writer) {
return null;
}

@Override
public JsonWriter createWriter(OutputStream out) {
return null;
}

@Override
public JsonWriterFactory createWriterFactory(Map<String, ?> config) {
return null;
}

@Override
public JsonReaderFactory createReaderFactory(Map<String, ?> config) {
return null;
}

@Override
public JsonObjectBuilder createObjectBuilder() {
return null;
}

@Override
public JsonArrayBuilder createArrayBuilder() {
return null;
}

@Override
public JsonBuilderFactory createBuilderFactory(Map<String, ?> config) {
return null;
}

}
}

0 comments on commit 28a5ec5

Please sign in to comment.