home
products
education
support
partners
company

introduction

get started

articles

manual

tutorials

reference docs

case studies

knowledge base

goodies

screenshots

demos

echoes

Carousel zone


SourceForge.net Logo

home

forums

downloads

bugs

mailing list
XUI Zone - manual

number of ratings 0
average rating 0.00

26 Introduction to Routes and Services

Routes and services within Carousel offer an easy to use way of integrating enterprise level functionality into an applications. Typically routes and services are used to add client-server communications and backend integration to an application.

The concept of a service within Carousel is designed to make it easy to embed a piece of functionality into the data model. A function can be evaluated and the data it returns can be considered to be part of the model just as though it were any other data node. In this way the location of the function and the details of the invocation can be hidden from the programmer. The service concept is sufficiently flexible to allow a wide variety of functional components to be embedded in the model.

Taking this abstraction a little futher we can chain such services together to build more complex functionality. Each service node within the hierarchy can do one specific task and delegate to the next service node for additional features. Because the role of each service node is clear cut it helps simplify implementation of that service.

Frequently services are used to transform, encode or somehow process data. The location of the original data can be hidden within a service and an individual service can be replaced so that high level logic, such as your business logic is freed from low level details that might otherwise give unwanted bindings or even hard coding of platform specific details.

In an enterprise level application these services play a key role, allowing client-server communications to take place. For example, a commonly used service is the HttpRequestService , this service allows that application to make a request to a webserver. The packaged services in Carousel thus provides much of the infrastructure needed to build powerful applications without placing lots of demands on the application programmer.

Routes then, are simple a layering together of a number of services so as to enable communications and data retrieval. A route might well consist of services for communications, security, authentication and so on. The difference from a pure collection of services is that a route is intended to be connected to end-points for implementation and consumption of the service. The route provides the common functionality needed to connect the end-points. Thus one route might well be used for a number of different services.

Routes in Carousel are used to control the flow, manipulation and transformation of data. A route is made up of one or several serives nodes or ServiceProxy objects which can end up at a web server on a filesystem or wherever the developer sees fit.

Setting up a routing file

In Carousel, the routes are specified in a routing file which is referenced from the datasets.xml file.

Code Sample 26-1 - Datasets.xml file referencing a routing file

The reference to the routing file is the last entry in the datasets.xml file. In order for Carousel to process this file as a routing file the type attribute needs set to routing . The startup property XDataSourceClass needs to be set to net.xoetrope.optional.data.XOptionalDataSource in the startup properties file.

Code Sample 26-2 - The required startup property

XDataSourceClass=net.xoetrope.optional.data.XOptionalDataSource

Next, the routing file needs to be created. In this example, the route will pass information to a file on the client machine. There is a single ServiceProxy layer within the route which will take care of opening and saving the information.

Code Sample 26-3 - The new routes.xml file.

This file specifies a single route with the name FileSave which will be used when saving information to a file.

Setting up the Services file

The services.xml file needs to define services which will refer to the routes defined in the routes.xml file.

Code Sample 26-4 - The services.xml file

The services.xml file needs to be referred to from the datasets.xml file as follows...

Code Sample 26-5 - The services.xml file

The type attribute needs to be defined as service in order for Carousel to process it as such

Saving a file using the FileSave route

In this example the FileSave route will be used to save the state of the xui_state/mortapp node in the model. The following is a page which is updating some personal information about a mortgage applicant with component bindings.

Code Sample 26-6 - A page with some bindings

...

...

.

Code Sample 26-7 - Saving via the FileSave route

package net.xoetrope.mortgage;

import java.io.StringWriter;

import javax.swing.JFileChooser;

import net.xoetrope.builder.NavigationHelper;

import net.xoetrope.data.XDataSource;

import net.xoetrope.optional.service.ServiceContext;

import net.xoetrope.optional.service.ServiceProxyArgs;

import net.xoetrope.optional.service.XServiceModelNode;

import net.xoetrope.xui.XProjectManager;

import net.xoetrope.xui.data.*;

import com.xoetrope.service.file.FileSave;

public class Finish extends NavigationHelper

{

public void save()

{

String filename = getFileName();

if ( filename != null ) {

XBaseModel model = ( XBaseModel ) rootModel.get( "FileSaveService" );

XServiceModelNode node = ( XServiceModelNode ) model.get();

ServiceContext context = new ServiceContext();

ServiceProxyArgs args = context.getArgs();

args.setPassParam( FileSave.ARG_NAME_MODE, FileSave.ARG_VALUE_SAVE );

args.setPassParam( FileSave.ARG_NAME_CONTENTS, getAppContent() );

args.setPassParam( FileSave.ARG_NAME_FILENAME, filename );

Object result = node.get( context );

}

}

private String getAppContent()

{

XBaseModel mdl = ( XBaseModel )rootModel.get( "xui_state/mortapp" );

StringWriter sw = new StringWriter();

XDataSource.outputModel( sw, mdl );

return "" + sw.toString() + "";

}

private String getFileName()

{

JFileChooser chooser = new JFileChooser();

if ( chooser.showSaveDialog( this ) == JFileChooser.APPROVE_OPTION ) {

return chooser.getSelectedFile().getAbsolutePath();

}

return null;

}

}

The save function retrieves the location and name of the file which is to be saved by calling the getFileName() function. Next it retrieves the FileSaveService model node from the model after which it casts its value to a XServiceModelNode object.

Next a ServiceContext object is created. This object is used to pass and return parameters through the route. In this case the FileSave arguments need to be set. These are set by using the public static String values of the FileSave class. The contents of the file are specified by making a call to the getAppContent() function. The getAppContent() function retrieves the xui_state/mortapp node from the model and outputs it to a StringWriter using the XDataSource.outputModel function. It then wraps the content in a Datasets node so that it can be reopened.

When this code is invoked the resulting file will look something like the following...

Code Sample 26-8 - The saved content

This file contains information from bindings on other pages, but it's possible to see the paths which were specified in the page declaration earlier.

Opening a file using the FileSave route

Now that the file is saved, the developer may wish to allow it to be opened again. The same route will be used to carry out this functionality but with some different parameters..

Code Sample 26-9 - Opening a flle via the FileSave route

public void open()

{

String filename = getFileName();

if ( filename != null ) {

( ( XBaseModel )rootMdl.get( "xui_state/mortapp" ) ).clear();

( ( XBaseModel )rootMdl.get( "mortapp" ) ).clear();

XOptionalDataSource ds = new XOptionalDataSource();

XBaseModel model = ( XBaseModel ) rootModel.get( "FileSaveService" );

XServiceModelNode node = ( XServiceModelNode ) model.get();

ServiceContext context = new ServiceContext();

ServiceProxyArgs args = context.getArgs();

args.setPassParam( FileSave.ARG_NAME_MODE, FileSave.ARG_VALUE_OPEN );

args.setPassParam( FileSave.ARG_NAME_FILENAME, filename );

Object result = node.get( context );

StringReader sr = new StringReader( ( String ) args.getReturnParam( FileSave.ARG_NAME_CONTENTS ) );

XmlElement ele = XmlSource.read( sr );

if ( ele != null )

ds.loadTable( ele, rootMdl );

ds.loadTable( ele, ( XModel )rootMdl.get( "xui_state") );

updateBindings();

}

}

private String getFileName()

{

JFileChooser chooser = new JFileChooser();

if ( chooser.showOpenDialog( this ) == JFileChooser.APPROVE_OPTION ) {

return chooser.getSelectedFile().getAbsolutePath();

}

return null;

}

Again, the name of the file to be opened is specified by using the file chooser in the getFileName() function. The mortapp node will be populated so it is a good idea to first clear it using the clear() function. The same should be done with the xui_state/mortapp node. The ServiceContext is constructed again except this time the contents parameter is not set and the mode is open instead of save. When the call returns it is now possible to get the contents of the file from the return parameters of the ServiceContext object. Once retrieved, the XML is used to populate the model. The application will now bind once more to the data in the file.

Creating a custom ServiceProxy class

The FileSave ServiceProxy is a fairly useful way of saving to a file in a consistent way but does not provide the developer with anything that they could not have done quite easily themselves. In order to gain some real benefit from routing several ServiceProxy layers are usually employed to manipulate or transform the passed data. In the following example the work which was done in moving the data in and out of the model will be taken care of by a custom ServiceProxy class.

Code Sample 26-10 - The custom StateManager class

package net.xoetrope.mortgage.service;

import net.xoetrope.optional.service.ServiceContext;

import net.xoetrope.optional.service.ServiceProxy;

import net.xoetrope.optional.service.ServiceProxyException;

public class StateManager extends ServiceProxy {

/**

* The overloaded call for the ServiceProxy

* @param method the name of the service

* @param args the service arguments

*/

public Object call( String method, ServiceContext context ) {

try {

return callNextProxy( method, context, null );

} catch ( ServiceProxyException e ){

return null;

}

}

}

This class extends the ServiceProxy class and by doing so also needs to overload the abstract call method. This call receives the name of the service as its first parameter and the ServiceContext which was constructed from the calling code as its second parameter. The code above does no more than pass the call on to the next ServiceProxy in the route and return its outcome.

In order for this class to be invoked when the FileSaveService is called the routes.xml file needs to be amended.

Code Sample 26-11 - The StateManager ServiceProxy as part of the route

The StateManager class is called before the FileSave service proxy whenever the FileSaveService is called. Now the model manipulation code can be moved out of the calling classes and into this service proxy layer.

Code Sample 26-12 - The fully functional StateManager class

public class StateManager extends ServiceProxy {

public static final String ARG_NAME_MODELPATH = "modelpath";

private XModel rootMdl = XProjectManager.getModel();

/**

* The overloaded call for the ServiceProxy

* @param method the name of the service

* @param args the service arguments

*/

public Object call( String method, ServiceContext context ) {

ServiceProxyArgs args = context.getArgs();

String mode = ( String )args.getPassParam( FileSave.ARG_NAME_MODE );

String modelPath = ( String )args.getPassParam( ARG_NAME_MODELPATH );

try {

Object ret = null;

if ( mode.compareTo( FileSave.ARG_VALUE_OPEN ) == 0 ) {

// The file is being opened so the returned content needs to populate the model

( ( XBaseModel )rootMdl.get( "xui_state/" + modelPath ) ).clear();

( ( XBaseModel )rootMdl.get( modelPath ) ).clear();

ret = callNextProxy( method, context, null );

StringReader sr = new StringReader( ( String ) args.getReturnParam( FileSave.ARG_NAME_CONTENTS ) );

XmlElement ele = XmlSource.read( sr );

if ( ele != null ) {

XOptionalDataSource ds = new XOptionalDataSource();

ds.loadTable( ele, rootMdl );

}

} else if ( mode.compareTo( FileSave.ARG_VALUE_SAVE ) == 0 ) {

// The file is being saved and the contents of the target model need to be output

XBaseModel mdl = ( XBaseModel )rootModel.get( "xui_state/" + modelPath );

StringWriter sw = new StringWriter();

XDataSource.outputModel( sw, mdl );

args.setPassParam( FileSave.ARG_NAME_CONTENTS, "" + sw.toString() + "" );

ret = callNextProxy( method, context, null );

}

return ret;

} catch ( ServiceProxyException e ){

return null;

}

}

}

The StateManager class now takes care of retrieving and setting the model data. The ARG_NAME_MODELPATH member variable is defined for convenience when setting the appropriate argument in the calling code. The first thing that is done in the call method is that the context is checked for the mode of the call. The FileSave variable is used for this so as to ensure consistency.

If the mode is to open the file the target model node is cleared as well as the xui_state model node. The call is then passed over to the next proxy which is the FileSave service proxy in this case as defined in the routes.xml file. Once returned the contents return parameter is used to populate the model.

If the mode is to save the file target model is output to a StringWriter which is used to set the ARG_NAME_CONTENTS pass parameter. The next proxy is called and control returns to the calling code.

The open and save code can now be cleaned up by removing the model manipulation code

Code Sample 26-13 - The modified save code

public void save()

{

String filename = getFileName();

if ( filename != null ) {

XBaseModel model = ( XBaseModel ) XProjectManager.getModel().get( "FileSaveService" );

XServiceModelNode node = ( XServiceModelNode ) model.get();

ServiceContext context = new ServiceContext();

ServiceProxyArgs args = context.getArgs();

args.setPassParam( FileSave.ARG_NAME_MODE, FileSave.ARG_VALUE_SAVE );

args.setPassParam( FileSave.ARG_NAME_FILENAME, filename );

args.setPassParam( StateManager.ARG_NAME_MODELPATH, "mortapp" );

node.get( context );

}

}

All that is left in this code is the setting up of the XServiceProxyNode , the service context and its parameters. An extra line has been added which sets the ARG_NAME_MODELPATH parameter which is used to address the correct part of the model.

Code Sample 26-14 - The modified open code

public void open()

{

String filename = getFileName();

if ( filename != null ) {

XBaseModel model = ( XBaseModel ) XProjectManager.getModel().get( "FileSaveService" );

XServiceModelNode node = ( XServiceModelNode ) model.get();

ServiceContext context = new ServiceContext();

ServiceProxyArgs args = context.getArgs();

args.setPassParam( FileSave.ARG_NAME_MODE, FileSave.ARG_VALUE_OPEN );

args.setPassParam( FileSave.ARG_NAME_FILENAME, filename );

args.setPassParam( StateManager.ARG_NAME_MODELPATH, "mortapp" );

node.get( context );

updateBindings();

}

}

Again, the code is much simplified. The code sets up the XServiceModelNode , creates the service context and its parameters. The updateBindings method of the XPage is called in case any components on the page are bound to the affected part of the model.

Building routes visually with Carousel

The Carousel plugin for KalIDEoscope allows the developer to create the routing and services files automatically as well as providing the ability to build up the routing layers visually. When the Carousel plugin has been loaded the files tab on the project editor will show the new settings as shown below.

These are the files for the services and routes as referred to earlier. There are other files referenced here which could do with some explanation.

Table 26-1 - Carousel project property files

Routes

This is the routing file which will be used by the client application. It will be created automatically by Carousel in the resources directory.

Services

This is the services file which will be used by the client application. It will be created automatically by Carousel in the resources directory.

ServerRoutes

This is the routing file which will be used by Carousel on the server side. It will be created automatically by Carousel in the resources/server directory.

ServerServices

This is the services file which will be used by Carousel on the server side. It will be created automatically by Carousel in the resources/server directory.

ServerDatasets

This is similar to the client side datasets file. It will be used on the server by Carousel to reference the routing and services files. This file will be created in the resources/server directory.

ServerStartup

Just like the client, the server-side Carousel component requires this startup file in order to identify the ModelData property. This file will be created in the resources/server directory.

CustomRouteDefs

As custom ServiceProxy classes are added to a project they can be referenced in this configuration file so that they can be added to routes via the routes menu. This file can also be moved between projects to build up a library of custom ServiceProxy classes. This file will be created in the project root directory.

In order to access the route editor within KalIDEoscope, click the Carousel | Services | Route Manager menu as shown below.

The routes editor will appear as in the screenshot below.

To create a new route on the client side, right click the Client node in the Routes tree and click New Route from the popup menu. A route called New Route will appear under the Client node. The new route can be renamed by selecting it and pressing F2 or by clicking the Rename Route icon in the Route Manager toolbar.

To add say, the FileSave ServiceProxy to the new route right-click the route in the Routes tree and from the popup menu select New ServiceProxy Layer . This menu will provide a list of registered ServiceProxy objects. Select com.xoetrope.service.file.FileSave from the submenu and it will appear under the route node.

Now the Route Manager can be used to create the custom StateManager ServiceProxy class as described earlier in this chapter. Click the Create a custom ServiceProxy layer button on the Route Manager toobar and the new custom ServiceProxy dialog will appear as below.

The default package for the project's XPage classes will have the services package appended to it and will appear as the class name so the StateManager class name needs to be added to it. The new ServiceProxy class can extend on of the existing ServiceProxy classes which are listed in the Base Class dropdown. Select the Create the class checkbox and click OK and the new class will be created automatically and opened for editing in the netBeans editor.

Now the new StateManager route can be added to the route by right clicking the route and clicking New ServiceProxy Layer in the popup menu as shown below.

The service for this route now needs to be defined and this is done through the Service Manager which can be accessed by clicking the Carousel | Services | Service Manager menu. The Service Manager will appear as below.

Right-click on the Client node in the Services tree and click New service on the popup menu. A new node with the text new service will appear below the Client node which can be renamed by pressing F2 or by clicking the Rename the selected service button on the toolbar.



Save the project and the routes and services files will be saved as previously without the need to hand-code them. It is worth looking at the newly created customroutedefs.xml file in the project root folder as mentioned previously.

Code Sample 26-15 - The customroutedefs.xml file

This file is used by Carousel to identify custom ServiceProxy classes which are registered for the project so that they can appear in the ServiceProxy list in the popup menu. This file can easily be transferred between projects along with the custom ServiceProxy classes.

Summary

Moving data manipulation code out of project code and into service proxy layers cleans up the project code and creates a consistent way of persisting and retrieving data. These layers can be built into a set of libraries depending on the requirements of the projects being developed. It is then much easier to replace functionality and to add other layers. A simple encryption layer could, for example, be added between the StateManager and FileSave service proxy classes to encrypt data as it is passed back and forth.

By using the editor to build up routes and services and to create custom ServiceProxy classes much of the manual work of setting up the route definitions is automated for you.

comments


If you were logged in you could rate this article or add a comment.