All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.protostuff.runtime.RuntimeEnv Maven / Gradle / Ivy

Go to download

protostuff runtime for mobile devices (actually JREs with no sun.reflect.* packages)

The newest version!
//========================================================================
//Copyright 2007-2011 David Yu [email protected]
//------------------------------------------------------------------------
//Licensed 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 io.protostuff.runtime;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * The runtime environment.
 * 
 * @author David Yu
 * @created Jul 8, 2011
 */
public final class RuntimeEnv
{
    /**
     * {@code true} for JDKs 9, 10, 11, etc; {@code false} for 1.8 and below.
     */
    public static final boolean JAVA_9_AND_ABOVE = !System.getProperty("java.specification.version", "1.0").startsWith("1.");
    
    /**
     * Returns true if serializing enums by name is activated. Disabled by default.
     */
    public static final boolean ENUMS_BY_NAME;

    /**
     * Enabled by default. For security purposes, you probably would want to register all known classes and disable this
     * option.
     */
    public static final boolean AUTO_LOAD_POLYMORPHIC_CLASSES;

    /**
     * Disabled by default. Writes a sentinel value (uint32) in place of null values. Works only on the binary formats
     * (protostuff/graph/protobuf).
     */
    public static final boolean PRESERVE_NULL_ELEMENTS;

    /**
     * Disabled by default. For pojos that are not declared final, they could still be morphed to their respective
     * subclasses (inheritance). Enable this option if your parent classes aren't abstract classes.
     */
    public static final boolean MORPH_NON_FINAL_POJOS;

