blob: e1201fe204b26ebc49a2283af5f90fe970bf844c [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.util.List;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.client.ClientCacheFactory;
/**
* PdxInstance provides run time access to the fields of a PDX without deserializing the PDX.
* Preventing deserialization saves time and memory and does not require the domain class. This
* interface is implemented by GemFire. The PdxInstance implementation is a light weight wrapper
* that simply refers to the raw bytes of the PDX that are kept in the cache. Applications can
* choose to access PdxInstances instead of Java objects by configuring the Cache to prefer PDX
* instances during deserialization. This can be done in <code>cache.xml</code> by setting the
* attribute <code>read-serialized</code> to true on the <code>pdx</code> element. Or it can be done
* programmatically using either the {@link CacheFactory#setPdxReadSerialized(boolean)
* setPdxReadSerialized} or {@link ClientCacheFactory#setPdxReadSerialized(boolean) client
* setPdxReadSerialized} method. Once this preference is configured, then any time deserialization
* of a PDX is done it will deserialize into a PdxInstance.
* <P>
* PdxInstances are immutable. If you want to change one call {@link #createWriter}.
* <p>
* A PdxInstance's fields will always be those of the version it represents. So if you add a field
* to your domain class you can end up with a PdxInstance for version 1 (that does not have the
* field) and a PdxInstance for version 2. The PdxInstance for version 1 will not have the added
* field and the PdxInstance for version 2 will have the field. This differs from deserialization of
* a pdx back to a domain class. In that case if version 2 is deserializing version 1 PdxReader will
* return a default value for the added field even though version 1 has no knowledge of it.
*
* @since GemFire 6.6
*/
public interface PdxInstance extends java.io.Serializable {
/**
* Return the full name of the class that this pdx instance represents.
* An empty string will be returned if no class is associated with this instance.
*
* @return the name of the class that this pdx instance represents.
* @since GemFire 6.6.2
*/
String getClassName();
/**
* Returns true if this instance represents an enum. Enum's have a String field named "name" and
* an int field named "ordinal". It is ok to cast a PdxInstance that represents an enum to
* {@link java.lang.Comparable}. PdxInstances representing enums are not writable.
*
* @return true if this instance represents an enum.
*/
boolean isEnum();
/**
* Deserializes and returns the domain object that this instance represents.
* If this instance is one that never deserializes then getObject returns "this".
*
* @return the deserialized domain object or "this" if this instance never deserializes
* @throws PdxSerializationException if the instance could not be deserialized
*/
Object getObject();
/**
* Checks if the named field exists and returns the result.
* <p>
* This can be useful when writing code that handles more than one version of a PDX class.
*
* @param fieldName the name of the field to check
* @return <code>true</code> if the named field exists; otherwise <code>false</code>
*/
boolean hasField(String fieldName);
/**
* Return an unmodifiable list of the field names on this PdxInstance.
*
* @return an unmodifiable list of the field names on this PdxInstance
*/
List<String> getFieldNames();
/**
* Checks if the named field was {@link PdxWriter#markIdentityField(String) marked} as an identity
* field.
* <p>
* Note that if no fields have been marked then all the fields are used as identity fields even
* though this method will return <code>false</code> since none of them have been <em>marked</em>.
*
* @param fieldName the name of the field to check
* @return <code>true</code> if the named field exists and was marked as an identify field;
* otherwise <code>false</code>
*/
boolean isIdentityField(String fieldName);
/**
* Reads the named field and returns its value. If the field does not exist <code>null</code> is
* returned.
* <p>
* A <code>null</code> result indicates that the field does not exist or that it exists and its
* value is currently <code>null</code>. The {@link #hasField(String) hasField} method can be used
* to figure out which if these two cases is true.
* <p>
* If an Object[] is deserialized by this call then that array's component type will be
* <code>Object.class</code> instead of the original class that the array had when it was
* serialized. This is done so that PdxInstance objects can be added to the array.
*
* @param fieldName name of the field to read
*
* @return If this instance has the named field then the field's value is returned, otherwise
* <code>null</code> is returned.
* @throws PdxSerializationException if the field could not be deserialized
*/
Object getField(String fieldName);
/**
* Returns true if the given object is equals to this instance.
* <p>
* If <code>other</code> is not a PdxInstance then it is not equal to this instance. NOTE: Even if
* <code>other</code> is the result of calling {@link #getObject()} it will not be equal to this
* instance.
* <p>
* Otherwise equality of two PdxInstances is determined as follows:
* <ol>
* <li>The domain class name must be equal for both PdxInstances
* <li>Each identity field must be equal.
* </ol>
* If the domain class is not the empty string then
* if one of the instances does not have a field that the other one does
* then equals will assume it has the field with a default value.
* If a PdxInstance has marked identity fields using
* {@link PdxWriter#markIdentityField(String) markIdentityField} then only the marked identity
* fields are its identity fields. Otherwise all its fields are identity fields.
* <P>
* An identity field is equal if all the following are true:
* <ol>
* <li>The field name is equal.
* <li>The field type is equal.
* <li>The field value is equal.
* </ol>
* <P>
* If a field's type is <code>OBJECT</code> then its value must be deserialized to determine if it
* is equals. If the deserialized object is an array then
* {@link java.util.Arrays#deepEquals(Object[], Object[]) deepEquals} is used to determine
* equality. Otherwise {@link Object#equals(Object) equals} is used.
* <P>
* If a field's type is <code>OBJECT[]</code> then its value must be deserialized and
* {@link java.util.Arrays#deepEquals(Object[], Object[]) deepEquals} is used to determine
* equality.
* <P>
* For all other field types then the value does not need to be deserialized. Instead the
* serialized raw bytes are compared and used to determine equality.
* <P>
* Note that any fields that have objects that do not override {@link Object#equals(Object)
* equals} will cause equals to return false when you might have expected it to return true. The
* only exceptions to this are those that call
* {@link java.util.Arrays#deepEquals(Object[], Object[]) deepEquals} as noted above. You should
* either override equals and hashCode in these cases or mark other fields as your identity
* fields.
*
* @param other the other instance to compare to this.
* @return <code>true</code> if this instance is equal to <code>other</code>.
*/
boolean equals(Object other);
/**
* Generates a hashCode based on the identity fields of this PdxInstance.
* <p>
* If a PdxInstance has marked identity fields using {@link PdxWriter#markIdentityField(String)
* markIdentityField} then only the marked identity fields are its identity fields. Otherwise all
* its fields are identity fields.
* <p>
* If an identity field is of type <code>OBJECT</code> then it is deserialized. If the
* deserialized object is an array then {@link java.util.Arrays#deepHashCode(Object[])
* deepHashCode} is used. Otherwise {@link Object#hashCode() hashCode} is used.
* <p>
* If an identity field is of type <code>OBJECT[]</code> this it is deserialized and
* {@link java.util.Arrays#deepHashCode(Object[]) deepHashCode} is used.
* <p>
* Otherwise the field is not deserialized and the raw bytes of its value are used to compute the
* hash code.
* <p>
* The algorithm used to compute the hashCode is: hashCode = 1; foreach (field:
* sortedIdentityFields()) { if (field.isDefaultValue()) continue; if (field.isArray()) { hashCode
* = hashCode*31 + Arrays.deepHashCode(field); } else { hashCode = hashCode*31 + field.hashCode();
* } } if (hashCode == 0) { hashCode = 1; }
*/
int hashCode();
/**
* Prints out all of the identity fields of this PdxInstance.
* <p>
* If a PdxInstance has marked identity fields using {@link PdxWriter#markIdentityField(String)
* markIdentityField} then only the marked identity fields are its identity fields. Otherwise all
* its fields are identity fields.
*/
String toString();
/**
* Creates and returns a {@link WritablePdxInstance} whose initial values are those of this
* PdxInstance. This call returns a copy of the current field values so modifications made to the
* returned value will not modify this PdxInstance.
*
* @return a {@link WritablePdxInstance}
* @throws IllegalStateException if the PdxInstance is an enum.
*/
WritablePdxInstance createWriter();
/**
* Returns false if this instance will never be deserialized to a domain class.
* Instances that never deserialize can be created using
* {@link PdxInstanceFactory#neverDeserialize} or by creating a factory with
* an empty string as the class name.
*
* @return false if this instance will never be deserialized to a domain class.
*
* @since Geode 1.9
*/
default boolean isDeserializable() {
return false;
}
}