com.gemstone.gemfire.pdx.ReflectionBasedAutoSerializer Maven / Gradle / Ivy
Show all versions of gemfire-core Show documentation
/*
* Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
*
* 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. See accompanying
* LICENSE file.
*/
package com.gemstone.gemfire.pdx;
import com.gemstone.gemfire.cache.Declarable;
import com.gemstone.gemfire.cache.RegionService;
import com.gemstone.gemfire.pdx.internal.AutoSerializableManager;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
/**
* This class uses Java reflection in conjunction with
* {@link com.gemstone.gemfire.pdx.PdxSerializer PdxSerialzer} to perform
* automatic serialization of domain objects. The implication is that the domain
* classes do not need to implement the PdxSerializable
interface.
*
* This implementation will serialize all relevant fields
*
* For example:
*
*
* Cache c = new CacheFactory().set("cache-xml-file", cacheXmlFileName)
* .setPdxSerializer(new ReflectionBasedAutoSerializer("com.foo.DomainObject"))
* .create();
*
*
* In this example DomainObject
would not need to implement
* PdxSerializable
to be serialized.
*
* The equivalent cache.xml
entries might be as follows:
*
*
* <pdx>
* <pdx-serializer>
* <class-name>
* com.gemstone.gemfire.pdx.ReflectionBasedAutoSerializer
* </class-name>
* <parameter name="classes">
* <string> com.company.domain.DomainObject </string>
* </parameter>
* </pdx-serializer>
* </pdx>
*
* See {@link ReflectionBasedAutoSerializer#reconfigure(String...) reconfigure}
* for additional details on the format of the parameter string.
*
* @since 6.6
* @author jens
* @author darrel
*/
public class ReflectionBasedAutoSerializer implements PdxSerializer, Declarable {
private final AutoSerializableManager manager;
/**
* Default constructor primarily used during declarative configuration via the
* cache.xml file.
* Instances created with this constructor will not match any classes
* so use {@link #ReflectionBasedAutoSerializer(String...)} instead.
*/
public ReflectionBasedAutoSerializer() {
this(new String[0]);
}
/**
* Constructor which takes a list of class name patterns which are to be
* auto-serialized. Portability of serialization will not be checked.
*
* Each string in the list represents a definition in the following form:
*
* <class pattern>#identity=<identity field pattern>#exclude=<exclude field pattern>
*
* The hash (#) characters are separators and are not part of the parameter
* name. An example would be:
*
* com.company.DomainObject.*#identity=id.*#exclude=creationDate
*
* This would select all classes with a class name beginning with com.company.DomainObject
* and would select as PDX identity fields any fields beginning with id
* and would not serialize the field called creationDate
.
*
* There is no association between the the identity and exclude
* options, so the above example could also be expressed as:
*
* com.company.DomainObject.*#identity=id.*
* com.company.DomainObject.*#exclude=creationDate
*
*
* Note that all defined patterns are used when determining whether a field
* should be considered as an identity field or should be excluded. Thus the
* order of the patterns is not relevant.
*
* @param classes the patterns which are matched against domain class
* names to determine whether they should be serialized
* @deprecated as of 6.6.2 use ReflectionBasedAutoSerializer(String...) instead.
*/
public ReflectionBasedAutoSerializer(List classes) {
this(listToArray(classes));
}
private static String[] listToArray(List l) {
if (l == null) {
l = Collections.emptyList();
}
return l.toArray(new String[l.size()]);
}
/**
* Constructor which takes a list of class name patterns which are to be
* auto-serialized. Portability of serialization will not be checked.
*
* Each string in the list represents a definition in the following form:
*
* <class pattern>#identity=<identity field pattern>#exclude=<exclude field pattern>
*
* The hash (#) characters are separators and are not part of the parameter
* name. An example would be:
*
* com.company.DomainObject.*#identity=id.*#exclude=creationDate
*
* This would select all classes with a class name beginning with com.company.DomainObject
* and would select as PDX identity fields any fields beginning with id
* and would not serialize the field called creationDate
.
*
* There is no association between the the identity and exclude
* options, so the above example could also be expressed as:
*
* com.company.DomainObject.*#identity=id.*
* com.company.DomainObject.*#exclude=creationDate
*
*
* Note that all defined patterns are used when determining whether a field
* should be considered as an identity field or should be excluded. Thus the
* order of the patterns is not relevant.
*
* @param patterns the patterns which are matched against domain class
* names to determine whether they should be serialized
* @since 6.6.2
*/
public ReflectionBasedAutoSerializer(String... patterns) {
this(false, patterns);
}
/**
* Constructor which takes a list of class name patterns which are to be
* auto-serialized.
*
* Each string in the list represents a definition in the following form:
*
* <class pattern>#identity=<identity field pattern>#exclude=<exclude field pattern>
*
* The hash (#) characters are separators and are not part of the parameter
* name. An example would be:
*
* com.company.DomainObject.*#identity=id.*#exclude=creationDate
*
* This would select all classes with a class name beginning with com.company.DomainObject
* and would select as PDX identity fields any fields beginning with id
* and would not serialize the field called creationDate
.
*
* There is no association between the the identity and exclude
* options, so the above example could also be expressed as:
*
* com.company.DomainObject.*#identity=id.*
* com.company.DomainObject.*#exclude=creationDate
*
*
* Note that all defined patterns are used when determining whether a field
* should be considered as an identity field or should be excluded. Thus the
* order of the patterns is not relevant.
*
* @param checkPortability if true
then an serialization done by
* this serializer will throw an exception if the object it not portable to
* non-java languages.
* @param patterns the patterns which are matched against domain class
* names to determine whether they should be serialized
* @since 6.6.2
*/
public ReflectionBasedAutoSerializer(boolean checkPortability, String... patterns) {
// We allow this class to escape its constructor so that our delegate can
// call back to us when needed. Callbacks will not happen until this instance
// is fully constructed and registered with the Cache.
this.manager = AutoSerializableManager.create(this, checkPortability, patterns);
}
/**
* Method to configure classes to consider for serialization, to set any
* identity fields and to define any fields to exclude from serialization.
*
* Each string in the list represents a definition in the following form:
*
* <class pattern>#identity=<identity field pattern>#exclude=<exclude field pattern>
*
* The hash (#) characters are separators and are not part of the parameter
* name. An example would be:
*
* com.company.DomainObject.*#identity=id.*#exclude=creationDate
*
* This would select all classes with a class name beginning with com.company.DomainObject
* and would select as PDX identity fields any fields beginning with id
* and would not serialize the field called creationDate
.
*
* There is no association between the the identity and exclude
* options, so the above example could also be expressed as:
*
* com.company.DomainObject.*#identity=id.*
* com.company.DomainObject.*#exclude=creationDate
*
*
* Note that all defined patterns are used when determining whether a field
* should be considered as an identity field or should be excluded. Thus the
* order of the patterns is not relevant.
*
* @param patterns the list of definitions to apply
* @deprecated as of 6.6.2 use {@link #reconfigure(String...)} instead.
*/
public final void setSerializableClasses(List patterns) {
reconfigure(listToArray(patterns));
}
/**
* Method to reconfigure this serializer. Any previous configuration is cleared.
* The serializer will not check for portable serialization.
*
* Each string in the list represents a definition in the following form:
*
* <class pattern>#identity=<identity field pattern>#exclude=<exclude field pattern>
*
* The hash (#) characters are separators and are not part of the parameter
* name. An example would be:
*
* com.company.DomainObject.*#identity=id.*#exclude=creationDate
*
* This would select all classes with a class name beginning with com.company.DomainObject
* and would select as PDX identity fields any fields beginning with id
* and would not serialize the field called creationDate
.
*
* There is no association between the the identity and exclude
* options, so the above example could also be expressed as:
*
* com.company.DomainObject.*#identity=id.*
* com.company.DomainObject.*#exclude=creationDate
*
*
* Note that all defined patterns are used when determining whether a field
* should be considered as an identity field or should be excluded. Thus the
* order of the patterns is not relevant.
*
* @param patterns the definitions to apply
* @since 6.6.2
*/
public final void reconfigure(String... patterns) {
reconfigure(false, patterns);
}
/**
* Method to reconfigure this serializer. Any previous configuration is cleared.
*
* Each string in the list represents a definition in the following form:
*
* <class pattern>#identity=<identity field pattern>#exclude=<exclude field pattern>
*
* The hash (#) characters are separators and are not part of the parameter
* name. An example would be:
*
* com.company.DomainObject.*#identity=id.*#exclude=creationDate
*
* This would select all classes with a class name beginning with com.company.DomainObject
* and would select as PDX identity fields any fields beginning with id
* and would not serialize the field called creationDate
.
*
* There is no association between the the identity and exclude
* options, so the above example could also be expressed as:
*
* com.company.DomainObject.*#identity=id.*
* com.company.DomainObject.*#exclude=creationDate
*
*
* Note that all defined patterns are used when determining whether a field
* should be considered as an identity field or should be excluded. Thus the
* order of the patterns is not relevant.
*
* @param patterns the definitions to apply
* @param checkPortability if true
then an serialization done by
* this serializer will throw an exception if the object it not portable to
* non-java languages.
* @since 6.6.2
*/
public final void reconfigure(boolean checkPortability, String... patterns) {
this.manager.reconfigure(checkPortability, patterns);
}
/**
* Method implemented from PdxSerializer
which performs object
* serialization.
*
* @param obj
* the object to serialize
* @param writer
* the PdxWriter
to use when serializing this object
* @return true
if the object was serialized, false
* otherwise
*/
public boolean toData(Object obj, PdxWriter writer) {
return manager.writeData(writer, obj);
}
/**
* Method implemented from PdxSerializer
which performs object
* de-serialization.
*
* @param clazz
* the class of the object to re-create
* @param reader
* the PdxReader
to use when creating this object
* @return the deserialized object if this serializer handles the given class,
* null otherwise.
*/
public Object fromData(Class> clazz, PdxReader reader) {
return manager.readData(reader, clazz);
}
/**
* Used for declarative class initialization from cache.xml. The following
* property may be specified:
*
* - classes - a comma-delimited list of strings which represent the
* patterns used to select classes for serialization, patterns to select
* identity fields and patterns to exclude fields. See {@link ReflectionBasedAutoSerializer#reconfigure(String...) reconfigure}
* for specifics.
*
* - check-portability - if true then an exception will be thrown if
* an attempt to serialize data that is not portable to .NET is made.
*
* @param props
* properties used to configure the auto serializer
*/
public void init(Properties props) {
this.manager.init(props);
}
/**
* Return a
Properties
object with a representation of the
* current config. Depending on how this ReflectionBasedAutoSerializer
* was configured, the returned property value will have the correct semantics
* but may differ from the the original configuration string.
*
* @return a Properties
object
*/
public Properties getConfig() {
return this.manager.getConfig();
}
/**
* Controls what classes will be auto serialized by this serializer.
* Override this method to customize what classes will be auto serialized.
*
* The default implementation:
*
* - only serializes classes whose name matches one of the patterns
*
- excludes classes whose package begins with "com.gemstone.", "java.", or "javax."
* unless the system property "gemfire.auto.serialization.no.hardcoded.excludes"
* is set to "true".
*
- excludes classes that do not have a public no-arg constructor
*
- excludes enum classes
*
- excludes classes that require standard java serialization. A class
* requires standard java serialization if it extends Externalizable or
* if it extends Serializable and has either a private writeObject method
* or a writeReplace method as defined by the java serialization specification.
*
*
* This method is only called the first time it sees a new class. The result
* will be remembered and used the next time the same class is seen.
* @param clazz the class that is being considered for auto serialization.
* @return true if instances of the class should be auto serialized; false if not.
* @since 6.6.2
*/
public boolean isClassAutoSerialized(Class> clazz) {
return this.manager.defaultIsClassAutoSerialized(clazz);
}
/**
* Controls what fields of a class will be auto serialized by this serializer.
* Override this method to customize what fields of a class will be auto serialized.
*
* The default implementation:
*
* - excludes transient fields
*
- excludes static fields
*
- excludes any fields that match an "#exclude=" pattern.
*
* All other fields are included.
*
* This method is only called the first time it sees a new class. The result
* will be remembered and used the next time the same class is seen.
* @param f the field being considered for serialization
* @param clazz the original class being serialized that owns this field.
* Note that this field may have been inherited from a super class by this class.
* If you want to find the class that declared this field use {@link Field#getDeclaringClass()}.
* @return true if the field should be serialized as a pdx field; false if it should be ignored.
* @since 6.6.2
*/
public boolean isFieldIncluded(Field f, Class> clazz) {
return this.manager.defaultIsFieldIncluded(f, clazz);
}
/**
* Controls the field name that will be used in pdx for a field being auto serialized.
* Override this method to customize the field names that will be generated by auto serialization.
* It allows you to convert a local, language dependent name, to a more portable name.
* The returned name is the one that will show up in a {@link PdxInstance} and that
* one that will need to be used to access the field when doing a query.
*
* The default implementation returns the name obtained from f
.
*
* This method is only called the first time it sees a new class. The result
* will be remembered and used the next time the same class is seen.
* @param f the field whose name is returned.
* @param clazz the original class being serialized that owns this field.
* Note that this field may have been inherited from a super class by this class.
* If you want to find the class that declared this field use {@link Field#getDeclaringClass()}.
* @return the name of the field
* @since 6.6.2
*/
public String getFieldName(Field f, Class> clazz) {
return f.getName();
}
/**
* Controls what fields of a class that is auto serialized will be marked
* as pdx identity fields.
* Override this method to customize what fields of an auto serialized class will be
* identity fields.
* Identity fields are used when a {@link PdxInstance} computes its hash code
* and checks to see if it is equal to another object.
*
* The default implementation only marks fields that match an "#identity=" pattern
* as identity fields.
*
* This method is only called the first time it sees a new class. The result
* will be remembered and used the next time the same class is seen.
* @param f the field to test to see if it is an identity field.
* @param clazz the original class being serialized that owns this field.
* Note that this field may have been inherited from a super class by this class.
* If you want to find the class that declared this field use {@link Field#getDeclaringClass()}.
* @return true if the field should be marked as an identity field; false if not.
* @since 6.6.2
*/
public boolean isIdentityField(Field f, Class> clazz) {
return this.manager.defaultIsIdentityField(f, clazz);
}
/**
* Controls what pdx field type will be used when auto serializing.
* Override this method to customize what pdx field type will be used
* for a given domain class field.
*
* The default implementation uses {@link FieldType#get(Class)}
* by passing it {@link Field#getType()}.
*
* This method is only called the first time it sees a new class. The result
* will be remembered and used the next time the same class is seen.
* @param f the field whose pdx field type needs to be determined
* @param clazz the original class being serialized that owns this field.
* Note that this field may have been inherited from a super class by this class.
* If you want to find the class that declared this field use {@link Field#getDeclaringClass()}.
* @return the pdx field type of the given domain class field.
* @since 6.6.2
*/
public FieldType getFieldType(Field f, Class> clazz) {
return this.manager.defaultGetFieldType(f, clazz);
}
/**
* Controls if a pdx field's value can be transformed during serialization.
* Override this method to customize what fields can have their values transformed.
* If you return true then you need to also override {@link #writeTransform}
* and {@link #readTransform}.
*
* The default implementation returns false.
*
* This method is only called the first time it sees a new class. The result
* will be remembered and used the next time the same class is seen.
* @param f the field in question
* @param clazz the original class being serialized that owns this field.
* Note that this field may have been inherited from a super class by this class.
* If you want to find the class that declared this field use {@link Field#getDeclaringClass()}.
* @return true if the {@link #writeTransform} and {@link #readTransform} need to be called
* when serializing and deserializing this field's value.
* @since 6.6.2
*/
public boolean transformFieldValue(Field f, Class> clazz) {
return false;
}
/**
* Controls what field value is written during auto serialization.
* Override this method to customize the data that will be written
* during auto serialization.
* This method will only be called if {@link #transformFieldValue}
* returned true.
* @param f the field in question
* @param clazz the original class being serialized that owns this field.
* Note that this field may have been inherited from a super class by this class.
* If you want to find the class that declared this field use {@link Field#getDeclaringClass()}.
* @param originalValue the value of the field that was read from the domain object.
* @return the actual value to write for this field. Return originalValue
* if you decide not to transform the value.
* @since 6.6.2
*/
public Object writeTransform(Field f, Class> clazz, Object originalValue) {
return originalValue;
}
/**
* Controls what field value is read during auto deserialization.
* Override this method to customize the data that will be read
* during auto deserialization.
* This method will only be called if {@link #transformFieldValue}
* returned true.
* @param f the field in question
* @param clazz the original class being serialized that owns this field.
* Note that this field may have been inherited from a super class by this class.
* If you want to find the class that declared this field use {@link Field#getDeclaringClass()}.
* @param serializedValue the value of the field that was serialized for this field.
* @return the actual value to write for this field. Return serializedValue
* if you decide not to transform the value.
* @since 6.6.2
*/
public Object readTransform(Field f, Class> clazz, Object serializedValue) {
return serializedValue;
}
/**
* Returns the cache that this serializer is installed on.
* Returns null if it is not installed.
* @since 6.6.2
*/
public final RegionService getRegionService() {
return this.manager.getRegionService();
}
}