This tutorial assumes knowledge of JavaSpaces API terms like the space, Entries and leases. If you're not familiar with these concepts, start with the previous tutorial, Plain JavaSpaces.
In this tutorial you will implement a Validator application that queries the space for account objects showing the different query methods GigaSpaces offers.
Application Components
There are three main components in our workflow:
Enterprise Data Grid space instance
Feeder
Writes an initial amount of accounts to the space.
Starts cycles of feeding new order objects to the space, each order is labeled Insecure or Normal.
Validator
Starts query-based polling of Normal orders from the space, validates according to the relevant account and writes the order back to the space as approved/rejected.
Registers for query-based notifications on Insecure orders, performs read on notify, holds a counter that is increased each time an Insecure order with risk higher then allowed (compared to a relevant account) is read.
Another additional component is:
Counter - Counts the number of Insecure orders that are riskier then allowed according to their corresponding account (Demonstrates use of GSIterator).
Application Workflow
The application workflow is as follows:
The Feeder PU's Pre-Loader service pre-loads the space with 100 account objects.
After the Pre-Loader finishes, the Feeder Service starts cycles of feeding the space with new order objects with type Normal or Insecure.
The Validator PU polling container continuously tries to take from the space new Normal orders, using a query template.
The Validator notify container is registered for query-based notification, for every New and Insecureorder written to the space.
Once a Normalorder is taken by a the polling container, the Normal Validator service validates it by querying the space for the relevant account for the order.
When a notification is received by the notify container about a written Insecure order to the space, the Risky Counter service examines it by querying the space for the relevant account for the order. its counter is increased if the account allowed risk is sufficient for the order.
(The Validator Service and Counter Service uses different query and query types (Directly Parameterized Query/Template Based Query)
The order is validated and marked as approved if an account matching the query is found, otherwise it is marked as rejected. After validation is complete, the order is written back to the space.
The Scripting and GSIterator based components are described individually in the following sections:
The following tabs show the services and configuration for each Processing Unit.
Usage of Parameterized query using a template object is demonstrated inside the Normal Validator Service tab.
Usage of Parameterized query using JDBC like API is demonstrated inside the Risky Counter Service tab.
Usage of Query templates in notify and polling containers is demonstrated inside the Feeder - Config and Validator - Config tabs.
Domain Model
The domain model is comprised of the Account object preloaded to the space and used by the Validator for order validation.
The Account object is a simple POJO representing a user account. It includes the user's first and last name attributes, an amount of money in the account attribute, and a risk allowed factor for this account, to compare against an order risk involved factor, during the order validation process:
/**
* A POJO account object used for query containing four fields:
* firstName, lastName, amount and riskAllowed
*
* Annotations:
* @SpaceProperty(index=IndexType.BASIC) annotated getters mark the attribute as indexed.
* Querying indexed fields speeds up read and take operations.
*
* @SpaceClass annotation in this example is only to indicate that this class is a space class.
*/
@SpaceClass
public class Account {
privateString firstName;
privateString lastName;
privateInteger amount;
privateInteger riskAllowed;
.
.
/**
* <code>@SpaceProperty</code> Defines this field data as indexed.
* Querying indexed fields speeds up read and take operations. Possible values of NONE and BASIC.
*/
@SpaceProperty(index=IndexType.BASIC)
publicString getFirstName() {
return firstName;
}
.
.
more constructors/setters/getters
}
The OrderEvent POJO represents an order placed by the Feeder, to be validated by the Validator.
The order includes the user name, order type and order status attributes – an orderID attribute that acts as the unique order identifier in the space (annotated as @SpaceID – see code snippet below), and a risk involved factor attribute used by the Validator business logic to validate the order:
/**
* Some important properties:
* orderID - annotated as the object unique space id (see the getter method for the annotation).
* firstName (used to perform routing when working with partitioned space, see getter method for annotation).
* status - indicating ifthis OrderEvent object in new, processed, or rejected.
* type - Normal or Insecure
*
* Annotations:
*
* Fields with getters annotated as @SpaceProperty(index=IndexType.BASIC) are indexed.
* Querying indexed fields speeds up read and take operations.
*
* @SpaceRouting annotation (see getFirstName()) indicates that firstName field
* will be used as a routing index to perform routing when working with partitioned space.
*
* @SpaceClass annotation in this example is only to indicate that this class is a space class.
*/
@SpaceClass
public class OrderEvent {
publicstaticfinalString STATUS_NEW = "New";
publicstaticfinalString STATUS_APPROVED = "Approved";
publicstaticfinalString STATUS_REJECTED = "Rejected";
publicstaticfinalString PRIORITY_URGENT = "Insecure";
publicstaticfinalString PRIORITY_NORMAL = "Normal";
privateString orderID;
privateString feederID;
privateString firstName;
privateString lastName;
privateInteger riskInvolved;
privateInteger price;
/** Order status, Possible values: New, Approved, Rejected. */
privateString status;
/** Order type, Possible values: Normal, Insecure */
privateString type;
/**
* Gets the ID of the orderEvent.
* @SpaceID annotation indicates that its value will be auto generated
* when it is written to the space.
*/
@SpaceId(autoGenerate = true)
publicString getOrderID() {
return orderID;
}
/**
* @return userName - Gets the user name of the orderEvent object.
* @SpaceProperty Defines ifthis field data is indexed.
*/
@SpaceProperty(index = IndexType.BASIC)
publicString getFirstName() {
return firstName;
}
.
.
more constructors/getters/setters
}
The Feeder's preloader service bean writes 100 unique Account objects to the space.
/*
* Copyright 2008 GigaSpaces Technologies Ltd. All rights reserved.
*
* THIS SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. GIGASPACES WILL NOT
* BE LIABLE FOR ANY DAMAGE OR LOSS IN CONNECTION WITH THE SOFTWARE.
*/
package com.gigaspaces.examples.tutorials.queries.feeder;
import org.openspaces.core.GigaSpace;
import org.openspaces.core.context.GigaSpaceContext;
import org.springframework.beans.factory.InitializingBean;
import com.gigaspaces.examples.tutorials.queries.common.Account;
/**
* A loader bean that writes Account objects with unique user names to the
* space. Since the write is executed in the init() method called directly in the afterPropertiesSet method
* (and not in a new thread), the processing unit waits until the loading is
* finished before initializing the next bean.
*/
public class AccountPreLoader implements InitializingBean {
/**
* Number of accounts to be loaded by the loader, hard-coded to 100, can be overridden
* in the pu.xml (by setting the prop key "numberOfAccounts")
*/
private int numberOfAccounts = 100;
@GigaSpaceContext(name = "gigaSpace")
private GigaSpace gigaSpace;
public void setGigaSpace(GigaSpace gigaSpace) {
this.gigaSpace = gigaSpace;
}
/**
* Allows to control the number of accounts that will be initially
* loaded to the Space. Defaults to <code>100</code>.
*/
public void setNumberOfAccounts(int numberOfAccounts) {
this.numberOfAccounts = numberOfAccounts;
}
/**
* The first method to run upon bean Initialization when implementing InitializingBean.
* Runs the pre-loader init() method
*/
public void afterPropertiesSet() throws Exception {
init();
}
/**
* init - Initializes the pre-loader:
* Writes <numberOfAccounts> unique accounts to the space.
*/
public void init() throws Exception {
System.out.println("\nFeeder Pre-Loader Starts writing accounts");
// Writing <numberOfAccounts> accountData objects to the space.
for (int i = 1; i <= numberOfAccounts; i++) {
Account account = new Account("FN"+i /* firstName */
,"LN"+i /* lastName */
,1000 /* amount */
,50); /* riskAllowedFactor */
gigaSpace.write(account);
}
System.out.println("Feeder Wrote "+numberOfAccounts+" Accounts\n");
}
}
Feeder Service (Within the Feeder)
The Feeder's service bean (started after the Feeder's preloader has finished – defined in the configuration as dependent) starts periodic cycles of an order feeding task to the space.
/*
* Copyright 2008 GigaSpaces Technologies Ltd. All rights reserved.
*
* THIS SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. GIGASPACES WILL NOT
* BE LIABLE FOR ANY DAMAGE OR LOSS IN CONNECTION WITH THE SOFTWARE.
*/
package com.gigaspaces.examples.tutorials.queries.feeder;
import org.openspaces.core.GigaSpace;
import org.openspaces.core.context.GigaSpaceContext;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import com.gigaspaces.examples.tutorials.queries.common.OrderEvent;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* A feeder bean that starts a scheduled task that writes a new OrderEvent object to the space.
* The orderEvent type attribute is set randomly to "normal" or "Insecure". <p>
*
* The space is injected into this bean using OpenSpaces support for @GigaSpaceContext
* annotation. <p>
*
* The scheduled support uses the java.util.concurrent Scheduled Executor Service. It
* is started and stopped based on Spring life-cycle events.
*/
public class OrderEventFeeder implements InitializingBean, DisposableBean {
private Random randomGen = new Random();
private ScheduledExecutorService executorService;
// Delayed result bearing action
private ScheduledFuture<?> sf;
/**
* Delay between scheduled tasks
*/
privatelong defaultDelay = 1000;
/**
* The scheduled orderEvent feeding task.
*/
private OrderEventFeederTask orderEventFeederTask;
@GigaSpaceContext(name = "gigaSpace")
private GigaSpace gigaSpace;
public void setGigaSpace(GigaSpace gigaSpace) {
this.gigaSpace = gigaSpace;
}
/**
* Unique ID forthis client
*/
privateDouble feederID;
/**
* @param defaultDelay - Sets default delay between feeding tasks.
*/
public void setDefaultDelay(long defaultDelay) {
this.defaultDelay = defaultDelay;
}
/**
* The first method to run upon bean Initialization, when implementing InitializingBean.
* Runs init() method which starts a scheduled orderEvent feeding task.
*/
public void afterPropertiesSet() throws Exception {
init();
}
/**
* init - Starts a scheduled orderEvent feeding task.
* @throws Exception
*/
public void init() throws Exception {
// Create unique ID forthis feeder
feederID = newDouble(System.nanoTime());
System.out.println("Feeder ["+feederID.toString()+"], Starting order feeding cycles (Delay between cycles "+defaultDelay+" msec)");
// Create a thread pool containing 1 thread capable of performing scheduled tasks
executorService = Executors.newScheduledThreadPool(1);
orderEventFeederTask = new OrderEventFeederTask();
// Schedule the thread to execute the task at fixed rate with the default delay defined
sf = executorService.scheduleAtFixedRate(
orderEventFeederTask // The task to schedule
,defaultDelay // Initial Delay before starting
,defaultDelay // Delay between tasks
,TimeUnit.MILLISECONDS // Time unit for the delay
);
}
public void destroy() throws Exception {
// Shutting down the thread pool upon bean disposal
sf.cancel(true);
sf = null;
executorService.shutdown();
}
public class OrderEventFeederTask implementsRunnable {
// Counts number of fed orderEvents
privateint counter;
Integer randomizedNameSuffix;
String randomizedType;
public void run() {
try {
// Prepare randomized values to set the orderEvent attributes with
randomizedNameSuffix = randomGen.nextInt(99)+1;
if (randomGen.nextBoolean()) {
randomizedType=OrderEvent.TYPE_NORMAL;
}
else {
randomizedType=OrderEvent.TYPE_INSECURE;
}
// Create a new orderEvent with randomized attributes
OrderEvent orderEvent = new OrderEvent("FN" + randomizedNameSuffix /* firstName */
,"LN" + randomizedNameSuffix/* lastName */
,feederID.toString() /* feederID */
,randomizedType /* type */
,randomGen.nextInt(99)+1 /* riskInvolvedFactor */
,(randomGen.nextInt(19)+1)*100); /* price */
// Write the new orderEvent to the space
gigaSpace.write(orderEvent);
System.out.println("\nFeeder wrote order:\n"+orderEvent);
}
catch (Exception e) {
e.printStackTrace();
}
}
publicint getCounter() {
return counter;
}
}
publicint getFeedCount() {
return orderEventFeederTask.getCounter();
}
public void setFeederID(Double feederID) {
this.feederID = feederID;
}
publicDouble getFeederID() {
return feederID;
}
}
Feeder Configuration
The XML file/code based application, holding the Feeder beans configuration.
Here the Preloader and Feeder services are wired and configured.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:os-core="http://www.openspaces.org/schema/core"xmlns:os-events="http://www.openspaces.org/schema/events"xmlns:os-remoting="http://www.openspaces.org/schema/remoting"xmlns:os-sla="http://www.openspaces.org/schema/sla"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.openspaces.org/schema/core http://www.openspaces.org/schema/core/openspaces-core.xsd
http://www.openspaces.org/schema/events http://www.openspaces.org/schema/events/openspaces-events.xsd
http://www.openspaces.org/schema/remoting http://www.openspaces.org/schema/remoting/openspaces-remoting.xsd
http://www.openspaces.org/schema/sla http://www.openspaces.org/schema/sla/openspaces-sla.xsd">
<!-- ========================================================================================================================== -->
<!-- Spring property configurer which allows us to use system properties (such as user.name).
Here we can define the numberOfAccounts to feed, injected to the PreLoader bean -->
<bean id="propertiesConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"><property name="properties"><props><prop key="numberOfAccounts">100</prop></props></property></bean><!-- Enables the usage of @GigaSpaceContext annotation based injection. --><os-core:giga-space-context/>
<!-- A bean representing a space (an IJSpace implementation).
Note, we perform a lookup on the space since we are working against a remote space. -->
<os-core:space id="space" url="jini://*/*/spaceQueries"/><!-- OpenSpaces simplified space API built on top of IJSpace/JavaSpace. --><os-core:giga-space id="gigaSpace" space="space"/><!-- ================================================================================================ --><!-- The Account pre-loader bean, writing new 100 unique accounts to the space. --><bean id="accountPreLoader" class="com.gigaspaces.examples.tutorials.queries.feeder.AccountPreLoader"><property name="numberOfAccounts" value="${numberOfAccounts}" /></bean>
<!-- The Data feeder bean, writing new OrderEvents objects to the space in a constant interval.
The depends-on attribute ensures the feeder bean will start only after the pre-loader bean is done -->
<bean id="orderEventFeeder" class="com.gigaspaces.examples.tutorials.queries.feeder.OrderEventFeeder" depends-on="accountPreLoader"/></beans>
/*
* Copyright 2008 GigaSpaces Technologies LTD. All rights reserved.
*
* THIS SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. GIGASPACES WILL NOT
* BE LIABLE FOR ANY DAMAGE OR LOSS IN CONNECTION WITH THE SOFTWARE.
*/
package com.gigaspaces.examples.tutorials.queries.codebasedfeeder;
import org.openspaces.core.GigaSpace;
import org.openspaces.core.GigaSpaceConfigurer;
import org.openspaces.core.space.UrlSpaceConfigurer;
import com.j_spaces.core.IJSpace;
import com.gigaspaces.examples.tutorials.queries.feeder.AccountPreLoader;
import com.gigaspaces.examples.tutorials.queries.feeder.OrderEventFeeder;
/**
* The feeder connects to the remote space and:
* 1. Pre-loads the space with 100 accounts.
* 2. Starts feeding the space with orderEvent objects, type set
* randomly as "Normal" or "Insecure". <p>
*
* This "CodeBased" version of the feeder is configured inside the main() method.
* The Processing Unit version of the feeder uses a pu.xml configuration file and can
* run inside a stand alone container or deployed onto GigaSpaces' ServiceGrid.
*/
public class CodeBasedFeeder {
publicstatic IJSpace space;
publicstatic GigaSpace gigaSpace;
/**
* @param args
* @throws Exception
*/
publicstatic void main(String[] args) throws Exception {
// Connect to the space
// ====================
// Connect to a remote space.
space = new UrlSpaceConfigurer("jini://*/*/spaceQueries").space();
// Create a GigaSpace simpler interface to interact with the space
gigaSpace = new GigaSpaceConfigurer(space).gigaSpace();
// Create the account pre-loader, connect it to the space, and initiate it.
// (The pre-loader writes 100 unique accounts to the space).
// =========================================================
AccountPreLoader accountPreLoader = new AccountPreLoader();
accountPreLoader.setGigaSpace(gigaSpace);
accountPreLoader.init();
// Create the orderEvent feeder, connect it to the space, and initiate it.
// (The feeder starts cycles of "New", "Normal"/"Insecure" order feeding task).
// ==========================================================================
OrderEventFeeder orderEventFeeder = new OrderEventFeeder();
orderEventFeeder.setGigaSpace(gigaSpace);
orderEventFeeder.init();
}
}
Normal Orders Validator Service (Within the Validator)
The Validator's validating bean in charge of validating Normal orders:
/*
* Copyright 2008 GigaSpaces Technologies LTD. All rights reserved.
*
* THIS SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. GIGASPACES WILL NOT
* BE LIABLE FOR ANY DAMAGE OR LOSS IN CONNECTION WITH THE SOFTWARE.
*/
package com.gigaspaces.examples.tutorials.queries.validator;
import org.openspaces.core.GigaSpace;
import org.openspaces.events.adapter.SpaceDataEvent;
import com.gigaspaces.examples.tutorials.queries.common.Account;
import com.gigaspaces.examples.tutorials.queries.common.OrderEvent;
import com.j_spaces.core.client.SQLQuery;
/**
* Simple bean used to validate the "Normal" orderEvent objects.
* Showing use of template parameterized query.
*/
public class NormalOrderEventValidator {
privatelong workDuration = 100;
/**
* Sets the simulated work duration (in milliseconds). Default to 100.
*/
public void setWorkDuration(long workDuration) {
this.workDuration = workDuration;
}
/**
* Validates the given OrderEvent object and returns the validated OrderEvent with
* status field set to Approved/Rejected according to the validation.
* Can be invoked using OpenSpaces Events when a matching event
* occurs.
* The order is approved if an account for the user is found holding enough money
* to buy the order (compared to orderEvent.price attribute).
*/
@SpaceDataEvent // This annotation marks the method as the event listener.
public OrderEvent validatesOrderEvent(OrderEvent orderEvent, GigaSpace gigaSpace) {
// sleep to simulate some work
try {
Thread.sleep(workDuration);
} catch (InterruptedException e) {
// do nothing
}
System.out.println("\nValidator validates [Normal] order: First Name["+orderEvent.getFirstName()+
"] Last Name ["+orderEvent.getLastName()+
"] Price ["+orderEvent.getPrice()+"]");
// Querying using template query
// =============================
// Create the template for the query
Account queryTemplate = new Account(orderEvent.getFirstName()/* account first name*/
,orderEvent.getLastName()/* account last name*/
,orderEvent.getPrice());/* account amount*/
// Create the query using the values from the template, each ? sign is replaced with the corresponding value
// The query is actually "firstName=(queryTemplate.firstName) and lastName=(queryTemplate.lastName) and amount>(queryTemplate.amount)" SQLQuery<Account> query = new SQLQuery<Account>(queryTemplate,"firstName = ? and lastName = ? and amount > ?");
// REMARK: Instead of using the constructor the query can also be
// set using the setTemplate and setQuery methods:
// ===============================================
// query.setTemplate(queryTemplate);
// query.setQuery("firstName = ? and lastName = ? and amount > ?");
// Read from the space the account matching the query
Account account=(Account)gigaSpace.read((Object)query);
// Set the order status according to the query result
if (account!= null)
{
orderEvent.setStatus(OrderEvent.STATUS_APPROVED);
}
else
{
orderEvent.setStatus(OrderEvent.STATUS_REJECTED);
}
System.out.println("Validator set order status to: ["+orderEvent.getStatus()+"]");
// orderID is declared as primary key and as auto-generated.
// It must be null before writing an operation.
orderEvent.setOrderID(null);
return orderEvent;
}
}
Risky Insecure Orders Counting Service (Within in Validator)
The Validator counting bean in charge of counting risky Insecure orders.
/*
* Copyright 2008 GigaSpaces Technologies LTD. All rights reserved.
*
* THIS SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. GIGASPACES WILL NOT
* BE LIABLE FOR ANY DAMAGE OR LOSS IN CONNECTION WITH THE SOFTWARE.
*/
package com.gigaspaces.examples.tutorials.queries.counter;
import java.util.concurrent.atomic.AtomicInteger;
import org.openspaces.core.GigaSpace;
import org.openspaces.core.IteratorBuilder;
import org.openspaces.core.context.GigaSpaceContext;
import org.springframework.beans.factory.InitializingBean;
import com.gigaspaces.examples.tutorials.queries.common.Account;
import com.gigaspaces.examples.tutorials.queries.common.OrderEvent;
import com.j_spaces.core.client.GSIterator;
import com.j_spaces.core.client.SQLQuery;
/**
* Simple bean used to read and display the "Insecure" orderEvent objects.
* Shows use of GSIterator.
*/
public class RiskyOrderEventCounter implements InitializingBean{
private AtomicInteger riskyOrderEventCounter = new AtomicInteger(0);
@GigaSpaceContext(name = "gigaSpace")
private GigaSpace gigaSpace;
public void setGigaSpace(GigaSpace gigaSpace) {
this.gigaSpace = gigaSpace;
}
/**
* The first method to run upon bean Initialization when implementing InitializingBean.
* Runs the init() method
*/
public void afterPropertiesSet() throws Exception {
init();
}
/**
* Starts a new thread running the OrderEventIteratorTask.
*/
public void init(){
Thread thread = newThread(new OrderEventIteratorTask());
thread.start();
}
/**
* Iterates through all the orderEvents matching the specified templates.
* Counts every orderEvent read, that has an associated account (same user name as the orderEvent)
* with a satisfying risk allowed.
*/
public class OrderEventIteratorTask implementsRunnable {
// Create a query for"New" and "Insecure" orderEvent objects.
SQLQuery<OrderEvent> queryInsecureOrders = new SQLQuery<OrderEvent>(OrderEvent.class,"status='"+OrderEvent.STATUS_NEW+"' and type='"+OrderEvent.TYPE_INSECURE+"'");
// Create and configure an iteratorBuilder to build iterators for the space with the specified templates.
IteratorBuilder iteratorBuilder = new IteratorBuilder(gigaSpace)
.addTemplate(queryInsecureOrders)
.bufferSize(100) // Limit of the number of objects to store for each iteration.
.withHistory(); // Indicates that this iterator will be first pre-filled with matching objects,
// otherwise it will start iterating only on newly arriving objects to the space.
public void run()
{
try
{
System.out.println("Counter creats iterator over the space insecure orders:");
// Build the iterator using the previously configured iteratorBuilder.
GSIterator gsIterator = iteratorBuilder.iterate();
System.out.println("Counter iterator thread reading messages");
OrderEvent insecureOrderEvent;
// Create the query for an account object, each ? place-holder will be replaced inside the following loop using the setParameters method.
SQLQuery<Account> accountQuery = new SQLQuery<Account>(Account.class,"firstName = ? and lastName = ? and riskAllowed > ?");
while (true)
{
while (gsIterator.hasNext())
{
insecureOrderEvent = (OrderEvent)gsIterator.next();
System.out.println("\nCounter examines order: "+insecureOrderEvent);
// Updating the query parameters, the query will actually equal:
// firstName=(accountQuery.firstName value) and lastName=(accountQuery.lastName value) and amount>(accountQuery.amount value)
accountQuery.setParameters(insecureOrderEvent.getFirstName(), /* account user first name*/
insecureOrderEvent.getLastName(), /* account user last name*/
insecureOrderEvent.getRiskInvolved()); /* account amount*/
// Read an account matching the query
Account account = (Account)gigaSpace.read((Object)accountQuery);
// Set the order status according to the query result
if (account != null)
{
System.out.println("Counter - examined order's risk is higher then allowed.");
riskyOrderEventCounter.incrementAndGet();
}
else
{
System.out.println("Counter - examined order's risk is allowed.");
}
System.out.println("Counter - total of ["+riskyOrderEventCounter+"] insecure risky orders (with risk higher then allowed) counted.");
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
}
Validator Configuration
The XML file/code based application, holding the Validator beans configuration.
The Normal Validator service bean is defined inside a query-based polling container, and the Insecure Counter service bean is defined in a query-based notify container:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:os-core="http://www.openspaces.org/schema/core"xmlns:os-events="http://www.openspaces.org/schema/events"xmlns:os-remoting="http://www.openspaces.org/schema/remoting"xmlns:os-sla="http://www.openspaces.org/schema/sla"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.openspaces.org/schema/core http://www.openspaces.org/schema/core/openspaces-core.xsd
http://www.openspaces.org/schema/events http://www.openspaces.org/schema/events/openspaces-events.xsd
http://www.openspaces.org/schema/remoting http://www.openspaces.org/schema/remoting/openspaces-remoting.xsd
http://www.openspaces.org/schema/sla http://www.openspaces.org/schema/sla/openspaces-sla.xsd">
<!-- ========================================================================================================================== --><!-- Enables the usage of @GigaSpaceContext annotation based injection. --><os-core:giga-space-context/>
<!-- A bean representing a space (an IJSpace implementation).
Note, we do not specify here the cluster topology of the space. It is declared outside of
the processing unit or within the SLA bean. -->
<os-core:space id="space" url="jini://*/*/spaceQueries"/><!-- OpenSpaces simplified space API built on top of IJSpace/JavaSpace. --><os-core:giga-space id="gigaSpace" space="space" tx-manager="transactionManager"/><!-- Defines a local Jini transaction manager. --><os-core:local-tx-manager id="transactionManager" space="space"/><!-- The normalOrderEvent validator bean --><bean id="normalOrderEventValidator" class="com.gigaspaces.examples.tutorials.queries.validator.NormalOrderEventValidator"/><!-- The insecureRiskyOrderEvent Counter bean --><bean id="insecureRiskyOrderEventCounter" class="com.gigaspaces.examples.tutorials.queries.validator.InsecureRiskyOrderEventCounter"/><!-- ========================================================================================================================== -->
<!-- A polling event container that performs (by default) polling take operations against
the space using the provided query template (in this case, the new normal orderEvents objects).
Once a match is found, the orderEvent validator bean event listener is triggered using the
annotation adapter, the listener method is annotated inside the bean with the @SpaceDataEvent
annotation. -->
<os-events:polling-container id="orderEventValidatorPollingEventContainer" giga-space="gigaSpace"><os-events:tx-support tx-manager="transactionManager"/><os-core:sql-query where="type='Normal' and status='New'" class="com.gigaspaces.examples.tutorials.queries.common.OrderEvent"/><os-events:listener><os-events:annotation-adapter><os-events:delegate ref="normalOrderEventValidator"/></os-events:annotation-adapter></os-events:listener></os-events:polling-container>
<!-- The notification container, registers for notification on every orderEvent write (notify
on write is default) that satisfies the query (in this case with type="Insecure" and status="New").
Upon notification invokes the insecureRiskyOrderEventCounter listner on a copy of the object that
triggered the event. -->
<os-events:notify-container id="orderEventNotifyContainer" giga-space="gigaSpace"><!-- perform-take-on-notify="true" ignore-event-on-null-take="true">--><os-events:tx-support tx-manager="transactionManager"/><os-core:sql-query where="type='Insecure' and status='New'" class="com.gigaspaces.examples.tutorials.queries.common.OrderEvent"/><os-events:listener><os-events:annotation-adapter><os-events:delegate ref="insecureRiskyOrderEventCounter"/></os-events:annotation-adapter></os-events:listener></os-events:notify-container></beans>
/*
* Copyright 2008 GigaSpaces Technologies LTD. All rights reserved.
*
* THIS SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. GIGASPACES WILL NOT
* BE LIABLE FOR ANY DAMAGE OR LOSS IN CONNECTION WITH THE SOFTWARE.
*/
package com.gigaspaces.examples.tutorials.queries.codebasedvalidator;
import org.openspaces.core.GigaSpace;
import org.openspaces.core.GigaSpaceConfigurer;
import org.openspaces.core.space.UrlSpaceConfigurer;
import org.openspaces.events.notify.SimpleNotifyContainerConfigurer;
import org.openspaces.events.notify.SimpleNotifyEventListenerContainer;
import org.openspaces.events.polling.SimplePollingContainerConfigurer;
import org.openspaces.events.polling.SimplePollingEventListenerContainer;
import com.j_spaces.core.IJSpace;
import com.j_spaces.core.client.SQLQuery;
import com.gigaspaces.examples.tutorials.queries.common.OrderEvent;
import com.gigaspaces.examples.tutorials.queries.validator.NormalOrderEventValidator;
import com.gigaspaces.examples.tutorials.queries.validator.InsecureRiskyOrderEventCounter;
/**
* CodeBasedValidator
* <p>
* The validator connects to the remote space and:
* 1. Registers a notify container that receives by notification "Insecure"&"New"
* orders using query, examines and counts according to their risk.
* 2. Starts a polling container that takes "Normal"&"New" orders using query,
* validates them and writes them back to the space as approved/rejected.
*
* This "CodeBased" version of the validator is configured inside the main() method.
* The Processing Unit version of the validator uses a pu.xml configuration file and can
* ran inside a stand alone container or onto GigaSpaces ServiceGrid.
*/
public class CodeBasedValidator {
publicstatic IJSpace space;
publicstatic GigaSpace gigaSpace;
/**
* @param args
* @throws Exception
*/
publicstatic void main(String[] args) throws Exception {
// Connect to the space
// ====================
// Connects to the remote space using its URL
space = new UrlSpaceConfigurer("jini://*/*/spaceQueries").space();
// Create a GigaSpace simpler interface to interact with the space
gigaSpace = new GigaSpaceConfigurer(space).gigaSpace();
// Create a Notify event container registered for notification when an orderEvent object
// matching the query is written to the space.
//============================================
// Create a SQLQuery to query orderEvent objects with attributes status="New" and type="Insecure" SQLQuery<OrderEvent> queryInsecureOrders = new SQLQuery<OrderEvent>(new OrderEvent(),"type='"+OrderEvent.TYPE_INSECURE+"' and status='"+OrderEvent.STATUS_NEW+"'");
// Create the Notify event container
SimpleNotifyEventListenerContainer notifyEventListenerContainer =
new SimpleNotifyContainerConfigurer(gigaSpace) /* The space the notify container is connected to */
.template(queryInsecureOrders) /* The query to match */
.eventListenerAnnotation(new InsecureRiskyOrderEventCounter()) /* The listener class containing the method to invoke upon notification (annotated @SpaceDataEvent) */
.notifyContainer();
// Create a Polling event container to periodically try to take
// orderEvent objects matching the query, from the space.
//=======================================================
// Create a SQLQuery to query orderEvent objects with attributes status="New" and type="Normal" SQLQuery<OrderEvent> queryNormalOrders = new SQLQuery<OrderEvent>(new OrderEvent(),"type='"+OrderEvent.TYPE_NORMAL+"' and status='"+OrderEvent.STATUS_NEW+"'");
// Create the Polling event container
SimplePollingEventListenerContainer pollingEventListenerContainer =
new SimplePollingContainerConfigurer(gigaSpace) /* The space the polling container is connected to */
.template(queryNormalOrders) /* The query to match */
.eventListenerAnnotation(new NormalOrderEventValidator()) /* The class containing the method to invoke upon match (annotated @SpaceDataEvent) */
.pollingContainer();
}
}
Expected Output
Feeder Pre-Loader Starts writing accounts
Feeder Wrote 100 Accounts
Feeder [1.10211539569135E14], Starting order feeding cycles (Delay between cycles 1000 msec)
Feeder wrote order:
type[Insecure] status[New] price[1200] first Name[FN82] last Name[LN82] risk involved[96]
Feeder wrote order:
type[Normal] status[New] price[700] first Name[FN93] last Name[LN93] risk involved[23]
Feeder wrote order:
type[Normal] status[New] price[600] first Name[FN11] last Name[LN11] risk involved[93]
.
.
.
.
.
.
Validator validates [Normal] order: First Name[FN4] Last Name [LN4] Price [500]
Validator set order status to: [Approved]
Validator validates [Normal] order: First Name[FN39] Last Name [LN39] Price [100]
Validator set order status to: [Approved]
Validator examines [Insecure] order with Risk Factor[53]
Validator - examined order's risk is allowed.
Validator - total of [3] insecure risky orders (with risk higher then allowed) counted.
Validator validates [Normal] order: First Name[FN8] Last Name [LN8] Price [1500]
Validator set order status to: [Rejected]
.
.
.