blob: 9740da9e899a36621f25e6d31cad8033d16390ce [file] [log] [blame]
/*
* 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 org.apache.geode.pdx;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.Declarable;
import org.apache.geode.cache.RegionService;
import org.apache.geode.pdx.internal.AutoSerializableManager;
/**
* This class uses Java reflection in conjunction with {@link org.apache.geode.pdx.PdxSerializer
* PdxSerialzer} to perform automatic serialization of domain objects. The implication is that the
* domain classes do not need to implement the <code>PdxSerializable</code> interface.
* <p>
* This implementation will serialize all relevant fields
* <p>
* For example:
*
* <pre>
* Cache c = new CacheFactory().set(&quot;cache-xml-file&quot;, cacheXmlFileName)
* .setPdxSerializer(new ReflectionBasedAutoSerializer("com.foo.DomainObject")).create();
* </pre>
* <p>
* In this example <code>DomainObject</code> would not need to implement
* <code>PdxSerializable</code> to be serialized.
* <p>
* The equivalent <code>cache.xml</code> entries might be as follows:
*
* <pre>
* &lt;pdx&gt;
* &lt;pdx-serializer&gt;
* &lt;class-name&gt;
* org.apache.geode.pdx.ReflectionBasedAutoSerializer
* &lt;/class-name&gt;
* &lt;parameter name="classes"&gt;
* &lt;string&gt; com.company.domain.DomainObject &lt;/string&gt;
* &lt;/parameter&gt;
* &lt;/pdx-serializer&gt;
* &lt;/pdx&gt;
* </pre>
*
* See {@link ReflectionBasedAutoSerializer#reconfigure(String...) reconfigure} for additional
* details on the format of the parameter string.
*
* @since GemFire 6.6
*/
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.
* <p>
* Each string in the list represents a definition in the following form:
*
* <pre>
* &lt;class pattern&gt;#identity=&lt;identity field pattern&gt;#exclude=&lt;exclude field pattern&gt;
* </pre>
*
* The hash (#) characters are separators and are not part of the parameter name. An example would
* be:
*
* <pre>
* com.company.DomainObject.*#identity=id.*#exclude=creationDate
* </pre>
*
* This would select all classes with a class name beginning with
* <code>com.company.DomainObject</code> and would select as PDX identity fields any fields
* beginning with <code>id</code> and would not serialize the field called
* <code>creationDate</code>.
* <p>
* There is no association between the the <i>identity</i> and <i>exclude</i> options, so the
* above example could also be expressed as:
*
* <pre>
* com.company.DomainObject.*#identity=id.*
* com.company.DomainObject.*#exclude=creationDate
* </pre>
*
* Note that <u>all</u> 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<String> classes) {
this(listToArray(classes));
}
private static String[] listToArray(List<String> l) {
if (l == null) {
l = Collections.emptyList();
}
return l.toArray(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.
* <p>
* Each string in the list represents a definition in the following form:
*
* <pre>
* &lt;class pattern&gt;#identity=&lt;identity field pattern&gt;#exclude=&lt;exclude field pattern&gt;
* </pre>
*
* The hash (#) characters are separators and are not part of the parameter name. An example would
* be:
*
* <pre>
* com.company.DomainObject.*#identity=id.*#exclude=creationDate
* </pre>
*
* This would select all classes with a class name beginning with
* <code>com.company.DomainObject</code> and would select as PDX identity fields any fields
* beginning with <code>id</code> and would not serialize the field called
* <code>creationDate</code>.
* <p>
* There is no association between the the <i>identity</i> and <i>exclude</i> options, so the
* above example could also be expressed as:
*
* <pre>
* com.company.DomainObject.*#identity=id.*
* com.company.DomainObject.*#exclude=creationDate
* </pre>
*
* Note that <u>all</u> 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 GemFire 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.
* <p>
* Each string in the list represents a definition in the following form:
*
* <pre>
* &lt;class pattern&gt;#identity=&lt;identity field pattern&gt;#exclude=&lt;exclude field pattern&gt;
* </pre>
*
* The hash (#) characters are separators and are not part of the parameter name. An example would
* be:
*
* <pre>
* com.company.DomainObject.*#identity=id.*#exclude=creationDate
* </pre>
*
* This would select all classes with a class name beginning with
* <code>com.company.DomainObject</code> and would select as PDX identity fields any fields
* beginning with <code>id</code> and would not serialize the field called
* <code>creationDate</code>.
* <p>
* There is no association between the the <i>identity</i> and <i>exclude</i> options, so the
* above example could also be expressed as:
*
* <pre>
* com.company.DomainObject.*#identity=id.*
* com.company.DomainObject.*#exclude=creationDate
* </pre>
*
* Note that <u>all</u> 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 <code>true</code> 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 GemFire 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.
* <p>
* Each string in the list represents a definition in the following form:
*
* <pre>
* &lt;class pattern&gt;#identity=&lt;identity field pattern&gt;#exclude=&lt;exclude field pattern&gt;
* </pre>
*
* The hash (#) characters are separators and are not part of the parameter name. An example would
* be:
*
* <pre>
* com.company.DomainObject.*#identity=id.*#exclude=creationDate
* </pre>
*
* This would select all classes with a class name beginning with
* <code>com.company.DomainObject</code> and would select as PDX identity fields any fields
* beginning with <code>id</code> and would not serialize the field called
* <code>creationDate</code>.
* <p>
* There is no association between the the <i>identity</i> and <i>exclude</i> options, so the
* above example could also be expressed as:
*
* <pre>
* com.company.DomainObject.*#identity=id.*
* com.company.DomainObject.*#exclude=creationDate
* </pre>
*
* Note that <u>all</u> 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 void setSerializableClasses(List<String> patterns) {
reconfigure(listToArray(patterns));
}
/**
* Method to reconfigure this serializer. Any previous configuration is cleared. The serializer
* will not check for portable serialization.
* <p>
* Each string in the list represents a definition in the following form:
*
* <pre>
* &lt;class pattern&gt;#identity=&lt;identity field pattern&gt;#exclude=&lt;exclude field pattern&gt;
* </pre>
*
* The hash (#) characters are separators and are not part of the parameter name. An example would
* be:
*
* <pre>
* com.company.DomainObject.*#identity=id.*#exclude=creationDate
* </pre>
*
* This would select all classes with a class name beginning with
* <code>com.company.DomainObject</code> and would select as PDX identity fields any fields
* beginning with <code>id</code> and would not serialize the field called
* <code>creationDate</code>.
* <p>
* There is no association between the the <i>identity</i> and <i>exclude</i> options, so the
* above example could also be expressed as:
*
* <pre>
* com.company.DomainObject.*#identity=id.*
* com.company.DomainObject.*#exclude=creationDate
* </pre>
*
* Note that <u>all</u> 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 GemFire 6.6.2
*/
public void reconfigure(String... patterns) {
reconfigure(false, patterns);
}
/**
* Method to reconfigure this serializer. Any previous configuration is cleared.
* <p>
* Each string in the list represents a definition in the following form:
*
* <pre>
* &lt;class pattern&gt;#identity=&lt;identity field pattern&gt;#exclude=&lt;exclude field pattern&gt;
* </pre>
*
* The hash (#) characters are separators and are not part of the parameter name. An example would
* be:
*
* <pre>
* com.company.DomainObject.*#identity=id.*#exclude=creationDate
* </pre>
*
* This would select all classes with a class name beginning with
* <code>com.company.DomainObject</code> and would select as PDX identity fields any fields
* beginning with <code>id</code> and would not serialize the field called
* <code>creationDate</code>.
* <p>
* There is no association between the the <i>identity</i> and <i>exclude</i> options, so the
* above example could also be expressed as:
*
* <pre>
* com.company.DomainObject.*#identity=id.*
* com.company.DomainObject.*#exclude=creationDate
* </pre>
*
* Note that <u>all</u> 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 <code>true</code> then an serialization done by this serializer will
* throw an exception if the object it not portable to non-java languages.
* @since GemFire 6.6.2
*/
public void reconfigure(boolean checkPortability, String... patterns) {
this.manager.reconfigure(checkPortability, patterns);
}
/**
* Method implemented from <code>PdxSerializer</code> which performs object serialization.
*
* @param obj the object to serialize
* @param writer the <code>PdxWriter</code> to use when serializing this object
* @return <code>true</code> if the object was serialized, <code>false</code> otherwise
*/
@Override
public boolean toData(Object obj, PdxWriter writer) {
return manager.writeData(writer, obj);
}
/**
* Method implemented from <code>PdxSerializer</code> which performs object de-serialization.
*
* @param clazz the class of the object to re-create
* @param reader the <code>PdxReader</code> to use when creating this object
* @return the deserialized object if this serializer handles the given class, null otherwise.
*/
@Override
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:
* <ul>
* <li><b>classes</b> - 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.</li>
* <li><b>check-portability</b> - 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
* @deprecated as of Geode 1.5 use initialize instead
*/
@Override
public void init(Properties props) {
this.manager.init(props);
}
/**
* Used for declarative class initialization from cache.xml. The following property may be
* specified:
* <ul>
* <li><b>classes</b> - 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.</li>
* <li><b>check-portability</b> - if true then an exception will be thrown if an attempt to
* serialize data that is not portable to .NET is made.
*
* @param cache the cache that owns this serializer
* @param props properties used to configure the auto serializer
* @since Geode 1.5
*/
@Override
public void initialize(Cache cache, Properties props) {
this.manager.setRegionService(cache);
this.manager.init(props);
}
/**
* Return a <code>Properties</code> object with a representation of the current config. Depending
* on how this <code>ReflectionBasedAutoSerializer</code> was configured, the returned property
* value will have the correct semantics but may differ from the the original configuration
* string.
*
* @return a <code>Properties</code> 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.
* <p>
* The default implementation:
* <ul>
* <li>only serializes classes whose name matches one of the patterns
* <li>excludes classes whose package begins with "org.apache.", "java.", or "javax." unless the
* system property "gemfire.auto.serialization.no.hardcoded.excludes" is set to "true".
* <li>excludes classes that do not have a public no-arg constructor
* <li>excludes enum classes
* <li>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.
* </ul>
* <p>
* 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 GemFire 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.
* <p>
* The default implementation:
* <ul>
* <li>excludes transient fields
* <li>excludes static fields
* <li>excludes any fields that match an "#exclude=" pattern.
* </ul>
* All other fields are included.
* <p>
* 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 GemFire 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.
* <p>
* The default implementation returns the name obtained from <code>f</code>.
* <p>
* 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 GemFire 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.
* <p>
* The default implementation only marks fields that match an "#identity=" pattern as identity
* fields.
* <p>
* 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 GemFire 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.
* <p>
* The default implementation uses {@link FieldType#get(Class)} by passing it
* {@link Field#getType()}.
* <p>
* 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 GemFire 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}.
* <p>
* The default implementation returns false.
* <p>
* 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 GemFire 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 <code>originalValue</code> if you
* decide not to transform the value.
* @since GemFire 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 <code>serializedValue</code> if you
* decide not to transform the value.
* @since GemFire 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 GemFire 6.6.2
*/
public RegionService getRegionService() {
return this.manager.getRegionService();
}
/**
* For internal use only.
*
* @since GemFire 8.2
*/
public Object getManager() {
// The result is not AutoSerializableManager because
// that class is not part of our public APIs.
return this.manager;
}
}