Developing a Symphony application with Eclipse

Goal

This tutorial walks you through the steps for developing application code using the Symphony plug-in for the Eclipse IDE. It then guides you through the process of building, packaging, and deploying the associated service. This tutorial was prepared for users that are already familiar with the Eclipse IDE.

Prerequisites

Note:

If you are running the Eclipse plug-in on a host that has a Symphony DE pre-5.0 version and Symphony DE 5.0 installed, you must ensure that the system environment is set up for Symphony DE 5.0. Refer to Installing Symphony Developer Edition in the Platform Knowledge Center.

Install the Symphony plug-in for Eclipse

For instructions on installing the Symphony plug-in into Eclipse, refer to the Symphony Plug-in for Eclipse topic in the Application Development Guide.

What the Symphony plug-in can do

The Symphony plug-in for Eclipse is an all-in-one tool package that eases the task of Symphony application development and package deployment.

The plug-in generates a framework of code that provides a foundation for Symphony application development. This framework of basic code is typically common to all Symphony applications. All you need to do is add your own logic to it. One of the key components of the plug-in is the Symphony project wizard, which guides you through the development process and prompts you for application-specific information.

The plug-in also facilitates service package deployment and application registration. This is achieved by combining multiple tasks into single operations through the Symphony DE Platform Management Console, which is integrated with the plug-in.

Where to find the documentation

You can access additional documentation such as the Java API Reference, the Platform Symphony Reference, and the Error Reference from the Symphony DE Knowledge Center.

Windows

  • From the Start menu, select Programs > Platform Computing > Symphony Developer Edition 5.0.0 > Developer Knowledge Center

Linux

  • $SOAM_HOME/docs/symphonyde/5.0/index.html

For convenience, the Java API Reference is also available via the Eclipse Help menu.

Create a new Symphony Java application

This section describes the steps for creating a new Symphony Java application (with generated code) using the Symphony project wizard in Eclipse.

Step 1 Create a new project

  1. Select File > New > Project

    The New Project dialog appears.

  2. Expand the Symphony wizard. Select Symphony Java Application (with Generated Code).
  3. Click Next.

    The Symphony Application Identification dialog appears.

Step 2: Name the application and package

The application name is what binds the client to the service and it must be unique in the cluster.

  1. Enter MySymphonyApp as the Symphony application name. Click Next.
  2. Verify that the Create a package for the generated Java classes box is checked and enter the following package name:

    com.platform.symphony.foo

    Note:

    (Classes that are not placed in a named package belong to the "default package" associated with the current project directory.)

Step 3: Name the client and service classes

  1. Enter AsyncClient as the client class name.
  2. Select Async as the client type. An async client requires a callback class, which will be added to the project when the project is created.
  3. Enter MyService as the service class name. Click Next.

Step 4: Define the message

Your client application needs to handle data that it sends as input, and output data that it receives from the service. You need to define message classes that implement methods to set and access the data, such as the message string and task ID.

  1. In the New Message dialog, click Add.
  2. Enter MyInput as the message class name to handle input messages.
  3. Double-click Edit.

    The Message elements definition dialog appears.

  4. Click Add. Enter Id as the name of the data element. Verify that the data type is int (integer).
  5. Click Add. Enter Message as the name of the second data element. Set the data type to String.
  6. Click OK.
  7. In the New Message dialog, click Add.
  8. Enter MyOutput as the message class name to handle output messages.
  9. Repeat steps 3 to 5 for the MyOutput class..
  10. Click Next.

    The Create a Java project dialog appears.

Step 5: Create the Java project

  1. The project name can be any descriptive name you choose. For this tutorial, enter MySymphonyProj as the project name. Your project will be created in the workspace associated with Eclipse.
  2. Select Create new project in workspace.
  3. Select Use default JRE. Ensure that you are using JDK version 1.5.
  4. Select Use project folder as root for sources and class files.
  5. Click Finish.

    Eclipse creates a new Java project in your workspace with subfolders for the newly-created classes. These classes contain generated code that can be used as a basis for Symphony application development.

    The Symphony plug-in for Eclipse also adds project-dependent files such as the JRE system library and Symphony API to the project.

    Note:

    If you only see the Eclipse welcome screen and not your project with the generated Java code, minimize the welcome screen.

    To view your project in Package Explorer, select Window > Show View > Package Explorer.

