Michael Yuan

“Science is a wonderful thing if one does not have to earn one’s living at it” — Albert Einstein

July 31st, 2007

Seam and SOA

I am in Atlanta for our internal JBoss ESB / SOA training this week. Burr Sutter and his team are doing a great job conveying our SOA vision -- to mesh and reuse services you already built in your "single silo" applications (e.g., a web store and a warehouse management app can share an "order" object when the user confirms an order. Each application then process and save the order in their own ways.).

I have been thinking how Seam fits into this picture. With the support for web services in Seam 2.0, it is actually quite easy to fit a Seam application into a SOA environment. In this post, I'd like to discuss some work I did with the seambay example (a spoof auction site) to make it share its listings with other services, and receive listings from other services.

@Out

To make the seambay application share its auction listings with other services in the SOA environment, we need to serialize the Auction object and push it to the ESB, while it is being saved into the database by the Seam application. The least intrusive way to do this is to use an EJB3 interceptor to "steal" the Auction object when the confirm() method is called. This way, we do not need to make any change to the Seam application code.

JAVA:
  1. public class AuctionInterceptor {
  2.  
  3.   @AroundInvoke
  4.   public Object sendAuctionToESB (InvocationContext ctx) throws Exception {
  5.  
  6.     System.out.println("*** Entering AuctionInterceptor");
  7.  
  8.     Object target = ctx.getTarget ();
  9.     if (target instanceof AuctionAction) {
  10.       if (ctx.getMethod().getName().equals("confirm")) {
  11.         System.out.println("We will send the following Auction object to ESB");
  12.         Auction auction = ((AuctionAction) target).getAuction ();
  13.         // Send the following XML string to JMS
  14.         formatMessage(auction);
  15.       }
  16.     }
  17.  
  18.     try {
  19.       return ctx.proceed();
  20.     } finally {
  21.        System.out.println("*** Exiting AuctionInterceptor");
  22.     }
  23.   }
  24.   ... ...
  25. }

When the user enters a new auction listing on the site, the server console prints the following message.

Picture 18.png

@In

To make the Seam application as a SOA service end-point requires external applications to invoke its business logic through web services. The challenge here is that most business logic in Seam are managed in "conversations", which holds finer grained application state than Http sessions. How do you keep the conversation state across multiple SOAP calls? Well, here is where the Seam Web Service comes into play.

To expose business logic methods in Seam components as Web Service methods, you need to write a facade SLSB to wrap around the methods you want to expose. You can use the Seam.getComponent() method to get any Seam component by its name and then proceed to invoke any method on the component.

JAVA:
  1. @Stateless
  2. @WebService(name = "AuctionService", serviceName = "AuctionService")
  3. public class AuctionService implements AuctionServiceRemote
  4. {
  5.    @WebMethod
  6.    public boolean login(String username, String password)
  7.    {
  8.       Identity.instance().setUsername(username);
  9.       Identity.instance().setPassword(password);
  10.       Identity.instance().login();
  11.       return Identity.instance().isLoggedIn();
  12.    }
  13.  
  14.    @WebMethod
  15.    public void createAuction(String title, String description, int categoryId)
  16.    {
  17.       AuctionActionInt action = getAuctionAction();
  18.       action.createAuction();
  19.       action.setDetails(title, description, categoryId);
  20.    }
  21.  
  22.    @WebMethod
  23.    public Auction getNewAuctionDetails()
  24.    {
  25.       return getAuctionAction().getAuction();
  26.    }
  27.  
  28.     @WebMethod
  29.    public void setAuctionPrice(double price)
  30.    {
  31.       getAuctionAction().getAuction().setPrice(price);
  32.    }
  33.  
  34.    @WebMethod
  35.    public void confirmAuction()
  36.    {
  37.       getAuctionAction().confirm();
  38.    }
  39.  
  40.    private AuctionActionInt getAuctionAction()
  41.    {
  42.       // Get seam component named "auctionAction" from context
  43.       return (AuctionActionInt) Component.getInstance("auctionAction", true);
  44.    }
  45.    ... ...
  46. }

Notice that the createAuction(), setAuctionDuration(), setAuctionPrice() and confirmAuction() methods all need to happen in the same conversation so that they can access the same "auctionAction" component. But we do not explicitly pass the conversation id to the web service calls. Instead, when the createAuction() method returns, it inserts a conversation id in the SOAP header, and expect the client to use the same conversation id in the header until the caller reaches confirmAuction(), which ends the conversation. The SOAP request inside a conversation looks like the following:

XML:
  1. <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
  2.                            xmlns:seam="http://seambay.example.seam.jboss.org/">
  3.    <soapenv:Header>
  4.       <seam:conversationId>2</seam:conversationId>
  5.    </soapenv:Header>
  6.    <soapenv:Body>
  7.       <seam:setAuctionPrice>
  8.          <arg0>20</arg0>
  9.       </seam:setAuctionPrice>
  10.    </soapenv:Body>
  11. </soapenv:Envelope>

As a proof of concept to demonstrate that Seam web service is well supported by existing SOAP tools, I build a SoapUI script to demonstrate how to add an auction to the Seam app via web services. Notice the "transfer" elements in the script. They are used to maintain the conversation id header across SOAP requests in a conversation.

Picture 17.png

Finally, I'd like to make the SOA version of the seambay example available for download. You can find the ready built EAR file in the dist directory, as well as the SoapUI script in the root directory.

July 30th, 2007

JBoss Seam: A “Deep Integration” Framework

TheServerSide is running my article this week:

JBossSeamFramework.gif

I like the artwork visualizing "Seam" in an origami bird. Elegant, balanced, and lightweight. Perfect for Seam. The article is based on presentations I recently did on Seam. It targets experienced Java EE developers (i.e., the JUG, NFJS crowd), and goes into depth on some of ideas behind Seam (instead of just a CRUD demo). I hope you enjoy it and let me know your comments. Thanks. Here are some slides from my presentations that are relevant to the article.

Picture 8.png

Picture 9.png

Picture 10.png

Picture 11.png

Picture 12.png

July 24th, 2007

Seam 2.0 and Tomcat

Darryl Smith pointed out that we can actually use non-JTA transactions with JPA in Tomcat now. So, I just added tomcat 5.5 and 6.0 build targets for the jpa example in CVS based on his suggestions.

In JBoss Seam 2.0, we made some changes to the way Tomcat is supported as a Seam runtime. It has caused some confusion. So, let me try to clarify here ...

As you recall, in Seam 1.0 to 1.2, we can run Seam applications on Tomcat by bundling the JBoss Embeddable EJB3 library or the JBoss micro-container inside the WAR application. The application will need to bootstrap the EJB3 container or micro-container upon startup. However, the EJB3 library Jars is more than 20 MB. The idea of bootstraping the entire thing for each application is not very efficient. In Seam 2, we leverage the "Embeddable JBoss" library to provide shared EJB3 service to all WAR applications deployed in the Tomcat instance. You have two ways to run Seam 2 applications in Tomcat:

1. If you need to use EJB3 beans, you will need to install the "Embeddable JBoss" libraries as shared library in Tomcat. It is an easy process. You can find instructions for Tomcat 5.5 and 6.0 respectively. Once you do that, you can deploy Seam applications without any restrictions. You will be able to use full EJB3 features including session beans, entity beans, message driven beans, and web services etc.

Of course, you still cannot deploy EAR files on Tomcat. But you can bundle EJB3 JARs in the WEB-INF/lib directory of your WAR application -- essentially re-creating the EAR structure in a WAR. Just run the tomcat.war ANT target on any of the official Seam examples to see how this is done.

2. If you absolutely cannot install any shared library or shared configuration on Tomcat, you can still run Seam applications. However, you will not be able to use EJB3 features -- you must use Seam POJOs instead. You can, however, use JPA in local transaction mode. Seam manages the transaction manager and entity manager for you in this case.

Checkout the jpa and hibernate2 examples in Seam distribution and build for the tomcat55 or tomcat6 targets to see how this works. The jpa shows how to use JPA EntityManager with Seam POJOs and the hibernate2 example shows how to use Hibernate sessions with Seam POJOs. You will need to copy hsqldb.jar into Tomcat's shared lib directory since the example app assumed that we will use a HSQL DB from JNDI (see META-INF/context.xml). Obviously, you do not have to do that in your own apps.

Okay, so much for Tomcat support. What about Seam 2 support for J2EE 1.4 application servers and non-JBoss Java EE 5 application servers? Here are the status:

  • J2EE 1.4 application servers: Try the jpa example in Seam distribution. It uses Seam POJOs to replace EJB3 session beans in other examples. The jpa example builds for JBoss 4.0.5/4.2.x, WebSphere 6.1.0.9, and WebLogic 9.2.
  • Java EE 5 application servers: Try the jee5 example in Seam distribution. It builds for JBoss AS 5.x, Glassfish V1/V2, and Oracle OC4J 11. We are currently working on WebLogic 10, which has some weird reflection issues.

Hope that helps!