com.gemstone.gemfire.Instantiator Maven / Gradle / Ivy
/*
* 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;
import com.gemstone.gemfire.internal.InternalInstantiator;
import com.gemstone.gemfire.internal.cache.EventID;
import com.gemstone.gemfire.internal.cache.tier.sockets.ClientProxyMembershipID;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
/**
* Instantiator
allows classes that implement {@link
* DataSerializable} to be registered with the data serialization
* framework. Knowledge of DataSerializable
classes
* allows the framework to optimize how instances of those classes are
* data serialized.
*
*
*
* Ordinarily, when a DataSerializable
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
* DataSerializable
object. After the marker class id is
* read by {@link DataSerializer#readObject} it performs the following
* operations,
*
*
*
* - The class name is read
*
* - The class is loaded using {@link Class#forName(java.lang.String)}
*
* - An instance of the class is created using reflection
*
* - {@link DataSerializable#fromData} is invoked on the
* newly-created object
*
*
*
* However, if a DataSerializable
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 DataSerializable
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 Instantiator
instance is invoked to
* create an "empty" instance of the DataSerializable
* instead of using reflection to create the new instance.
*
*
*
* Commonly, a DataSerializable
class will register
* itself with the Instantiator
in a static initializer
* as shown in the below example code.
*
*
*
*
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();
}
}
*
*
* Instantiator
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 User
class into the
* region. The act of instantiating User
will load the
* User
class and invoke its static initializer, thus
* registering the Instantiator
with the data
* serialization framework. Because the region is a replicate, the
* User
will be data serialized and sent to VM2.
* However, when VM2 attempts to data deserialize the
* User
, its Instantiator
will not
* necessarily be registered because User
's static
* initializer may not have been invoked yet. As a result, an
* exception would be logged while deserializing the User
* and the replicate would not appear to have the new value. So, in
* order to ensure that the Instantiator
is registered in
* VM2, the data serialization framework distributes a message to each
* member when an Instantiator
is {@linkplain #register(Instantiator)
* registered}. Note that the framework does not require that an
* Instantiator
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
*
* @author David Whitlock
* @since 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 Instantiator
*/
private int id;
/** The eventId of this Instantiator
*/
private EventID eventId;
/** The originator of this Instantiator
*/
private ClientProxyMembershipID context;
/////////////////////// Static Methods ///////////////////////
/**
* Registers a DataSerializable
class with the data
* serialization framework. This method is usually invoked from the
* static initializer of a class that implements
* DataSerializable
.
*
* @param instantiator
* An Instantiator
whose {@link #newInstance}
* method is invoked when an object is data deserialized.
*
* @throws IllegalStateException
* If class c
is
* already registered with a different class id, or another
* class has already been registered with id
* classId
* @throws NullPointerException
* If instantiator
is null
.
*/
public static synchronized void register(Instantiator instantiator) {
InternalInstantiator.register(instantiator, true);
}
/**
* Registers a DataSerializable
class with the data
* serialization framework. This method is usually invoked from the
* static initializer of a class that implements
* DataSerializable
.
*
* @param instantiator
* An Instantiator
whose {@link #newInstance}
* method is invoked when an object is data deserialized.
*
* @param distribute
* True if the registered DataSerializer
has to be
* distributed to other members of the distributed system.
*
* @throws IllegalArgumentException
* If class c
is
* already registered with a different class id, or another
* class has already been registered with id
* classId
* @throws NullPointerException
* If instantiator
is null
.
*/
public static synchronized void register(Instantiator instantiator,
boolean distribute) {
InternalInstantiator.register(instantiator, distribute);
}
//////////////////////// Constructors ////////////////////////
/**
* Creates a new Instantiator
that instantiates a given
* class.
*
* @param c
* The DataSerializable
class to register. This
* class must have a static initializer that registers this
* Instantiator
.
* @param classId
* A unique id for class c
. The
* classId
must not be zero.
* This has been an int
since dsPhase1.
*
* @throws IllegalArgumentException
* If c
does not implement
* DataSerializable
, classId
is
* less than or equal to zero.
* @throws NullPointerException
* If c
is null
*/
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 DataSerializable
* class whose state will be filled in by invoking its {@link
* DataSerializable#fromData fromData} method.
*
* @see DataSerializer#readObject
*/
public abstract DataSerializable newInstance();
/**
* Returns the DataSerializable
class that is
* instantiated by this Instantiator
.
*/
public final Class extends DataSerializable> getInstantiatedClass() {
return this.clazz;
}
/**
* Returns the unique id
of this
* Instantiator
.
*/
public final int getId() {
return this.id;
}
/**
* sets the unique eventId
of this
* Instantiator
. For internal use only.
*/
public final void setEventId(Object/*EventID*/ eventId) {
this.eventId = (EventID)eventId;
}
/**
* Returns the unique eventId
of this
* Instantiator
. For internal use only.
*/
public final Object/*EventID*/ getEventId() {
return this.eventId;
}
/**
* sets the context of this
* Instantiator
. For internal use only.
*/
public final void setContext(Object/*ClientProxyMembershipID*/ context) {
this.context = (ClientProxyMembershipID)context;
}
/**
* Returns the context of this
* Instantiator
. For internal use only.
*/
public final Object/*ClientProxyMembershipID*/ getContext() {
return this.context;
}
}