    /**
     * Disabled by default. If true, type metadata will be included on serialization for fields that are collection
     * interfaces. Enabling this is useful if you want to retain the actual collection impl used.
     * 

* If disabled, type metadata will not be included and instead, will be mapped to a default impl. *

* *

     * Collection = ArrayList
     * List = ArrayList
     * Set = HashSet
     * SortedSet = TreeSet
     * NavigableSet = TreeSet
     * Queue = LinkedList
     * BlockingQueue = LinkedBlockingQueue
     * Deque = LinkedList
     * BlockingDequeue = LinkedBlockingDeque
     * 
*

* You can optionally enable only for a particular field by annotating it with {@link io.protostuff.Morph}. */ public static final boolean MORPH_COLLECTION_INTERFACES; /** * Disabled by default. If true, type metadata will be included on serialization for fields that are map interfaces. * Enabling this is useful if you want to retain the actual map impl used. *

* If disabled, type metadata will not be included and instead, will be mapped to a default impl. *

* *

     * Map = HashMap
     * SortedMap = TreeMap
     * NavigableMap = TreeMap
     * ConcurrentMap = ConcurrentHashMap
     * ConcurrentNavigableMap = ConcurrentSkipListMap
     * 
*

* You can optionally enable only for a particular field by annotating it with {@link io.protostuff.Morph}. */ public static final boolean MORPH_MAP_INTERFACES; /** * On repeated fields, the List/Collection itself is not serialized (only its values). If you enable this option, * the repeated field will be serialized as a standalone message with a collection schema. Even if the * List/Collection is empty, an empty collection message is still written. *

* This is particularly useful if you rely on {@link Object#equals(Object)} on your pojos. *

* Disabled by default for protobuf compatibility. */ public static final boolean COLLECTION_SCHEMA_ON_REPEATED_FIELDS; /** * Disabled by default. If enabled, a list's internal state/fields * will be serialized instead of just its elements. */ public static final boolean POJO_SCHEMA_ON_COLLECTION_FIELDS; /** * Disabled by default. If enabled, a map's internal state/fields * will be serialized instead of just its elements. */ public static final boolean POJO_SCHEMA_ON_MAP_FIELDS; /** * If true, sun.misc.Unsafe is used to access the fields of the objects instead of plain java reflections. Enabled * by default if running on a sun jre. */ public static final boolean USE_SUN_MISC_UNSAFE; static final Method newInstanceFromObjectInputStream, newInstanceFromObjectStreamClass; static final long objectConstructorId; static final boolean android43; static final Constructor OBJECT_CONSTRUCTOR; public static final IdStrategy ID_STRATEGY; static { Constructor c = null; Class reflectionFactoryClass = null; try { c = Object.class.getConstructor((Class[]) null); reflectionFactoryClass = Thread.currentThread() .getContextClassLoader() .loadClass("sun.reflect.ReflectionFactory"); } catch (Exception e) { // ignore } OBJECT_CONSTRUCTOR = c != null && reflectionFactoryClass != null ? c : null; newInstanceFromObjectInputStream = OBJECT_CONSTRUCTOR == null ? getMethodNewInstanceFromObjectInputStream() : null; if (newInstanceFromObjectInputStream != null) { newInstanceFromObjectInputStream.setAccessible(true); newInstanceFromObjectStreamClass = null; objectConstructorId = -1; android43 = false; } else { Number[] holder = new Number[1]; newInstanceFromObjectStreamClass = getMethodNewInstanceFromObjectStreamClass(holder); if (newInstanceFromObjectStreamClass == null) { objectConstructorId = -1; android43 = false; } else { objectConstructorId = holder[0].longValue(); android43 = holder[0] instanceof Long; } } Properties props = OBJECT_CONSTRUCTOR == null ? new Properties() : System.getProperties(); ENUMS_BY_NAME = Boolean.parseBoolean(props.getProperty( "protostuff.runtime.enums_by_name", "false")); AUTO_LOAD_POLYMORPHIC_CLASSES = Boolean.parseBoolean(props.getProperty( "protostuff.runtime.auto_load_polymorphic_classes", "true")); PRESERVE_NULL_ELEMENTS = Boolean.parseBoolean(props.getProperty( "protostuff.runtime.preserve_null_elements", "false")); MORPH_NON_FINAL_POJOS = Boolean.parseBoolean(props.getProperty( "protostuff.runtime.morph_non_final_pojos", "false")); MORPH_COLLECTION_INTERFACES = Boolean.parseBoolean(props.getProperty( "protostuff.runtime.morph_collection_interfaces", "false")); MORPH_MAP_INTERFACES = Boolean.parseBoolean(props.getProperty( "protostuff.runtime.morph_map_interfaces", "false")); COLLECTION_SCHEMA_ON_REPEATED_FIELDS = Boolean.parseBoolean(props.getProperty( "protostuff.runtime.collection_schema_on_repeated_fields", "false")); POJO_SCHEMA_ON_COLLECTION_FIELDS = Boolean.parseBoolean(props.getProperty( "protostuff.runtime.pojo_schema_on_collection_fields", "false")); POJO_SCHEMA_ON_MAP_FIELDS = Boolean.parseBoolean(props.getProperty( "protostuff.runtime.pojo_schema_on_map_fields", "false")); // must be on a sun jre USE_SUN_MISC_UNSAFE = OBJECT_CONSTRUCTOR != null && Boolean.parseBoolean(props.getProperty( "protostuff.runtime.use_sun_misc_unsafe", "true")); String factoryProp = props .getProperty("protostuff.runtime.id_strategy_factory"); if (factoryProp == null) ID_STRATEGY = new DefaultIdStrategy(); else { final IdStrategy.Factory factory; try { factory = ((IdStrategy.Factory) loadClass(factoryProp) .newInstance()); } catch (Exception e) { throw new RuntimeException(e); } ID_STRATEGY = factory.create(); factory.postCreate(); } } private static Method getMethodNewInstanceFromObjectInputStream() { try { return java.io.ObjectInputStream.class.getDeclaredMethod( "newInstance", Class.class, Class.class); } catch (Exception e) { return null; } } private static Method getMethodNewInstanceFromObjectStreamClass(Number[] holder) { try { // android >= 4.3 Method m43 = java.io.ObjectStreamClass.class.getDeclaredMethod( "newInstance", Class.class, long.class); Method mcid43 = java.io.ObjectStreamClass.class.getDeclaredMethod( "getConstructorId", Class.class); mcid43.setAccessible(true); holder[0] = (Number) mcid43.invoke(null, Object.class); return m43; } catch (Exception ex) { try { // android <= 4.2.2 Method m = java.io.ObjectStreamClass.class.getDeclaredMethod( "newInstance", Class.class, int.class); Method mcid = java.io.ObjectStreamClass.class.getDeclaredMethod( "getConstructorId", Class.class); mcid.setAccessible(true); holder[0] = (Number) mcid.invoke(null, Object.class); return m; } catch (Exception e) { return null; } } } @SuppressWarnings("unchecked") static Class loadClass(String className) { try { return (Class) Thread.currentThread().getContextClassLoader() .loadClass(className); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } /** * Returns an instatiator for the specified {@code clazz}. */ public static Instantiator newInstantiator(Class clazz) { final Constructor constructor = getConstructor(clazz); if (constructor == null) { // non-sun jre if (android43) return new Android43Instantiator(clazz); if (newInstanceFromObjectInputStream == null) { if (objectConstructorId == -1) throw new RuntimeException("Could not resolve constructor for " + clazz); return new Android3Instantiator(clazz); } return new Android2Instantiator(clazz); } return new DefaultInstantiator(constructor); } private static Constructor getConstructor(Class clazz) { try { return clazz.getDeclaredConstructor((Class[]) null); } catch (SecurityException e) { return null; // return OBJECT_CONSTRUCTOR == null ? null : // OnDemandSunReflectionFactory.getConstructor(clazz, // OBJECT_CONSTRUCTOR); } catch (NoSuchMethodException e) { return null; // return OBJECT_CONSTRUCTOR == null ? null : // OnDemandSunReflectionFactory.getConstructor(clazz, // OBJECT_CONSTRUCTOR); } } private RuntimeEnv() { } public static abstract class Instantiator { /** * Creates a new instance of an object. */ public abstract T newInstance(); } static final class DefaultInstantiator extends Instantiator { final Constructor constructor; DefaultInstantiator(Constructor constructor) { this.constructor = constructor; constructor.setAccessible(true); } @Override public T newInstance() { try { return constructor.newInstance((Object[]) null); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalArgumentException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } } static final class Android2Instantiator extends Instantiator { final Class clazz; Android2Instantiator(Class clazz) { this.clazz = clazz; } @Override @SuppressWarnings("unchecked") public T newInstance() { try { return (T) newInstanceFromObjectInputStream.invoke(null, clazz, Object.class); } catch (IllegalArgumentException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } } static final class Android3Instantiator extends Instantiator { final Class clazz; Android3Instantiator(Class clazz) { this.clazz = clazz; } @Override @SuppressWarnings("unchecked") public T newInstance() { try { return (T) newInstanceFromObjectStreamClass.invoke(null, clazz, (int) objectConstructorId); } catch (IllegalArgumentException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } } static final class Android43Instantiator extends Instantiator { final Class clazz; Android43Instantiator(Class clazz) { this.clazz = clazz; } @Override @SuppressWarnings("unchecked") public T newInstance() { try { return (T) newInstanceFromObjectStreamClass.invoke(null, clazz, objectConstructorId); } catch (IllegalArgumentException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } } }