Summary: Implementing a Validator application that queries the space for account objects showing the different query methods GigaSpaces offers.

Overview

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
    1. Writes an initial amount of accounts to the space.
    2. Starts cycles of feeding new order objects to the space, each order is labeled Insecure or Normal.
  • Validator
    1. 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.
    2. 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.

Two additional components are:

  • Counter - Counts the number of Insecure orders that are riskier then allowed according to their corresponding account (Demonstrates use of GSIterator).
  • Scripting Counter - Counts number of Insecure orders with risk attribute higher then a fixed risk factor (Demonstrates use of Scripting).
    Scripting Counter is available only from GigaSpaces 6.5 and onwards.

Application Workflow

The application workflow is as follows:

  1. The Feeder PU's Pre-Loader service pre-loads the space with 100 account objects.
  2. After the Pre-Loader finishes, the Feeder Service starts cycles of feeding the space with new order objects with type Normal or Insecure.
  3. 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 Insecure order written to the space.
  4. Once a Normal order 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)
  5. 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:

GigaSpaces Features in this Tutorial

The following SQLQuery methods are used in this tutorial:

  • Plain, non parameterized SQL queries using rlike for regular expression pattern matching.
  • Parameterized querying using a template object for parameter binding.
  • Parameterized querying using JDBC like API for parameter binding.
  • Using queries as templates for notify and polling containers.
  • Queries in scripts.
  • Using GSIterator for iterations based query.
  • Indexing fields to speed up query based operations.

How to Query

Before you begin, you can take a look at this section, which summarizes the different query methods and shows how to index fields to speed up query based operations. If you're already familiar with the SQLQuery API, you can jump ahead to Writing the code. All code is documented inside each example as well.

See use of Query using Scripting and Query using GSIterator inside the tutorial.

For more detailed information, see SQLQuery.

We use the following Person POJO:

public class Person{

   private Integer age;
   private Integer height;
   private String name;
   .
   .
   Constructors/Setters/Getters
}

SQLQuery is the primary API when accessing the space.

Simple query

SQL Query

Using a SQL query in the code:

// Create SQLQuery that returns all the person objects that have age field equals 5 and height field less then 180
SQLQuery<Person> query = new SQLQuery<Person>(Person.class,"age = 5 and height < 180");

// Get all results that match the query
Person[] result = gigaSpace.readMultiple(query /* the query */ ,Integer.MAX_VALUE /* maximum objects to read */);

Plain, Non-Parameterized SQL Query using rlike

Regular expression pattern matching should be used with extreme care. Matching regular expressions in read operations is done in a serial manner and cannot take advantage of the space indexing mechanisms. Therefore, with large result sets, it can take a considerable amount of time to process and consume a lot of CPU resources.
Using regular expression pattern matching for notify templates is less dangerous since matching the objects is done once at a time, when the operation that the template was registered for (read, write, update, take, etc.) is invoked. In this case, the client application is not directly affected by this as the template matching is done in the background and does not affect client performance directly.

Using regular expressions for pattern matching on POJO properties in the query is done using the rlike option, as part of the SQL query:

// Create SQLQuery with 'rlike' that return all the objects that have a name field that starts with a or c
SQLQuery<Person> query = new SQLQuery<Person>(Person.class,"name rlike '(a|c).*'");

// Get all results that match the query
MyObject[] result = gigaSpace.readMultiple(query /* the query */ ,Integer.MAX_VALUE /* maximum objects to read */);

Parameterized query using JDBC like API

You can use a single SQLQuery instance multiple times by binding different values to it every time. The query should include the ? placeholder instead of the actual value. When executing the query, the condition that includes the ? placeholder is replaced with corresponding field values taken from the value defined in the setParameters method, or in the constructor.

Performing dynamic queries using parameters allows you to use the same field several times as part of the SQL statement, where the template value is used to fill in the parameter values.

You can perform dynamic queries using parameters, or using templates:

It is not recommended to perform both types of dynamic queries (using parameters and using templates) in one query. If you perform these together, only the values you define in the dynamic query using parameters is taken into account.

Set the parameters using the setParameters() method:

SQLQuery query<Person> = new SQLQuery<Person>(Person.class,"age > ? or height > ? and name=?");
query.setParameters(22,178,"USA");

Or set each parameter seperetly using the setParameter() method:

query.setParameter(1,22);
query.setParameter(2,178);
query.setParameter(3,"USA");

Or pass the values in the constructor:

SQLQuery<Person> query = new SQLQuery<Person>(Person.class,"age > ? or height > ? and name=?",22,178,"USA");

Parameterized query using a template object

You can create one SQLQuery object and assign different queries and templates to it. The values in the template are used to construct the query. The query should include the ? placeholder instead of the actual value. When executing the query, the condition that includes the ? placeholder is replaced with corresponding field values taken from the relevant template field.

When performing dynamic queries using templates, you can not use the same field several times as part of the SQL statement. To do this, use the setParameters() method in the previous tab (Parameterized query using JDBC like API).

Set the the Person template attributes:

Person template = new Person();
template.setAge(22);
template.setHeight(178);
template.setName("USA");

Set the query template using the setTemplate method:

SQLQuery<Person> query;
query.setTemplate(template);
query.setQuery("age > ? and height < ? and name=?");

Or pass the template and clause in the constructor:

SQLQuery<Person> query = new SQLQuery<Person>(template,"age > ? and salary < ? and userName=?");

Query in event containers

This polling container (defined in a pu.xml configuration file) registers for notifications on every Person object that is written to the space and matches the query template. Upon registration, the simpleListener is invoked:

Namespace:

<os-events:notify-container id="eventContainer" giga-space="gigaSpace">
    <os-core:sql-query where="age>22 and height>178 and name=&apos;USA&apos;" class="mypackage.Person"/>
    <os-events:listener>
        <os-events:annotation-adapter>
            <os-events:delegate ref="simpleListener"/>
        </os-events:annotation-adapter>
    </os-events:listener>
</os-events:notify-container>

Plain XML:

<bean id="eventContainer" class="org.openspaces.events.notify.SimpleNotifyEventListenerContainer">

    <property name="gigaSpace" ref="gigaSpace" />

    <property name="template">
        <bean class="com.j_spaces.core.client.SQLQuery">
            <constructor index="0" value="mypackage.Person" />
            <constructor index="0" value="age>22 and height>178 and name=&apos;USA&apos;" />
        </bean>
    </property>
    
    <property name="eventListener">
    	<bean class="org.openspaces.events.adapter.AnnotationEventListenerAdapter">
    	    <property name="delegate" ref="simpleListener" />
    	</bean>
    </property>
</bean>

Index to speed up operations

Index to speed up operations

Querying indexed fields speeds up read and take operations.
To index a field use the field level decoration @SpaceProperty:

import com.gigaspaces.annotation.pojo.SpaceProperty;
import com.gigaspaces.annotation.pojo.SpaceProperty.IndexType;

public class Person{
   .
   .
   private String name;
   .
   .
   @SpaceProperty(index=IndexType.BASIC)
   public String getName() {
		return name;
	}
}

Field Level Decoration @SpaceProperty

Element XML Space Mapping File Element Name Type Description Default Value
index index Enum of IndexType Defines if this field data is indexed. Querying indexed fields speeds up read and take operations. Possible values of NONE and BASIC. NONE
nullValue null-value String Specifies that a value be treated as null.
(Usage: @SpaceProperty(nullValue="-1") whereas -1 functions as a null value)
 

Writing the Code

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

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 {
	
	private String firstName;
	private String lastName;
	private Integer amount;
	private Integer 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)
	public String getFirstName() {
		return firstName;
	}
	.
	.
	more constructors/setters/getters	
}

View Account.java class source code

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 if this 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 {
	
	public static final String STATUS_NEW = "New";	
	public static final String STATUS_APPROVED = "Approved";	
	public static final String STATUS_REJECTED = "Rejected";
	public static final String PRIORITY_URGENT = "Insecure";
	public static final String PRIORITY_NORMAL = "Normal";
    
    private String orderID;
    private String feederID;
    private String firstName;
    private String lastName;
    private Integer riskInvolved;
    private Integer price;

    /**	Order status, Possible values: New, Approved, Rejected. */
    private String status;
    
    /**	Order type, Possible values: Normal, Insecure */
    private String 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)
    public String getOrderID() {
        return orderID;
    }
    
    /** 
     * @return userName - Gets the user name of the orderEvent object.
     * @SpaceProperty Defines if this field data is indexed.
	 */
    @SpaceProperty(index = IndexType.BASIC)
    public String getFirstName() {
        return firstName;
    }
    .
    .
    more constructors/getters/setters
    
}

View OrderEvent.java class source code


Feeder - PreLoader Service

PreLoader Service (Within the Feeder)

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 - Feeder Service

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
     */
    private long 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 for this client
     */
    private Double 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 for this feeder
		feederID = new Double(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 implements Runnable {
    	
    	//	Counts number of fed orderEvents
        private int 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();
            }
        }

        public int getCounter() {
            return counter;
        }
    }

    public int getFeedCount() {
        return orderEventFeederTask.getCounter();
    }

	public void setFeederID(Double feederID) {
		this.feederID = feederID;
	}

	public Double getFeederID() {
		return feederID;
	}
}

Feeder - Config

Feeder Configuration

The XML file/code based application, holding the Feeder beans configuration.

Here the Preloader and Feeder services are wired and configured.

Configured using pu.xml file:

<?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>

Configured using code:

/*
 * 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 {

	public static IJSpace space;
	public static GigaSpace gigaSpace;
	
	/**
	 * @param args
	 * @throws Exception 
	 */
	public static 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();	
	}
}

Validator - Normal Validator Service

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 {
	
	private long 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;
    }
}

Validator - Insecure Counter Service

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 = new Thread(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 implements Runnable {
	    
		// 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 - Config

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:

Configured using pu.xml file:

<?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=&apos;Normal&apos; and status=&apos;New&apos;" 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=&apos;Insecure&apos; and status=&apos;New&apos;" 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>

Configured using code:

/*
 * 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 {

	public static IJSpace space;
	public static GigaSpace gigaSpace;
	
	/**
	 * @param args
	 * @throws Exception 
	 */
	public static 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
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
 
.
.
.
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]
.
.
.
GigaSpaces.com - Legal Notice - 3rd Party Licenses - Site Map - API Docs - Forum - Downloads - Blog - White Papers - Contact Tech Writing - Gen. by Atlassian Confluence