Wednesday, 27 July 2011

Separating Webservice Classes from EJBs

In our JavaEE Video Training Course, we write both a RESTful and a traditional SOAP webservice.

One of the seductive features of JavaEE is that for many situations, it is a trivial job to convert an existing EJB in to a SOAP Webservice with a single annotation. On the JavaEE course, we end up with a class that looks something like this:

@Stateless
@WebService(name="employeeManagementWebService")
public class EmployeeManagementImplementation 
    implements EmployeeManagementServiceRemote, EmployeeManagementServiceLocal {

 @EJB
 private EmployeeDataAccess dao;

 @EJB
 private ExternalPayrollSystem payrollSystem;

 public void registerEmployee(Employee newEmployee) throws SystemUnavailableException 
 {
    dao.insert(newEmployee);
    payrollSystem.enrollEmployee(newEmployee);
 }

 // further code omitted

Line 2 is the line of interest, and with this single line, on deployment you get a WSDL and a Webservice Endpoint for free.

On the course, I do take care to explain that real life situations may not be as simple as this: you may have requirements to change the interface to your clients (perhaps the operation names in the WSDL are different to the method names in the Java), or you may want to handle some complex XML mappings and so on, but the point remains: adding a SOAP layer to an EJB is wonderfully easy, at least at first.

However, a few users have reported that adding the @WebService annotation to an EJB isn't working in Glassfish 3.1. (We supply Glassfish 3.0 as part of the course downloads, but many of our users prefer to use the latest version, which is fine).

This is definitely a bug in Glassfish, but there is a workaround. The workaround sounds like a real pain. But actually it turns out to be a much better way of implementing a webservice, and I would recommend you follow this approach anyway.

The Workaround: Separate the Webservice Class from the EJB Class.


That's right: we're going to turn our back on the seductive feature and go for a longhand - but ultimately better - way.

Step 1: Write a Webservice Class.


This is a brand new class. It is annotated with the @WebService interface. It isn't an EJB as it doesn't have a @Stateless annotation. But each method in the class delegates to an EJB, in this case the EmployeeManagementService.

@WebService(name="employeeManagementWebService")
public class EmployeeManagementWebService implements EmployeeManagementServiceRemote
{
 @EJB
 private EmployeeManagementServiceLocal employeeManagementService;

 public void registerEmployee(Employee newEmployee) throws SystemUnavailableException 
 {
  employeeManagementService.registerEmployee(newEmployee);
 }
  
 // other methods would follow here
}

(Note that I've implemented the Remote interface on my webservice: in fact you don't have to do this, implementing an interface is optional on a JavaEE webservice).

The line of most interest here is line 5, where we are injecting the EmployeeManagementService EJB as a dependency. Notice that I'm using the Local interface - as both the Webservice class and the EJB class are deployed to the same server, I need to ensure that I don't get the performance hit of making remote calls. (This technique is described in detail on the full JavaEE course).

Step 2: Restore the EJB


And now the EJB can have anything related to WebServices removed. I won't show the code here, it will be very similar to our original EJB, just with the @WebService annotation removed.

At first, this may seem ridiculous. It is much more work, you need a whole second class (with a lot of apparent repetition), and yet the end result is the same.

What have we gained?


Put simply, it is better engineering. We have two completely separate concerns here:

1) The interface that we want to expose to our Remote clients
2) The implementation of the management of the business logic

And both of these will need to vary independently. For example, it is more than possible that over time, the webservice interface needs to change. Remember that SOAP webservices are closely related to XML, and there are lot of XML concerns that need to be addressed by our webservice class (a random example - namespace changes).

Although on the training course, our Webservice class is very simple, as your requirements get more serious you are likely to need more and more Webservice related annotations on your class. Without the separation, this would eventually begin to obscure the really important employee management code inside the class.

So, although it is seductive to just drop a "@WebService" annotation on any EJB, think carefully before doing so. Separating the two concerns is easy enough to do, and could save you a lot of pain in the future.

Usually on our courses, we aim to show the professional way of doing things rather than the simple way. This was one area where I really should have thought more carefully.

(Ps - in the REST chapter, I do separate the REST class from the EJB class, but it would be very difficult to combine those two classes anyway, due to the nature of REST).