layout | title | categories | weight | parent |
---|---|---|---|---|
post |
Creating the Hello World Application |
SBP |
200 |
first-xap-app.html |
{%summary%}{%endsummary%}
Learn how to create and run a Processing Unit - a scalable unit of deployment, inside your development environment. Learn how to use the XAP basic API, by implementing a simple processor and feeder application.
There are two components in our scenario:
{% section %}
{% column width=40% %}
(1)[Processor Processing Unit](#Processor Processing Unit) - Processes Message objects as they are written to the data grid (Space)
It contains 3 components: {%wbr%}
a Polling Container
component that listens to new Message objects written to the Space
, and a Processor Bean
that is delegated the actual processing work by the Polling Container
.
{% endcolumn %} {% column width=10% %} {% endcolumn %} {% column width=40% %}
(2) Feeder - an application that feeds unprocessed Message objects to the Space, and after a certain period of time, counts and reads one of them at random.
(3) Message Object - a simple POJO with an id and info attributes. {% endcolumn %} {% column width=10% %}
{% endcolumn %} {% endsection %}
The Processing Unit itself runs within a dedicated processing unit container in a host environment. (This can be your IDE, any Java process, or the GigaSpaces Grid Service Container - more on this in the next tutorial.)
{% section %} {% column width=70% %}
-
The helloFeeder application writes 1000 Message objects (POJOs) to the space and waits.
-
Inside the Processing Unit, the Polling Container continuously removes unprocessed objects from the data grid (one at a time) and hands them to its Processor Bean for processing.
-
After each Message object has been processed, the Polling Container writes it back to the Space. (Steps 2 & 3 repeat, until there are no more unprocessed Message objects left to process in the Space.)
-
After waiting 100 milliseconds (to allow for all the objects to be processed), the feeder counts all the processed Message objects inside the Processor Processing Unit's Space, and reads one of them at random.
{% endcolumn %} {% column width=30% %} {%popup /attachment_files/Helloworld_workflow.jpg%} {% endcolumn %} {% endsection %}
{% anchor Jwalkthrough %}
You can download the full source code from {%git https://github.com/Gigaspaces/xap-example-helloworld %}
First let's take a look at the Message object that is being written to the space by the feeder application:
{% anchor JObject %}
This is a simple POJO containing two attributes: id, which represents the object id, and info, which represents the information that this object holds. Both have setter and getter methods.
{% info %}
The getter for the id attribute is annotated with the @SpaceRouting
annotation that is used to route Message objects when they are written to the space. This is necessary for scaling the application, and will be explained in the next tutorial. For now, just remember that this annotation should decorate one of the object's properties.
{%endinfo%}
{% highlight java %} private Integer id; // object id
public void setId(Integer id) { this.id = id; }
@SpaceRouting public Integer getId() { return id; } {% endhighlight %}
{% highlight java %} private String info; // info represents the info the object holds
public String getInfo() { return info; }
public void setInfo(String info) { this.info = info; } {% endhighlight %}
A necessary default empty constructor and another constructor to construct a new Message object with a given id and info:
{% highlight java %} public Message() { // Mandatory empty constructor
}
public Message(Integer id, String info) { this.id = id;
this.info = info;
} {% endhighlight %}
Next, let's take a look at the Processor Processing Unit.
{% anchor Processor Processing Unit %}
The Processor Processing Unit contains two components: a space (cache), which holds objects in memory, and a processor bean that takes, modifies and writes objects back to this space.
{% comment %} ---- OUT-----
Processing Unit File Structure
The Processing Unit is the GigaSpaces unit of deployment. It is packaged as a JAR file. For example, our processor processing unit has the following directory structure:
{% indent %} hello-processor.jar META-INF/spring pu.xml - the processing unit configuration file org\openspaces\example\helloworld\processor (and any other package in your application) Processor.class - the class definition of the processor bean lib - contains any JAR files that your processing unit needs to run shared-lib - contains JAR files with classes that are shared with other processing units (e.g. our Message class) {% endindent %}
---- OUT----- {% endcomment %}
Processor Processing Unit Configuration (META-INF/spring/pu.xml)
A Processing Unit always has an XML file called pu.xml, that resides under the META-INF\spring directory. In fact, this is a standard Spring framework XML configuration file, with a number of custom GigaSpaces specific tags. Let's take a look at this file. In our example there are 3 main components contained within the Processing Unit:
- The first component is a space (cache) instance embedded inside the Processing Unit, named processorSpace. It has a URL property. On the second line, we define a transaction manager, which is referencing this space, and manages its transactions. Finally a bean called gigaSpace wraps the space, and provides a simple client API to interact with it, as we will see later in this tutorial.
{% highlight xml %} <os-core:embedded-space id="space" name="processorSpace"/> <os-core:distributed-tx-manager id="transactionManager" /> <os-core:giga-space id="gigaSpace" space="space" tx-manager="transactionManager"/> {% endhighlight %}
- The second component is a helloProcessor Bean, which contains the method that does the actual processing. This bean is defined in the Processor.java source file, which is shown in the [next section](#Processor Bean).
{% highlight xml %} {% endhighlight %}
The third, key component in this workflow is the Polling Container, which continuously removes (takes) objects matching certain criteria from the space. The criteria are expressed in the form of a template object (also known as example object). In our case, the polling container is instructed to take objects of type Message. However, it does not take all instances of the Message class, only those whose "info" property equals the string "Hello ". When a match is found, the object is taken and passed to a listener bean - here the listener is the previously defined Processor bean. This bean has a method annotated with the @SpaceDataEvent annotation, which is invoked with the taken object as a parameter. It returns a processed Message object, which is written back to the space by the Polling Container.
{% highlight xml %} <os-events:polling-container id="helloProcessorPollingEventContainer" giga-space="gigaSpace"> <os-events:tx-support tx-manager="transactionManager"/> os-core:template </os-core:template> os-events:listener os-events:annotation-adapter <os-events:delegate ref="helloProcessor"/> </os-events:annotation-adapter> </os-events:listener> </os-events:polling-container> {% endhighlight %}
Next we'll see the source code for the Processor bean.
{% anchor Processor Bean %} Processor Bean (Processor.java)
We saw that the pu.xml file defines a bean called Processor. Now let's look at this bean's source code. It has one method. The primary method, processMessage(), is annotated with the @SpaceDataEvent annotation. Previously we saw that the Processor bean is referenced by the Polling Container and acts as its listener. When a Message object is taken from the space by the Polling Container, this method is invoked with the object as an argument. It returns a processed object. In this example it simply adds the "World !!" string to the object's info property:
{% highlight java %} public class Processor {
@SpaceDataEvent
public Message processMessage(Message msg) {
System.out.println("Processor PROCESSING : " + msg);
msg.setInfo(msg.getInfo()+"World !!");
return msg;
}
public Processor(){
System.out.println("Processor instantiated...");
}
} {% endhighlight %}
Now we are ready to view feeder application that feeds Message objects to the space.
{% anchor JFeeder %}
The feeder main
method constructs a new Feeder instance, and passes the space URL to it, to connect to the space.
Each space is identified uniquely by its name. In the processor processing unit, we defined the space with the URL "/./processorSpace", which means an embedded space named "processorSpace". Therefore the URL the feeder uses to connect to the space, is "jini:/*/*/processorSpace". Without getting into the details, it means that the Jini protocol is used to discover a space named "processorSpace" in the network. It is passed to the feeder as a program argument.
We then start writing the Message object to the space and then read the results from it.
{% highlight java %} public static void main(String [] args) { if(args.length==0){ System.out.println("Usage: java Feeder "); System.exit(1); }
Feeder feeder = new Feeder (args[0]); // create the feeder and connect it to the space
feeder.feed(1000); // run the feeder (start feeding)
feeder.readResults(); // read back results
} {% endhighlight %}
Here's the constructor of the Feeder connects to the Processor Processing unit Space by using the input URL:
{% highlight java %} public Feeder(String spaceName){
// Wrap the space with the gigaSpace API
this.gigaSpace = new GigaSpaceConfigurer(new UrlSpaceConfigurer(spaceName)).gigaSpace();
} {% endhighlight %}
The feed()
method loops and writes Message objects to the space by using the gigaSpace.write()
method:
{% highlight java %} public void feed(int numberOfMessages){ for(int counter=0;counter<numberOfMessages;counter++){ Message msg = new Message(counter, "Hello "); gigaSpace.write(msg); } System.out.println("FEEDER WROTE " + numberOfMessages + " messages"); } {% endhighlight %}
Here's how all processed objects are read from the space, using template matching. The number of processed objects in the space (all of them should have their info property set to "Hello World !!") is then printed out:
{% highlight java %} public void readResults(){
Message template = new Message(); // Create a template to read a Message with info
template.setInfo("Hello World !!"); // attribute that equals "Hello World !!"
// Read an object matching the template
System.out.println("Here is one of them printed out: "+gigaSpace.read(template));
//wait 100 millis for all to be processed:
try{
Thread.sleep(100);
}catch(InterruptedException ie){ /*do nothing*/}
// Count number of objects in the space matching the template
int numInSpace=gigaSpace.count(template);
System.out.println("There are "+numInSpace+" processed Message objects in the space now.");
} {% endhighlight %}
Next, we compile and run the sample application
{% anchor JRun All In IDE %}
Steps to run the application inside Eclipse IDE:
If you haven't already done so,download GigaSpaces and set up your development environment
- This is needed for running the tutorial sample application.
{% anchor JImporting Project to the IDE %}
Importing the project into Eclipse
- Import the hello-common, *hello-processor and hello-feeder projects located under the
<XAP Root>/examples/helloworld
folder. (After importing, you'll see some errors since the GS_HOME path variable is not set yet)
{% togglecloak id=IMPORT %} How do I do that...{% endtogglecloak %} {% gcloak IMPORT %} {% panel bgColor=white|borderStyle=solid %}
Importing the sample projects into the IDE
- Start Eclipse. A Workspace Launcher Dialog appears.
- Write a new workspace name or select one of your existing workspaces, and click the OK button.
- To import the project, select File > Import ... to open the import dialog
- Select Existing projects into workspace and click Next to open the import project dialog
- In the Select root directory field click the Browse button to open the browse dialog
- Select the folder
/examples/helloworld
and click OK - Make sure all 3 projects are selected: hello-common, hello-processor and hello-feeder
- Click Finish
{% endpanel %} {% endgcloak %}
- Create a new Eclipse environment variable called GS_HOME, and point it to your GigaSpaces installation Root folder
{% togglecloak id=GSHOME %} How do I do that...{% endtogglecloak %} {% gcloak GSHOME %} {% panel bgColor=white|borderStyle=solid %}
Setting an environment variable pointing to the XAP root folder
- Right Click on the hello-common project in the Package Explorer tab to open the context menu
- Select Build Path > Configure Build Path... to open the Java Build Path dialog
- Select the Libraries tab and click the Add Variable... button to open the New Variable Classpath Entry dialog
- Click the Configure Variables... button to open the Classpath Variables dialog
- Click the New... button to open the New Variable Entry dialog
- In the Name field write
GS_HOME
to name the variable - Click the Folder... button and browse to your GigaSpaces installation root folder
- Select your GigaSpaces installation root folder and click OK
- Click OK and OK again
- Click Yes to do full rebuild
- Close remaining dialogs
{% endpanel %} {% endgcloak %}
{% tip %}
Make sure your project includes the latest Spring libraries located at <XAP Root>\lib\required
folder.
{% endtip %}
{% anchor Run Processor in IDE %} Running the Processor
- From the toolbar at the top of the screen, select Run > Run Dialog... to open the Run dialog
- Click the + to the left of Java Application, to unfold it
- Select the Hello Processor launch configuration, and click the Run button
{% anchor JRun Feeder in IDE %}
Waiting for the Processor to instantiate
- Before running the feeder, you should wait for the following output to appear in the Console tab at the bottom of the screen: Processor instantiated, waiting for messages feed... This indicates the Processor is up and running.
{% anchor JRun Feeder in IDE2 %}
Running the Feeder
- From the toolbar at the top of the screen, select Run > Run Dialog... to open the Run dialog again
- Click the + left to Java Application, to unfold it
- Select the Hello Feeder launch configuration
- Click the Run button
{% anchor JView Output %}
You can use the Management Console to view the Object count and statistics for the different operations:
Running the processor and the feeder results in the following output, which can be viewed in the Console tab at the bottom of the screen. Use the Display Selected Console button to switch between the feeder and processor output consoles
Feeder expected output
The feeder starts, writes 100 message objects to the space, reads and prints one of them at random, and finally prints the number of processed messages in the space: {%highlight console%} Starting the Feeder (Will wait for the space to initialize first...) FEEDER WROTE 1000 objects Here is one of them printed out: id[47] info[Hello World !!] There are 841 processed Message objects in the space now. Press any key to continue . . . {%endhighlight%}
Processor expected output
The processor prints the id and info attributes for each messages it takes for processing:
{%highlight console%} Processor PROCESSING : id[445] info[Hello ] Processor PROCESSING : id[904] info[Hello ] Processor PROCESSING : id[896] info[Hello ] Processor PROCESSING : id[446] info[Hello ] Processor PROCESSING : id[889] info[Hello ] . . . Processor PROCESSING : id[893] info[Hello ] Processor PROCESSING : id[905] info[Hello ] Processor PROCESSING : id[897] info[Hello ] Processor PROCESSING : id[875] info[Hello ] Processor PROCESSING : id[900] info[Hello ] {%endhighlight%}
To view the Launch configurations do the following:
- From the toolbar at the top of the screen, select Run > Run Dialog... to open the Run dialog again
- Click the + left to Java Application, to unfold it
Launch configuration to run the Processor inside the IDE The launch is configured to use the hello-feeder project and to run the main class found in the class org.openspaces.examples.feeder.Feeder
The url the feeder is using to connect to the space is written in the
Launch configuration to run the Processor inside the IDE