Review and understand the generated code

We will review the client, message, and service code that is generated by the Symphony plug-in for Eclipse and discuss what you need to do to complete the application coding.

Client class

Import message class

Since you need to create an instance of the message class in your client class, you must import the message class. Add the following import statement to the generated code for the client class:

import com.platform.symphony.foo.message.*;

Connect to an application

A connection establishes a context for your client and workload. When you connect to an application:

  • Application attributes defined in the application profile are used to provide context such as which service to use, session type, and any additional scheduling or application parameters.

  • A connection object is returned.

The application name in the connection must match that defined in the application profile. This name was assigned when you created the new project using the wizard.

The default security callback encapsulates the callback for the user name and password. In Symphony DE, there is no security checking and login credentials are ignored —you can specify any user name and password. However, when using your client on the grid with Platform Symphony, you need a valid user name and password.

The generated code for connecting to the application is complete and does not require any additional code to make it functional.

Here is the generated code:

...
    // Set up application specific information to be supplied to Symphony
    String appName="MySymphonyApp";
    // Set up application authentication information using the default security provider
 
    DefaultSecurityCallback securityCB = new DefaultSecurityCallback("Guest","Guest");
    Connection connection = null;
    try
    {
      // Connect to the specified application
      connection = SoamFactory.connect(appName, securityCB);
      // Retrieve and print our connection ID
      System.out.println("connection ID=" + connection.getId());
...
    finally
    {
      // Mandatory connection close
      if (connection != null)
      {
          connection.close();
          System.out.println("Connection closed");
      }
    } 
...
Important:

The creation and usage of the connection object is scoped in a try-finally block. The finally block, with the connection.close() method, ensures that the connection is always closed whether exceptional behavior occurs or not. Failure to close the connection causes the connection to continue to occupy system resources.

Create a session to group tasks

A session is a way of logically grouping tasks that are sent to a service for execution.

When creating a session, you need to specify the session attributes by using the SessionCreationAttributes object. The generated code sets four parameters in the SessionCreationAttributes object.

The first parameter is the session name. This is optional. The session name can be any descriptive name you want to assign to your session. It is for informational purposes, such as in the command line interface.

The second parameter is the session type. The session type is optional. You can leave this parameter blank and system default values are used for your session.

The third parameter is the session flag, which the code generator specified as Session.PARTIAL_ASYNC. This flag setting was determined by the client type specified in the project wizard. This indicates to Symphony that the client expects to receive messages asynchronously.

The fourth parameter is the callback object. This object is used by Symphony to call back to the client when the results are ready to be received.

The attributes object is passed to the createSession() method, which returns the created session.

Important:

Similar to the connection object, the creation and usage of the session (sending and receiving data) is scoped in a try-finally block. The finally block, with the session.close() method, ensures that the session is always closed, whether exceptional behavior occurs or not. Failure to close the session causes the session to continue to occupy system resources.

Here is the generated code:

...
    // Set up session attributes
    SessionCreationAttributes attributes = new SessionCreationAttributes();
    attributes.setSessionName("mySession");
    attributes.setSessionType("");  // we will use the default session type
    attributes.setSessionFlags(Session.PARTIAL_ASYNC);
    attributes.setSessionCallback(sessionCallback);
                
    // Create a asynchronous session
    Session session = null;
    try
    {
        session = connection.createSession(attributes);
        // Retrieve and print session ID
         System.out.println("Session ID:" + session.getId());                               
...
    finally
    {
        // Mandatory session close
        if (session != null)
        {
            session.close();
            System.out.println("Session closed");
        }
    }
...

Send input data to be processed

In this step, you create 10 input messages to be processed by the service. When a message is sent, a task input handle is returned. This task input handle contains the ID for the task that was created for this input message. The part of the code that is missing is shown by the TODO comments. To make this code functional, you must create an input message and attach it to the TaskSubmissionAttributes object, which is subsequently sent to the Symphony middleware.

Here is the generated code:

...
    // Now we will send some messages to our service
    for (int taskCount = 0; taskCount < tasksToSend; taskCount++)
    {
        // Create a message
        ////////////////////////////////////////////////////////////////
        // TODO: Place code here to construct message object to be sent
        // eg . InputMessage myInput = new InputMessage(...)
        ////////////////////////////////////////////////////////////////
        // Set task submission attributes
        TaskSubmissionAttributes taskAttr = new TaskSubmissionAttributes();
        ////////////////////////////////////////////////////////////////
        // TODO: Place code here to set input for task submission
        // eg . taskAttr.setTaskInput(myInput);
        ////////////////////////////////////////////////////////////////
        // Send it
        TaskInputHandle input = session.sendTaskInput(taskAttr);
        // Retrieve and print task ID
        System.out.println("task submitted with ID : " + input.getId());
    }
...

The following code snippet shows an example of the completed code for creating the message and attaching it to the TaskSubmissionAttributes object. In this case, we are inserting a simple string and task ID into the input message.

...
    // Now we will send some messages to our service
    for (int taskCount = 0; taskCount < tasksToSend; taskCount++)
    {
        // Create a message
        MyInput myInput = new MyInput();
        myInput.setId(taskCount);
        myInput.setMessage("Hello Grid!!");
        // Set task submission attributes
        TaskSubmissionAttributes taskAttr = new TaskSubmissionAttributes();
        taskAttr.setTaskInput(myInput);
        // Send it
        TaskInputHandle input = session.sendTaskInput(taskAttr);
        // Retrieve and print task ID
        System.out.println("task submitted with ID : " + input.getId());
    }
...

Synchronize the controlling and callback threads

This step is performed after sending the input data to be processed.

Since our client is asynchronous, we need to synchronize the controlling thread and the callback thread. In the generated code, the controlling thread blocks until all replies have come back.

The callback signals when all results are received.

Here is the generated code:

...
    synchronized(sessionCallback)
    {
        while (!sessionCallback.isDone())
        {
            sessionCallback.wait();
        }
    } 
...

Message input/output classes

The MyInput class represents the data input to the service, and the MyOutput class represents the data output from the service. These classes implement methods to set and access the data, such as the message string and task ID.

The code that is generated uses Symphony’s API to serialize the MyInput and MyOutput objects. Symphony serialization is achieved by deriving from the SOAM Message object and implementing the appropriate serialization handlers, i.e., onSerialize() and onDeserialize().

The following code was generated for the MyInput class:

...
public class MyInput extends com.platform.symphony.soam.Message
{
    //=============================================
    //.Constructor.    
    //=============================================  
    public MyInput()
    { 
        super();   
    }    
    //=============================================
    //.Accessors. 
    //============================================= 
    // "Id" () 
    public int getId()
    {      
        return this.Id;
    }    
    public void setId(int value)
    {
        this.Id = value;
    }       
    // "Message" ()
    public String getMessage()
    {
        return this.Message;
    }    
    public void setMessage(String value)
    {      
        this.Message = value; 
    }
    //============================================= 
    //.Serialization - Deserialization.
    //=============================================  
    public void onSerialize(OutputStream ostream) throws SoamException
    {
        ostream.writeLong(this.V4350FA63DBAA4f65A190EDDE29709AC6);
        ostream.writeInt(this.Id); 
        ostream.writeString(this.Message);
    } 
    public void onDeserialize(InputStream istream) throws SoamException 
    {        
        this.V4350FA63DBAA4f65A190EDDE29709AC6 = istream.readLong();
        if (this.V4350FA63DBAA4f65A190EDDE29709AC6 != 1)
        {           
            String errorMessage="A version mismatch error has occured.";
            errorMessage += "The message being deserialized is verion " +
            this.V4350FA63DBAA4f65A190EDDE29709AC6; 
            errorMessage += " but we were expecting verion <1>."; 
            errorMessage += "Verify that all message definitions are up to date.";  
            throw new FatalException(errorMessage); 
        }        
        this.Id = istream.readInt();
        this.Message = istream.readString();    
    }
...

Callback class

When the client type is set to asynchronous in the project wizard, it generates a callback class that extends the SessionCallback class.

Import message class

Since you need to create an instance of the message class in your callback class, you must import the message class. Add the following import statement to the generated code for the callback class:

import com.platform.symphony.foo.message.*;

Retrieve the output

This class contains the onResponse() method to retrieve the output for each input message that is sent. To make the generated code complete, you need to add code to the onResponse() method, as indicated by the TODO comments.

Note that:
  • onResponse() is called every time a task completes and output is returned to the client. The task output handle allows the client code to manipulate the output.

  • isSuccessful() checks whether there is output to retrieve.

public void onResponse(TaskOutputHandle output) throws SoamException
{
    try
    {
        // Check for success of task
        if (output.isSuccessful())
        {
            // Get the message returned from the service
            /////////////////////////////////////////////////////
            // TODO: Retrieve the result from the TaskOutputHandle
            // NOTE : If your output message was generated by the 
            // Symphony Eclipse Pluggin
            // You must use the TaskOutputHandle.populateTaskOutput() method to
            // retrieve the output. The Symphony Eclipse
            // Pluggin generates messages inherited
            // from the "com.platform.symphony.soam.Message" class. 
            // eg.
            // MyOutputMessage myOutput = new MyOutputMessage();
            // output.populateTaskOutput(myOutput);
            ////////////////////////////////////////////////////
            // Display content of reply
            ////////////////////////////////////////////////////
            // TODO: Display some reply
            // eg . 
            // System.out.println("\nTask Succeeded [" + 
            // output.getId() + "]");
            // System.out.println("Your Internal ID was : " +
            // myOutput.getId());
            // System.out.println("Estimated runtime was recorded
            // as : ");
            // System.out.println(myOutput.getRunTime());
            // System.out.println(myOutput.getString());
            ////////////////////////////////////////////////////
        }
        else
        {
            // Get the exception associated with this task
            SoamException ex = output.getException();
            System.out.println("Task Failed : ");
            System.out.println(ex.toString());
        }
    }
    catch (Exception exception)
    {
        System.out.println("Exception occured in onResponse() : ");
        System.out.println(exception.getMessage());
    }
    // Update counter used to synchronize the controlling thread 
    // with this callback object
    incrementTaskCount();
}

The following code example shows the completed code for the onResponse() method. If there is output to retrieve, populateTaskOutput() gets the output. Once results return, print them to standard output and return.

public void onResponse(TaskOutputHandle output) throws SoamException
{
    try
    {
        // check for success of task            
        if (output.isSuccessful())
        {                
            // get the message returned from the service 
            MyOutput myOutput = new MyOutput();
            output.populateTaskOutput(myOutput);
            // display content of reply  
              System.out.println("\nTask Succeeded [" +  output.getId() + "]");             
            System.out.println("Your Internal ID was : " + myOutput.getId());           
            System.out.println(myOutput.getMessage()); 
        }            
        else            
        {             
            // get the exception associated with this task  
            SoamException ex = output.getException();             
            System.out.println("Task Failed :");
            System.out.println(ex.getMessage()); 
        }
    }
    catch (Exception exception)
    { 
        System.out.println("Exception occured in onResponse() : ");         
        System.out.println(exception.getMessage());
    } 
    // Update counter used to synchronize the controlling thread 
    // with this callback object      
    incrementTaskCount();
}

Service class

Import message class

Since you need to create an instance of the message class in your service class, you must import the message class. Add the following import statement to the generated code for the service class:

import com.platform.symphony.foo.message.*;

Process the input

Symphony calls onInvoke() on the service container once per task. This is where the calculation is performed.

Important:

Services are virtualized. As a result, a service should not read from stdin or write to stdout. Services can, however, read from and write to files that are accessible to all compute hosts.

To gain access to the data set from the client, you call the populateTaskInput() method on the taskContext. The Symphony middleware is responsible for placing the input into the taskContext object.

The task context contains all information and functionality that is available to the service during an onInvoke() call in relation to the task that is being processed.

The following code was generated by the Symphony plug-in. What is missing, as identified by the TODO comment, is your service logic for the onInvoke() method.

...
public void onInvoke (TaskContext taskContext) throws SoamException
{
    ////////////////////////////////////////////////////////////////
    // TODO: Place your service logic here. This method will be 
    //       called for each task that has to be processed.
    //
    // NOTE : If your message was generated by the Symphony Eclipse Pluggin
    // You must use the TaskContext.populateTaskInput() method to retrieve 
    // the input. The Symphony Eclipse Pluggin generates messages inherited
    // from the "com.platform.symphony.soam.Message" class. 
    // eg.
    //
    // // get input
    // MyInputMessage myInput = new MyInputMessage();
    // taskContext.populateTaskInput(myInput);
    //
    // // do some processing of input
    //  ...
    //
    // // set output (which is returned to client)
    // taskContext.setTaskOutput(myOutput);
    ////////////////////////////////////////////////////////////////
}
...

The following code example shows the onInvoke() method with the service logic. The input message is simply echoed back to the client by creating a string in the output message and passing it to the setTaskOutput() method.

...
public void onInvoke (TaskContext taskContext) throws SoamException   
{
    // We simply echo the data back to the client
    MyOutput myOutput = new MyOutput();
    // get the input that was sent from the client 
    MyInput myInput = new MyInput();
    taskContext.populateTaskInput(myInput);
    // echo the ID   
    myOutput.setId(myInput.getId());
    // setup a reply to the client
    StringBuffer sb = new StringBuffer(); 
    sb.append("Client sent : ");  
    sb.append(myInput.getMessage());
    sb.append("\nSymphony replied : Hello Client !!");
    myOutput.setMessage(sb.toString()); 
    // set our output message 
    taskContext.setTaskOutput(myOutput);  
}
...

Create a deployment package

You must package the files required by your service to create a service package.

  1. In the Eclipse Package Explorer, right-click on project MySymphonyProj. Select Symphony Operations > Create Deployment Package
  2. Enter MyService.zip as the package name in the Specify the package name textbox.
  3. Click Browse beside Select the package path to select the path where the service package will be stored.
  4. Click Browse beside Specify Main Service Class Name. Enter MyService in the textbox. Click OK.
  5. The JVM options can be used to configure JVM performance tuning such as memory allocation for the service instance. In this tutorial, we are using default JVM settings so it is not necessary to specify any JVM options.
  6. Select *.message.jar and *.service.jar files in the Package name or file name list.
  7. The option for Do stdout redirection from generated service script can be left unchecked. Selecting this option will cause standard output from the JVM to be redirected to a file located in the Symphony DE work directory. The file pattern is <main class name>_<computername>_<process ID of script>_<timestamp>.log.
  8. Click Create and Validate Service Package.

    After creating and validating the service package, the following message is displayed:

    Compressed package_path/MyService.zip successfully!

    The service package is valid and it is ready to be deployed to DE.

  9. Click Finish. The next step is to create an application profile and register your application.

Creating an application profile

The application profile defines the application behavior within Symphony and provides information that Symphony needs to run services and manage workload. It also binds the application to a specific service.

Note:

To create an application profile via the Symphony DE PMC, your Symphony DE cluster must be started.

Create an application profile

  1. In the Package Explorer, right-click on project MySymphonyProj. Select Symphony Operations > Add/Remove Application.
  2. Select Add an application. Click Continue.
  3. Select Create new profile and add application wizard. The wizard automatically populates the application name field with MySymphonyApp.
    Important:

    If your development environment has more than one version of JDK installed (for example, JDK versions 1.4 and 1.5), you should configure the PATH environment variable to point to JDK 1.5. Similarly, if you have 32-bit and 64-bit versions of JDK 1.5 installed, you should configure the PATH environment variable to point to the version that matches your 32-bit or 64-bit platform. Failure to configure the PATH correctly may prevent your service from running. To configure the PATH environment variable, click Specify environment variables for this service. Enter PATH as the Name and the path to the JDK 1.5 bin directory as the value.

  4. Click Continue.
  5. Click Browse and navigate to the location of the service package. Select the service package file MyService.zip. Click Continue.
  6. Select System Defaults for the session type. Click Continue.
  7. Click Confirm to accept the application profile definitions.

    The project wizard creates your application and registers it within Symphony.

Run the application

  1. In the Package Explorer, right-click com.platform.symphony.foo.client class. Select Run As > Java Application. The application runs and prints results in the console window.
  2. To monitor the session status, right-click the project in the Package Explorer and select Symphony Operations > Monitor Workload.

    For more information about monitoring sessions and tasks, refer to the Symphony DE PMC help.

Service package re-deployment

You must update and re-deploy the service package if you have made modifications to the message or service code in your application.

  1. In the Eclipse Package Explorer, right-click on your project. Select Symphony Operations > Create Deployment Package.
  2. Verify the service information in the Symphony Service Packaging Utility dialog.
  3. Click Create and Validate Service Package.

    After creating and validating the service package, the following message is displayed:

    Compressed package_path/MyService.zip successfully!

    The service package is valid and it is ready to be deployed to DE.

  4. Click Deploy the Package.
    Note:

    The Deploy the Package button is only enabled when the application is registered. Clicking this button adds the service package to the Symphony DE repository.

    After deploying the service package, the following message is displayed:

    Service package was deployed successfully to DE.

  5. Click Finish.

Importing samples into Eclipse

Every sample includes a .classpath file sufficient for building the project within Eclipse. Consequently, there is no need to import the project by importing the existing Ant build file.

Note:

Importing and execution of Java samples in Eclipse from Symphony DE TAR packages is not supported.

Note:

Importing samples into Eclipse from previous versions of Symphony DE (3.2 and lower) is not recommended. Older samples require reorganization of their directory structure and editing of the application profile.

Note:

You cannot pre-load information about message code that was generated outside the Eclipse plug-in since the code generation wizard is not aware of message code that has been created or modified externally. For example, if you define new message classes for an imported project using the code generation wizard, the existing message classes from the imported project will not appear in the Message Definition dialog.

In this section, we will import the SampleApp sample that is included with Symphony DE and deploy its service. The Java samples are located at:

Windows

  • %SOAM_HOME%\5.0\samples\Java

Linux

  • $SOAM_HOME/5.0/samples/Java

For more information about the SampleApp code sample or any other sample included with Symphony DE, refer to the appropriate tutorial or readme in the Knowledge Center.

Import samples into Eclipse

  1. From the Eclipse menu, select File > Import.

    The Import dialog appears.

  2. Double-click General. Double-click Existing Projects into Workspace.
  3. Choose Select root directory. Click Browse. Browse to the SampleApp directory in Symphony DE. Click OK.
  4. Click Finish.

Create and validate the service package

  1. In the Eclipse Package Explorer, right-click on the sample project SampleApp. Select Symphony Operations > Create Deployment Package
  2. Enter SampleAppService.zip as the package name in the Specify the package name textbox..
  3. Click Browse to select the path where the service package will be stored.
  4. Click Browse to specify the main service class name. Enter MyService in the textbox. Click OK.
  5. Select *.common.jar and *.service.jar files in the Package name or file name list.
  6. Click Create and Validate Service Package.

    After creating and validating the service package, the following message is displayed:

    Compressed package_path/SampleAppService.zip successfully!

    The service package is valid and it is ready to be deployed to DE.

  7. Click Finish. The next step is to create an application profile and register your application.

Add the application to Symphony DE

  1. In the Package Explorer, right-click on project SampleApp. Select Symphony Operations > Add/Remove Application.

    The Configure Symphony Project Page displays.

  2. Click Validate to test the port connection to the Symphony DE PMC. (The plug-in "pings" the URL of the PMC to verify the connection.)
    Note:

    If the port is already in use, specify a different port number in file vem_resource.conf and enter it in the Please specify the port number textbox.

  3. Once the PMC server replies, click OK.
  4. In the Package Explorer, right-click on project SampleApp. Select Symphony Operations > Add/Remove Application.

    The DE PMC displays.

  5. Select Add an application. Click Continue.
  6. Select Use existing profile and add application wizard. Click Browse and navigate to the location of the SampleAppJava.xml application profile. Click Continue.
  7. Click Browse and navigate to the location of the service package. Select the service package file SampleAppService.zip. Click Continue.
  8. Click Confirm to accept the application profile definitions.

    The project wizard creates your application and registers it within Symphony.

  9. Click Close.
  10. You can now run your client and submit workload to your Symphony application. In the Package Explorer, right-click com.platform.symphony.samples.SampleApp.client class. Select Run As > Java Application. In the Select Java Application dialog, select SyncClient - com.platform.sysmphony.samples.SampleApp.client. Click OK.

Modifying existing applications

If you need to modify a service package or an application profile, refer to the Symphony DE PMC help for more information.