Using the Java Persistence API 2.0 services with WebSphere Process Server V7, Part 4: Creating an SCA client

Part 4 of this 6-part series shows how to use the enterprise integration service wizard to introspect the EJB definition and to automatically generate the service data objects and SCA definitions to integrate the SCA and JEE programming technologies. Along the way, you will investigate the recursive nature of JPA relationships, and how this can impact SCA clients.

Marvin W. Malcolm (marvin.malcolm@au1.ibm.com), Consulting IT Specialist, IBM

Photo of Marvin W. MalcolmMarvin Malcolm started his IT career in 1989 developing airline reservation systems in IBM 370 assembler. Three years later, he was integrating the airline reservation system with the various global distribution systems and has been integrating systems ever since. Joining IBM in 2004, he has worked as a WebSphere Business Process Management and Integration consultant, helping clients get maximum value from the IBM WebSphere product stack. He has worked with WebSphere Process Server since it was launched in 2005 and now, as part of the Australia Lab Services Development team, he provides training, enablement, and first of a kind implementations.



08 December 2010

Introduction

This is the fourth in our series of six tutorials describing the creation of a Java Persistence API V2.0 (JPA) service that will be consumed by WebSphere Process Server V7 (hereafter called Process Server). The JPA capability is provided via the OSGi and Java Persistence API V2.0 Feature Pack for WebSphere Application Server.

The series uses a simple order processing use case and builds the solution using the different aspects of WebSphere Integration Developer V7.

This tutorial assumes a basic knowledge of the WebSphere Integration Developer environment.

Quick recap

The previous tutorials in this series have shown how to:

  • Define the physical data model.
  • Generate the JPA entities from the data model.
  • Create an EJB v3.0 stateless session bean to wrap the JPA data access entities.

In this tutorial, you will create an SCA client that consumes the EJB service and exposes it as a service to other applications.

Objectives

In this tutorial, you will:

The tutorial series consists of:

A project interchange file, tutorial_4_PI.zip, contains a completed implementation of this tutorial that you can download.

Prerequisites

You need to have a basic understanding of Java, JEE, and WebSphere Process Server SCA programming concepts, ideally having performed some development using WebSphere Integration Developer.

This tutorial also assumes that you have completed the previous tutorials in this series:

System requirements

To follow the examples in this tutorial, you will need:

  • WebSphere Integration Developer V7.0.0.2 or later
  • WebSphere Process Server V7.0.0.2 or later
  • WebSphere Application Server V7.0.0.9 or later
  • Your WebSphere Process Server environment augmented with the WebSphere Application Server Feature Pack for OSGi Applications and Java Persistence API 2.0.

Note that the OSGi and JPA 2.0 Feature Pack requires WebSphere Application Server V7.0.0.9 or later.

Duration

This tutorial takes approximately one hour to complete.


Creating an SCA EJB client

  1. Switch to the Business Integration perspective.
  2. Create a new module called OrderSCAClient.
  3. Configure the required project dependencies. Open the dependencies for the OrderSCAClient. Click Add under the Java section and select the OrderJPA project (Figure 1).
    Figure 1. Updating the OrderSCAClient dependencies
    Updating the OrderSCAClient dependencies
  4. Save and close the OrderSCAClient Dependencies view.
  5. Expand the OrderEJBClient project and drag the OrderServiceRemote.java interface onto the OrderSCAClient assembly diagram (Figure 2). This is the easy way to create an EJB import. You can also create it by Enterprise Discovery.
    Figure 2. Creating an EJB Import from a Java interface
    Creating an EJB Import from a Java interface
  6. In the resulting dialog, select Import with Enterprise JavaBeans Binding and click OK.
  7. On the “Create an EJB Import” dialog (Figure 3), ensure the OrderServiceRemote interface is selected and click Finish.
    Figure 3. Selecting the EJB interface
    Selecting the EJB interface
  8. This puts an Import on the assembly diagram with an EJB binding, and also creates a number of XML schema and WSDL definitions, which define the interface (Figure 4).
    Figure 4. Generated WSDL and XSD artefacts for the EJB Import
    Generated WSDL and XSD artefacts for the EJB Import

Note that these schema and WSDL artifacts are added into the module rather than a Process Server library project. This does not actually present a problem as the interface is only accessed via this module. It also adds the OrderEJBClient project as a deployable dependency on the OrderSCAClient module.


Creating a service interface

Now that you have an interface to your data, you need to expose this as a service to other applications to consume. In a real SOA environment, it is unlikely that you expose the details of your backend data service to clients. So, in the same way, you will also create a new similar interface to illustrate the process.

  1. Create a Process Server Library project called OrderLib.
  2. Add the following data objects shown in Tables 1 to 4.
Table 1. Order data object
Data Object: Order
Field Type Description
id int This is the unique identifier for the order. It is automatically generated by your JPA entity.
customer Customer This data object contains the details of a customer.
items OrderItem [ ] This is an array of order items, describing the contents of the order.
Table 2. OrderItem data object
Data Object: OrderItem
Field Type Description
itemCode String This is the product identifier for this particular item. In the real world, it is likely that this is a unique value, but for our simple example, this does not need to be the case.
description String This is a text string describing the item.
quantity int This is the number of items required.
price Decimal This is the per item cost.
Table 3. Customer data object
Data Object: Customer
Field Type Description
customerId int This is the unique identifier for the customer. It is automatically generated by your JPA entity.
firstName String This is the first name of the customer.
lastName String This is the last name of the customer.
creditRating int This is an integer value for the credit rating of your customer.
address Address This is a data object containing the customers address.
Table 4. Address data object
Data Object: Address
Field Type Description
line1 String Address field line 1
line2 String Address field line2
suburb String Suburb
state String State
postcode String Postal code
country String Country

The resulting data objects look similar to the data structures shown in Figure 5.

Figure 5. Service interface data objects
Service interface data objects

Creating the interfaces

The three retrieve methods you added to the EJB interface were:

  • Retrieve a specific order.
  • Retrieve the total outstanding debt for a particular customer.
  • Retrieve the customers' order history.

You will add corresponding service interfaces on the SCA client mediation so that consumer applications can invoke these EJB interfaces (Table 5).

This allows you to provide a mediation layer in between your clients and the EJB interface. You can view the EJB service as a just another back-end system that you need to isolate from your client applications, as you would in any integration project.

Table 5. Interface operation definition
Operation Request ObjectResponse Object
retrieveOrder OrderIdRequest Order
getTotalOutstandingDebt CustomerIdRequest TotalDebtResponse
getOrderHistory CustomerIdRequest OrderInfoListResponse

You will notice that even though the request objects typically require just an identifier, which are of type “int”, you will wrap these in complex types to make them more consumable by Process Server components and the JSON REST clients.

Request types

Create two data objects, OrderIdRequest and CustomerIdRequest, both with a single string representation of the respective identifier (Figure 6). These will be supplied by the UI applications as JSON objects and the Process Server mediations will automatically convert them to the required business object types.

Figure 6. OrderId and Customer Id request data objects
OrderId and Customer Id request data objects

Response types

  1. The Order object for the retrieveOrder operation has already been defined. For the getTotalOutstandingDebt operation, create a data object called TotalDebtResponse, which contains a single field totalDebt of type Decimal (Figure 7).
    Figure 7. TotalDebtResponse data object
    TotalDebtResponse data object
  2. For the getOrderHistory operation, create two data objects called OrderInfo and OrderInfoListResponse (Figure 8).
    Figure 8. OrderInfoListResponse data object
    OrderInfoListResponse data object
  3. The final set of data objects required will be used to return a list of order items. Create two data objects called OrderItem and OrderItemListResponse, respectively (Figure 9).
    Figure 9. OrderItemListResponse data object
    OrderItemListResponse data object
  4. Add an interface called iOrder to the library with five operations as shown in Figure 10:
    • createOrder
    • retrieveOrder
    • getOrderHistory
    • getTotalOutstandingDebt
    • getOrderItems
    Figure 10. Service interface definition
    Service interface definition
  5. Add this library (OrderLib) as a dependency on the OrderSCAClient project (Figure 11).
    Figure 11. OrderSCAClient dependencies
    OrderSCAClient dependencies

Creating the mediation flow

Now that you have defined your external interface, you need to mediate between this and your EJB interface. You will use an ESB mediation module to perform this task.

  1. Open the OrderSCAClient assembly diagram and add a mediation component called OrderMediation.
  2. Add an interface to the mediation component and select the iOrder interface (Figure 12).
    Figure 12. Adding the external interface to the mediation
    Adding the external interface to the mediation
  3. Add another reference to the mediation component and select the OrderServiceRemote interface (Figure 13).
    Figure 13. Adding a reference to the EJB Service
    Adding a reference to the EJB Service
  4. Wire up the mediation module to the EJBImport as shown in Figure 14.
    Figure 14. Wiring the mediation to the EJB import
    Wiring the mediation to the EJB import
  5. Double-click on the OrderMediation mediation component and click Yes to implement the component.
  6. Click on the createOrder operation and select a Blank Mediation Flow template as shown in Figure 15. This provides an empty mediation flow that you can use to create a simple mapping of the service interface to the EJB interface.
    Figure 15. Implementing the createOrder mediation flow
    Implementing the createOrder mediation flow
  7. On the request flow, add a Callout from the palette to the mediation and specify the createOrder operation of the OrderServiceRemotePartner reference (Figure 16).
    Figure 16. Adding a callout to the EJB interface
    Adding a callout to the EJB interface
  8. Wire the input to the OrderServiceRemorePartner. When prompted, select to use the XSL transform primitive to perform the transformation. This results in a flow shown in Figure 17.
    Figure 17. createOrder request mediation flow
    createOrder request mediation flow
  9. Rename the default XSL Transformation1 mediation flow component to Create Order and accept the default options to create an XML map. Renaming the XSLT transformation is not required, but it makes it easier to identify the transformation in the Enterprise Explorer view and in a trace file.
    Figure 18. Mapping the request message
    Mapping the request message

    The map, as shown in Figure 18, is fairly self-explanatory, moving similar entities from one request to another. Points of interest are:

    • receivedDate, which is set to the current date and time.
    • status, which is set to the text “Received”.
    • OrderItems in the service request are mapped, using a “For each” construct to the orderItemCollection on the EJB request. This uses the mapping shown in Figure 19.
    Figure 19. Mapping the order item array with a local For each construct
    Mapping the order item array with a local For each construct

    For more details of the mapping, review the map from the project interchange file that you can download from the Download section.

  10. Follow a similar procedure to create the response flow, making sure to wire up a fail terminal to the Callout Response fail terminal (Figure 20). In a real application, you will have a more appropriate error handling strategy. For the purpose of this tutorial, the fail terminal will be sufficient.
    Figure 20. Mediation response flow for the createOrder operation
    Mediation response flow for the createOrder operation
  11. The transformation for the response flow is self-explanatory mapping, like fields from the EJB response to the service response. Figure 21 and Figure 22 show the mapping definition. Once again, these are available in the project interchange file that goes along with this tutorial.
    Figure 21. Mapping the EJB response to the CreateOrder request
    Mapping the EJB response to the CreateOrder request
    Figure 22. Mapping the order item response
    Mapping the order item response
  12. We will follow the same process to create the mediation flow for the retrieveOrder operation.

    The XSL transformation is trivial as there is only a single parameter to map on each interface. Note how the map converts the orderId from a string to an "int" (Figure 23).

    Figure 23. Mapping the request flow for the retrieveOrder operation
    Mapping the request flow for the retrieveOrder operation
  13. Wire up the response flow using an XSL transformation to connect the Callout Response to the retrieveOrder Input Response.
  14. Also wire the Callout Response fail terminal to a Fail mediation flow component. This is the same as the response flow of the createOrder operation. See Figure 21 and Figure 22.

Returning the JSON friendly responses

  1. Two of the operations, getOrderHistory and getOrderItems, return responses that are going to be used to populate a dojox.grid.DataGrid structure. DataGrids use a data store to supply its data. In our example, we will use a dojo.data.ItemFileReadStore data store. If we return the data in a standard JSON format that can be used to directly populate a Dojo ItemFileReadStore, this reduces the amount of work we have to do in the UI.

    The returned data needs to provide three fields:

    • identifier
    • label
    • list of items
  2. The identifier is an optional field in the response and is used to specify the field in the items structure that uniquely identifies each item. In the example in Figure 24, it is the itemCode.

    The label field is optional and provides name of a field in the item list that contains a human readable field describing the item. The order item description is an ideal field for this.

    The list of items contains an array of objects that are used to populate the data store.

    Figure 24. Creating a feed for a JSON data store
    Creating a feed for a JSON data store
  3. Complete the implementation of the iOrder interface, mapping each operation to the EJB interface operations as shown in Table 6.
Table 6. Complete operation mapping definition
iOrder operation EJB interface operation
createOrder oreateOrder
retrieveOrder findOrder
getOrderHistory getCustomerOrderHistory
getTotalOutstandingDebt getTotalOutstandingDebt
getOrderItems findOrder
  1. This completed mediation is shown in Figure 25.
    Figure 25. Mapped iOrder interface
    Mapped iOrder interface

    Note that we have not described all the XSLT mappings. Although they are simple, they are numerous and only serve to clutter the tutorial. You can find them in the attached project interchange file, which you can import into your workspace.

  2. Save all files and ensure the workspace builds successfully.

Deploying and testing the SCA client

  1. Open the Servers view and remove all projects from the server.
  2. Re-deploy the two modules to the server:
    • OrderJPAEAR
    • OrderSCAClientApp
  3. Right-click on the EJBImport in the OrderSCAClient assembly diagram and select TestComponent.
  4. Enter some data and run the test. As shown in Figure 26, you will need to create array items for the order item collection by right-clicking the orderitemCollection and selecting the Add Elements option.
    Figure 26. Add array elements in the Integration Test Client
    Add array elements in the Integration Test Client

    Figure 27 shows a completed test run.

    Figure 27. Testing the EJB import in the Integration Test Client
    Testing the EJB import in the Integration Test Client

When it is run, you get a ServiceRuntimeException. It is not quite what you expected. You may see the Exception message as either:

java.lang.InternalError: java.lang.reflect.InvocationTargetException
Refer to the server logs for more information.

or:

java.lang.StackOverflowError
Refer to the server logs for more information.

Either way, looking at the logs, it is easy to see that there is an infinite loop with the sequence of calls in the stack trace in the SDOToJavaConvertor method.

...
at com.ibm.wbiserver.datahandler.j2w.util.SDOToJavaConvertor.
 getClasses(SDOToJavaConvertor.java:242)
at com.ibm.wbiserver.datahandler.j2w.util.SDOToJavaConvertor.
 getClasses(SDOToJavaConvertor.java:307)
at com.ibm.wbiserver.datahandler.j2w.util.SDOToJavaConvertor.
 getClasses(SDOToJavaConvertor.java:307)
...

What's happening here is that the JPA Java classes that were created from the data model have a ManyToOne and OneToMany relationships that are recursive in nature.

From a business perspective, what we envisioned is the following structure:

Orderinfo
     |----> Customer
          |----> Address
     |----> OrderItems[ ]

The relationship between Orderinfo and Customer is ManyToOne as one customer can have many orders, but each order only has one customer. Also, the relationship between Orderitems and Orderinfo is ManyToOne as an order can have many order items, but each order item can belong to one order.

The data model that supports this defines this structure as follows:

Orderinfo
     |----> Customer
          |----> Address
          |----> Orderinfo[ ]
     |----> OrderItems[ ]
          |----> Orderinfo[ ]

If you look in the Orderinfo entity definition, the Customer reference is defined as follows:

@ManyToOne
@JoinColumn(name="CUSTOMER")
private Customer customer;

In the Customer entity definition, there is a collection of Orderinfo objects as follows:

@OneToMany(mappedBy="customer")
private Set<Orderinfo> orderinfoCollection;

You can see that in Customer.java, there is a collection of Orderinfo objects, each of which has a Customer attached. This in turn will have a collection of Orderinfo and so on.


Removing recursive structures for the SCA Import EJB binding

  1. To fix this, you have to go back to your EJB session bean and add some private methods to copy the returned objects, removing the relationship references, and hence, flattening the returned structure that is converted to SDOs.

    The resulting structure matches the following:

    Orderinfo
         |----> Customer
           |----> Address
           |----> Orderinfo[ ] (only a shallow copy, which removes the Customer 
                                 and Orderitem references)
         |----> OrderItems[ ]  (only a shallow copy, which removes the Orderinfo 
                                references)

    This ensures that there is no recursive navigation of the JPA classes to generate the SDOs in the EJB binding.

    Listing 1. Creating shallow copies of the Orderinfo JPA class
    1   private Orderinfo copyOrderinfo(Orderinfo oi) {
    2	Orderinfo order = shallowCopyOrderinfo(oi);
    3	order.setCustomer(copyCustomer(oi.getCustomer()));
    		
    4	Set s = new HashSet();
    5	Set itemSet = oi.getOrderitemCollection();
    6	if (itemSet != null) {
    7		for (Iterator<Orderitem> it = itemSet.iterator(); it.hasNext();) {
    8			s.add(shallowCopyOrderitem(it.next()));
    9		}
    10		order.setOrderitemCollection(s);
    11	}
    	
    12	return order;
    13  }
  2. As shown in Listing 1, on Lines 2 and 8, two methods have been added to make shallow copies of the Orderinfo and Orderitem JPA classes. The code of the shallowCopyOrderinfo and shallowCopyOrderitem is simple, basically copying only the properties that you need and omitting any objects that may have a recursive element in them.

    The shallowCopyOrderinfo omits the Customer and OrderitemCollection properties and the shallowCopyOrderitem omits the Orderinfo property.

  3. A full listing of the methods is found in the OrderService.java source file in the provided project interchange file.
  4. Once this is done, add the following changes on Lines 2, 20, and 22 from Listing 2 to the createOrder method.
    Listing 2. Modified EJB service method createOrder
    1  public Orderinfo createOrder(Orderinfo order) {
    2	Orderinfo orderInfo = null;
    3	if (order != null) {
    4		Customer c = em.find(Customer.class, order.getCustomer().getId());
    5		if (c == null) {
    6			em.persist(order.getCustomer());
    7			em.persist(order.getCustomer().getAddress());
    8		}
    9
    10		em.persist(order);
    11
    12		Set s = order.getOrderitemCollection();
    13		if (s != null) {
    14		     for (Iterator<Orderitem> it = s.iterator(); it.hasNext();) {
    15				Orderitem ordI = it.next();
    16				ordI.setOrderinfo(order);
    17				em.persist(ordI);
    18		     }
    19		}
    20		orderInfo = copyOrderinfo(order);
    21	}
    22	return orderInfo;
    23  }
  5. You will also modify the findOrder method to perform the same shallow copy of the Orderinfo response (Listing 3).
    Listing 3. Updated findOrder EJB method
    1	public Orderinfo findOrder(int orderId) {
    2		Orderinfo oi = em.find(Orderinfo.class, orderId);
    3		Orderinfo order = null;
    4		if (oi != null) {
    5			order = copyOrderinfo(oi);
    6		}
    7		return order;
    8	}
  6. Save the modifications, rebuild, and re-publish the server to update OrderJPAEAR.
  7. If you re-run the test now, it completes successfully.
  8. Also, note that each time it is run, even with the same requesting data, the response shows the primary keys of each of the data types are being updated automatically by the sequences you created. See the underlined ID fields in Figure 28. An important fact to note, and you will make use of this later when creating your BPM process, is that the primary keys are provided back to you before the transaction has been committed to the database.

    This also means that if there is an error and the transaction is rolled back, that returned ID is discarded. You will also probably notice that during your testing, you will see gaps in between these ID values, both when you have errors and when you have long gaps between test runs.

    Figure 28. Successful order creation tests
    Successful order creation tests

Conclusion

In this tutorial, you created an SCA client to the EJB V3.0 service, which wraps your JPA data access classes. This included a service interface and mediation flow that decoupled the client from the EJB service. You needed to modify the service slightly, removing any recursive elements to ensure that the service mapping between the JPA classes and SDOs can complete successfully.

In the next tutorial, Part 5, you will create and start a simple business process each time an order request is received. This will be the basis for the final tutorial, Part 6, where you expose the service interfaces you have just created to the user interface as REST services.


Download

DescriptionNameSize
Code sampletutorial_4_PI.zip86KB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=594279
ArticleTitle=Using the Java Persistence API 2.0 services with WebSphere Process Server V7, Part 4: Creating an SCA client
publish-date=12082010