/*
* 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;
import org.apache.geode.internal.InternalInstantiator;
import org.apache.geode.internal.cache.EventID;
import org.apache.geode.internal.cache.tier.sockets.ClientProxyMembershipID;
import org.apache.geode.internal.i18n.LocalizedStrings;
/**
* <code>Instantiator</code> allows classes that implement {@link DataSerializable} to be registered
* with the data serialization framework. Knowledge of <code>DataSerializable</code> classes allows
* the framework to optimize how instances of those classes are data serialized.
*
* <P>
*
* Ordinarily, when a <code>DataSerializable</code> object is written using
* {@link DataSerializer#writeObject(Object, java.io.DataOutput)}, a special marker class id is
* written to the stream followed by the class name of the <code>DataSerializable</code> object.
* After the marker class id is read by {@link DataSerializer#readObject} it performs the following
* operations,
*
* <OL>
*
* <LI>The class name is read</LI>
*
* <LI>The class is loaded using {@link Class#forName(java.lang.String)}</LI>
*
* <LI>An instance of the class is created using reflection</LI>
*
* <LI>{@link DataSerializable#fromData} is invoked on the newly-created object</LI>
*
* </OL>
*
* However, if a <code>DataSerializable</code> class is {@linkplain #register(Instantiator)
* registered} with the data serialization framework and assigned a unique class id, an important
* optimization can be performed that avoid the expense of using reflection to instantiate the
* <code>DataSerializable</code> class. When the object is written using
* {@link DataSerializer#writeObject(Object, java.io.DataOutput)}, the object's registered class id
* is written to the stream. Consequently, when the data is read from the stream, the
* {@link #newInstance} method of the appropriate <code>Instantiator</code> instance is invoked to
* create an "empty" instance of the <code>DataSerializable</code> instead of using reflection to
* create the new instance.
*
* <P>
*
* Commonly, a <code>DataSerializable</code> class will register itself with the
* <code>Instantiator</code> in a static initializer as shown in the below example code.
*
* <!-- The source code for the CompanySerializer class resides in tests/com/examples/ds/User.java
* Please keep the below code snippet in sync with that file. -->
*
* <PRE>
public class User implements DataSerializable {
private String name;
private int userId;
static {
Instantiator.register(new Instantiator(User.class, 45) {
public DataSerializable newInstance() {
return new User();
}
});
}
public User(String name, int userId) {
this.name = name;
this.userId = userId;
}
/**
* Creates an "empty" User whose contents are filled in by
* invoking its toData() method
*/
private User() {
}
public void toData(DataOutput out) throws IOException {
out.writeUTF(this.name);
out.writeInt(this.userId);
}
public void fromData(DataInput in)
throws IOException, ClassNotFoundException {
this.name = in.readUTF();
this.userId = in.readInt();
}
}
* </PRE>
*
* <code>Instantiator</code>s may be distributed to other members of the distributed system when
* they are registered. Consider the following scenario in which VM1 and VM2 are members of the same
* distributed system. Both VMs define the sameRegion and VM2's region replicates the contents of
* VM1's using replication. VM1 puts an instance of the above <code>User</code> class into the
* region. The act of instantiating <code>User</code> will load the <code>User</code> class and
* invoke its static initializer, thus registering the <code>Instantiator</code> with the data
* serialization framework. Because the region is a replicate, the <code>User</code> will be data
* serialized and sent to VM2. However, when VM2 attempts to data deserialize the <code>User</code>,
* its <code>Instantiator</code> will not necessarily be registered because <code>User</code>'s
* static initializer may not have been invoked yet. As a result, an exception would be logged while
* deserializing the <code>User</code> and the replicate would not appear to have the new value. So,
* in order to ensure that the <code>Instantiator</code> is registered in VM2, the data
* serialization framework distributes a message to each member when an <code>Instantiator</code> is
* {@linkplain #register(Instantiator) registered}.
* <p>
* Note that the framework does not require that an <code>Instantiator</code> be
* {@link java.io.Serializable}, but it does require that it provide a
* {@linkplain #Instantiator(Class, int) two-argument constructor}.
*
* @see #register(Instantiator)
* @see #newInstance
*
* @since GemFire 3.5
*/
public abstract class Instantiator {
/**
* The class associated with this instantiator. Used mainly for debugging purposes and error
* messages.
*/
private Class<? extends DataSerializable> clazz;
/** The id of this <code>Instantiator</code> */
private int id;
/** The eventId of this <code>Instantiator</code> */
private EventID eventId;
/** The originator of this <code>Instantiator</code> */
private ClientProxyMembershipID context;
/////////////////////// Static Methods ///////////////////////
/**
* Registers a <code>DataSerializable</code> class with the data serialization framework. This
* method is usually invoked from the static initializer of a class that implements
* <code>DataSerializable</code>.
*
* @param instantiator An <code>Instantiator</code> whose {@link #newInstance} method is invoked
* when an object is data deserialized.
*
* @throws IllegalStateException If class <code>c</code> is already registered with a different
* class id, or another class has already been registered with id <code>classId</code>
* @throws NullPointerException If <code>instantiator</code> is <code>null</code>.
*/
public static synchronized void register(Instantiator instantiator) {
InternalInstantiator.register(instantiator, true);
}
/**
* Registers a <code>DataSerializable</code> class with the data serialization framework. This
* method is usually invoked from the static initializer of a class that implements
* <code>DataSerializable</code>.
*
* @param instantiator An <code>Instantiator</code> whose {@link #newInstance} method is invoked
* when an object is data deserialized.
*
* @param distribute True if the registered <code>Instantiator</code> has to be distributed to
* other members of the distributed system. Note that if distribute is set to false it may
* still be distributed in some cases.
*
* @throws IllegalArgumentException If class <code>c</code> is already registered with a different
* class id, or another class has already been registered with id <code>classId</code>
* @throws NullPointerException If <code>instantiator</code> is <code>null</code>.
* @deprecated as of 9.0 use {@link Instantiator#register(Instantiator)} instead
*/
public static synchronized void register(Instantiator instantiator, boolean distribute) {
InternalInstantiator.register(instantiator, distribute);
}
//////////////////////// Constructors ////////////////////////
/**
* Creates a new <code>Instantiator</code> that instantiates a given class.
*
* @param c The <code>DataSerializable</code> class to register. This class must have a static
* initializer that registers this <code>Instantiator</code>.
* @param classId A unique id for class <code>c</code>. The <code>classId</code> must not be zero.
* This has been an <code>int</code> since dsPhase1.
*
* @throws IllegalArgumentException If <code>c</code> does not implement
* <code>DataSerializable</code>, <code>classId</code> is less than or equal to zero.
* @throws NullPointerException If <code>c</code> is <code>null</code>
*/
public Instantiator(Class<? extends DataSerializable> c, int classId) {
if (c == null) {
throw new NullPointerException(
LocalizedStrings.Instantiator_CANNOT_REGISTER_A_NULL_CLASS.toLocalizedString());
}
if (!DataSerializable.class.isAssignableFrom(c)) {
throw new IllegalArgumentException(
LocalizedStrings.Instantiator_CLASS_0_DOES_NOT_IMPLEMENT_DATASERIALIZABLE
.toLocalizedString(c.getName()));
}
if (classId == 0) {
throw new IllegalArgumentException(LocalizedStrings.Instantiator_CLASS_ID_0_MUST_NOT_BE_0
.toLocalizedString(Integer.valueOf(classId)));
}
this.clazz = c;
this.id = classId;
}
////////////////////// Instance Methods //////////////////////
/**
* Creates a new "empty" instance of a <Code>DataSerializable</code> class whose state will be
* filled in by invoking its {@link DataSerializable#fromData fromData} method.
*
* @see DataSerializer#readObject
*/
public abstract DataSerializable newInstance();
/**
* Returns the <code>DataSerializable</code> class that is instantiated by this
* <code>Instantiator</code>.
*/
public final Class<? extends DataSerializable> getInstantiatedClass() {
return this.clazz;
}
/**
* Returns the unique <code>id</code> of this <code>Instantiator</code>.
*/
public final int getId() {
return this.id;
}
/**
* sets the unique <code>eventId</code> of this <code>Instantiator</code>. For internal use only.
*/
public final void setEventId(Object/* EventID */ eventId) {
this.eventId = (EventID) eventId;
}
/**
* Returns the unique <code>eventId</code> of this <code>Instantiator</code>. For internal use
* only.
*/
public final Object/* EventID */ getEventId() {
return this.eventId;
}
/**
* sets the context of this <code>Instantiator</code>. For internal use only.
*/
public final void setContext(Object/* ClientProxyMembershipID */ context) {
this.context = (ClientProxyMembershipID) context;
}
/**
* Returns the context of this <code>Instantiator</code>. For internal use only.
*/
public final Object/* ClientProxyMembershipID */ getContext() {
return this.context;
}
}