Summary: GigaSpaces JavaSpaces API Plain Old Java Object support - the
POJO.
Overview
GigaSpaces JavaSpaces POJO support allows you to use JavaBean classes as space domain classes, and perform space operations using these objects. POJO domain Classes should follow rules similar to the ones defined by JPA, Hibernate and other domain class frameworks.
GigaSpaces POJO rules:
Do not implement the net.jini.core.entry interface.
Avoid using numeric primitives (long , int) as the POJO fields. Use their relevant Object wrapper instead (Long , Integer). This will avoid the need to specify a null-value and simplify query construction when using the SQLQuery.
Have getter and setter methods for every field you would like to be stored within the space object.
Include space class metadata decorations (indexed fields, affinity-keys, persisted mode, etc.).
You can define space classes metadata by class and field level decorations. These can be defined via annotations or XML configurations files (*gs.xml file).
This section deals with the POJO class as a space domain class, used to model the space, and store application data into the IMDG. POJO classes deployed as services into the Service Grid are described in the Data Event Listener and Space Based Remoting sections. In these cases, the POJO class is used to process incoming data, or is invoked remotely.
A POJO as a Space Domain Class
When using a POJO as a space domain class, follow these guidelines:
A POJO class must implement a default (zero argument) constructor.
A POJO class cannot implement the net.jini.core.entry interface; otherwise, it is treated differently.
A POJO class should have space class metadata decorations using annotations or a gs.xml file with relevant metadata (indexed field list, version field, FIFO mode, persistency mode, primary key (i.e. id)). If neither are provided, the defaults are presumed. (The default settings might not always match your needs.)
Getter/setter methods for fields that you want to be persisted in the space.
Non-primitive fields must implement Serializable or Externalizable. For example, if you are using a POJO class that contains a nested class.
When performing matching/queries using primitive fields (int , long , double, etc.) thenullValueannotation ornull-valuetag must be specified with relevant values, to function correctly.
The ID field can be determined using the @SpaceId annotation or the id tag.
To force GigaSpaces to ignore a POJO field when the space class being first time introduced to the space you should use one of the following:
Use the @SpaceExclude annotation on the getter method for the fields you don't want to be included as part of the space class.
Use the @SpaceClass(includeProperties=IncludeProperties.EXPLICIT) class level annotation and use the @SpaceProperty() with each getter method for fields you would like to be considered as part of the space class.
When using POJOs, the write operation uses the UpdateModifiers.UPDATE_OR_WRITE mode by default. This means that when the space already includes an object with the same UID (@SpaceId(autoGenerate=false) with the same value), the POJO is updated, and a new object is not inserted.
When using SpaceId(autoGenerate=true), the UID is stored inside the SpaceId field, causing an overhead when indexed.
TokenQuery is not supported with POJOs.
POJO space mapping files gs.xml files can be loaded from:
<CLASSPATH>\config\mapping folder, or
The same package where the class file is located using the format <<Class Name>>.gs.xml.
A null value as template is not supported. Use new Object() instead.
A POJO class must implement the Serializable or Externalizable interface if used as a parameter for a remote call. (see OpenSpaces remoting for more detail.)
The @Spaceid annotation or id tag must be declared when performing update operations.
To persist a POJO object using the ExternalDataSource , Mirror Service, JDBC Storage Adapter, or indexed file options, the persist decoration must have the value true.
When a space is configured to use the ExternalDataSource, the @Spaceid annotation or id tag auto-generate attribute should be set to false. The object must include a unique value with the SpaceId field when written into the space.
The SpaceId field can be java.lang.String type or any other type that implements the toString() which provides a unique value.
All net.jini.core.entry.Entry based classes meta data methods are not supported with POJO based classes. These include: _setEntryInfo() , __getEntryInfo() , __setEntryUID() , __getEntryUID() ,_getSpaceIndexedFields(). With POJO based space domain classes, meta data is declared using relevant annotations or xml tags.
Primitive boolean should not be used as a POJO field as this could lead to problems when using template based matching. Boolean should be used instead.
In order to query a base abstract class you, due to the fact that one can't create an instance of an abstract class, SQLQuery should be used.
When running in embedded mode, Space object fields are passed by reference to the space. Extra caution should be taken with non-primitive none mutable fields such as collections (HashTable, Vector). Changes made to those fields outside the context of the space will impact the value of those fields in the space and may result in unexpected behavior. For example, index lists aren't maintained because the space is unaware of the modified field values. For those fields it is recommended to pass a cloned value rather then the reference itself. Passing a cloned value is important when several threads access the Object fields - for example application threads and replication threads.
Indexing is critical for good performance over large spaces. Don't forget to index properly with the @SpaceIndex(type=SpaceIndexType.BASIC) or @SpaceIndex(type=SpaceIndexType.EXTENDED) annotation or use the gs.xml equivalent.
UrlSpaceConfigurer urlSpaceConfigurer = new UrlSpaceConfigurer("jini://*/*/mySpace");
GigaSpace space = new GigaSpaceConfigurer(urlSpaceConfigurer.space())
.defaultTakeTimeout(1000)
.defaultReadTimeout(1000)
.gigaSpace();
The following writes an Employee object and reads it back using a simple template:
GigaSpace space;
Employee employee = new Employee("Last Name", newInteger(32));
employee.setFirstName("first name");
space.write(employee);
Employee template = new Employee();
Employee result = space.read(template);
Notifications
Registering for Notifications
The following registers for notifications:
GigaSpace space;
SimpleNotifyEventListenerContainer
notifyEventListenerContainer = new SimpleNotifyContainerConfigurer(space)
.template(new Employee())
.eventListenerAnnotation(newObject()
{
@SpaceDataEvent
public void eventHappened(Object event) {
System.out.println("onEvent called Got" + event);
}
})
.fifo(true)
.notifyWrite(true)
.notifyUpdate(true)
.notifyContainer();
Writing a Batch
Writing a Batch of Objects
When writing a batch of objects into the space, these should be placed into an array to be used by the GigaSpace.writeMultiple operation. The returned array will include the corresponding LeaseContext object.
GigaSpace space;
Employee emps[] = new Employee[2];
emps[0] = new Employee("Last Name A", newInteger(10));
emps[1] = new Employee("Last Name B", newInteger(20));
try {
LeaseContext[] leaseContexts = space.writeMultiple(emps);
for (int i = 0;i<leaseContexts.length ; i++) {
System.out.println ("Object UID " + leaseContexts[i].getUID() + " inserted into the space");
}
} catch (WriteMultipleException e) {
IWriteResult[] writeResult = e.getResults();
for (int i = 0;i< writeResult.length ; i++) {
System.out.println ("Problem with Object UID " + writeResult ");
}
}
Reading using SQLQuery
Reading Objects using SQLQuery
The following queries the space using SQL:
GigaSpace space;
String querystr = "employeeID=1 or employeeID=2";
SQLQuery query = new SQLQuery(Employee.class.getName(), querystr);
Employee results[] = space.readMultiple(query , 10000);
Constructing SQLQuery objects is a relatively expensive operation. You should not construct these with every space query operation. Instead, it is recommended to construct it once, and then use it with dynamic query options: SQLQuery.setParameters and SQLQuery.setParameter.
Clear
Clear Objects
You can use the SQLQuery with the GigaSpace.clear to remove objects from the space:
When using the SQLQuery with bigger/less than queries, turn on the extended indexing.
Updating an Object
Updating an Object
The GigaSpace.write with the UpdateModifiers.UPDATE_ONLY modifier should be used to explicitly perform an update operation. The UpdateModifiers.UPDATE_OR_WRITE is the default mode with write operations. This means that subsequent calls to the write operation with an object with identical SpaceId will result in an update operation - i.e. a new object will not be inserted into the space.
Make sure your Space Class will have the SpaceId(autoGenerate=false) when performing update operations.
The GigaSpace.write has a few activity modes - With each mode the return object options are different.:
Inserting or updating an existing object - The UpdateModifiers.UPDATE_OR_WRITE modifier should be used. This is the default mode.
. Thrown only when running in Optimistic Locking mode. This Exception includes the existing version id of the object within the space and the client side version id of the object. In this case you should read the object again and retry the update operation. See Optimistic Locking for more details.
when the UpdateModifiers.PARTIAL_UPDATE modifier is applied the return values are the same as with the UpdateModifiers.UPDATE_ONLY case. Fields that should not be updated should have the value null. This means that only fields which are set will be sent into the space to replace the existing field's value. Make sure the updated object include its ID when using this option.
When updating an object, you can specify 0 (ZERO) as the lease time. This will instruct the space to use the original lease time used when the object has been written into the space.
try
{
LeaseContext ret = space.write(employee ,/*lease*/ 0 ,/*timeout*/ 1000 , UpdateModifiers.UPDATE_ONLY);
if ( ret == null)
{
// Object is locked - unsuccessful update
}
elseif (ret.getObject() instanceof Employee)
{
// successful update
}
}
catch (EntryNotInSpaceException enise)
{
// Object not in space - unsuccessful update
}
catch (SpaceOptimisticLockingFailureException solfe)
{
// Client holds wrong version of the object - unsuccessful update. We need to read it again and issue the update call again.
}
PARTIAL_UPDATE Example:
GigaSpace space = new GigaSpaceConfigurer (new UrlSpaceConfigurer("jini://*/*/mySpace").noWriteLease(true)).gigaSpace();
MyClass obj = new MyClass();
obj.setId("1");
obj.setField1("A");
obj.setField2("B");
obj.setField3("C");
space.write(obj);
obj.setField1(null);
obj.setField2("BBBB");
obj.setField3(null);
try
{
space.write(obj,0,0,UpdateModifiers.PARTIAL_UPDATE);
MyClass ret = space.readById(MyClass.class , "1");
}
catch (EntryNotInSpaceException enise)
{
// Object not in space - unsuccessful update
}
Updating a Batch
Updating a Batch of Objects
Make sure your Space Class will have the SpaceId(autoGenerate=false) when performing update operations.
The GigaSpace.updateMultiple returns an array of objects which correspond to the input object array. The returned object element can be one of the following:
mode does not support timeout based updates, there is no way to identify if an updated object is already locked under a transaction - i.e. the UpdateOperationTimeoutExceptionis not returned as part of the returned array elements. With a transactional system, it is recommended to perform batch updates using the UpdateModifiers.UPDATE_ONLY modifier.
Next subchapter:POJO Support - Advanced - This advanced section deals with the annotations and gs.xml mapping file, troubleshooting procedures, considerations, UID generation and usage, as well as frequently used code snippets.