Sunday, November 15, 2009

EJB and Web Services: getting the best of both worlds

If you have ever worked in a project where both EJBs and Web Services are used, it is very likely that you have gotten into discussions about whether a given component should be implemented as an EJB or a Web Service. You might also wonder what is the best way to bridge between these two technologies. For example, you might have been in a situation where you wanted to reuse an existing EJB in a BPEL process. In this post I will demonstrate how you can avoid these questions by making your service implementations independent of the protocol used to invoke them. While this type of protocol independence can also be achieved using SCA, in this post I will focus on EJB 3.0 and JAX-WS 2.1 because these standards are part of JEE 5 and are in wider use than SCA.
Before describing the pattern, it might be useful to explain why EJB is still relevant as an integration and remote invocation protocol:
  • EJB relies on Java serialization and binary protocols such as IIOP which are more efficient than SOAP/HTTP.
  • Propagation of the transaction and security context is built into EJB from the ground up.
  • Most EJB containers have support for load balancing and failover with a degree of reliability that is not as easy to achieve with Web Services.
It is also clear what are the major drawbacks of using EJBs in a Service Oriented Architecture:
  • Since EJBs are not (necessarily) described by a WSDL interface, it is not easy to reuse them, e.g. in a BPEL process.
  • EJB is specific to the Java platform and interoperability with other platforms (or e.g. XML appliances) is limited.
Obviously it is possible to expose a component both as a traditional EJB and a Web service, e.g. by wrapping the EJB in a Web service interface. This however doesn't achieve true protocol independence because switching between EJB remote invocation and SOAP is not transparent to the consumer of the service. It also requires additional effort when reusing an EJB that has not yet been wrapped as a Web service. What I will show in this post is that by leveraging the new features introduced in EJB 3.0 and by carefully designing the EJBs it is possible to provide a protocol independent client view with minimal effort and without making any concessions in terms of best practices in Web service design.

Starting with version 3.0, the EJB specification allows to expose stateless session beans as JAX-WS style Web services. More precisely, a stateless session bean now may have up to three different types of client views: remote, local and Web service. The basic idea of the pattern proposed here is to make the choice between EJB remote invocation and SOAP transparent to the client by using the same Java interface for the remote and Web service views. Taking into account best practices in Web service design, the procedure can be summarized as follows:
  1. Design the service contract using WSDL and XML schema.
  2. Use JAX-WS (wsimport) to generate a corresponding Java interface. Since this artifact will be used by the bean implementation and the (Java) clients, it is strongly recommended to make extensive use of JAX-WS and JAXB bindings to customize the code generation so that the end result is a convenient and easy to use API. It is also recommended to package the generated artifacts in a separate JAR that can be referenced by the bean implementation as well as the client.
  3. Create a stateless session bean implementing the interface and declare this interface as both the remote and Web service view of the bean. Note that the interface that the bean needs to implement is the one generated from the portType in the WSDL, i.e. the one annotated with @WebService.
While this looks simple and straightforward, there are however some additional points that need to be taken into account. The first is that in order to be a valid remote view, the interface must conform to RMI rules. The good news is that starting with EJB 3.0, the methods of a remote interface are no longer required to declare RemoteException. However, the restriction that all method arguments must be serializable is of course still applicable. This is not a fundamental issue since the classes generated by JAXB are simple POJOs that only refer to primitive types, serializable types such as String, Date, etc., collections and other generated POJOs. Therefore they can be made serializable by adding the Serializable interface. Fortunately, a simple JAXB customization (xjc:serializable) is sufficient to do this. It should also be noted that while it is allowed to use a single interface for the remote and Web service views, it is not possible to use the same interface as local and remote view. This problem is easy to overcome with the usual pattern of creating two interfaces that extend the interface generated by JAX-WS and declare them as local and remote respectively.
One should be aware that customizing the JAX-WS code generation is a task that requires effort that should not be underestimated, at least if one wants a clean Java interface. A complete discussion of the best practices in this area is out of the scope of this article, but I would like to draw attention to a feature of JAX-WS which is not very well known but which is important to make sure that JAX-WS generates a Java interface with convenient method signatures for operations using the document/literal style. This feature is called "Wrapper Style" and allows JAX-WS to unwrap the request object. A more detailed description of this feature as well as the criteria that the WSDL must meet can be found in section 2.3.1.2 of the JAX-WS 2.1 specification.
A second point that I would like to mention is that when customizing the code generation, one has to choose between declaring the JAX-WS and JAXB bindings inline in the WSDL and the schemas or using a separate binding file. Very often it is argued that these bindings are specific to the implementation of the service provider and/or consumer and should therefore be separated from the WSDL. This is certainly true in cases where the service is always invoked using SOAP. On the other hand, if the service can also be invoked as an EJB, one can argue that since the JAX-WS and JAXB bindings together with the WSDL completely describe the EJB interface, they are an integral part of the service contract and should be added to the WSDL. From this point of view, the JAX-WS/JAXB bindings are similar to the wsdl:binding elements mapping the abstract interface to the SOAP/HTTP protocol. Note however that while the abstract interface (i.e. the portType) and the SOAP/HTTP bindings can be separated into two WSDL files, this is not possible for the JAX-WS/JAXB bindings.

Assuming that all necessary customizations have been done and that a local interface is not required, the declaration of the stateless session bean would look as follows:
import employee.EmployeeService;

@Stateless
@WebService(endpointInterface="employee.EmployeeService")
@Remote(EmployeeService.class)
public class EmployeeServiceBean implements EmployeeService {
Note that the methods of the bean don't need any special annotations since they are all present on the interface. Except for container specific procedures (e.g. running endptEnabler on WebSphere), no further action is required to expose the bean as a Web service. It can now be invoked as an EJB or a Web service, and if the client is implemented in Java, the same Java interface can be used for both invocation styles.
Let's look more closely at the latter aspect, i.e. the invocation from a Java client. If the client should invoke the service as an EJB, we can use the @EJB annotation to let the container inject a reference:
@EJB
private EmployeeService employeeService;
On the other hand, if the client should use SOAP, then we can use the @WebServiceRef annotation. Note that this annotation can be used to inject either the interface annotated with @WebService (corresponding to the portType in the WSDL) or the interface annotated with @WebServiceClient (corresponding to the service element in the WSDL). Since the latter is not meaningful when invoking the service as an EJB, we use the first approach in order to achieve protocol independence:
@WebServiceRef(EmployeeServiceClient.class)
private EmployeeService employeeService;
In this sample, EmployeeServiceClient is the @WebServiceClient annotated interface (A JAX-WS binding has been used to assign this class name). As you can see, switching between EJB and SOAP is just a matter of changing the annotation. Note that in a JEE 5 compliant container, @WebServiceRef can be used wherever @EJB is recognized, in particular in session beans and servlets. Of course these references can alternatively be declared in the deployment descriptor and looked up using JNDI.
Making a @WebServiceRef work properly is actually a bit more tricky than @EJB. Two conditions must be met:
  • The WSDL file must be available. Ideally it should be included (together with all dependency artifacts such as imported WSLDs and schemas) as a resource in the JAR that contains the JAX-WS generated artifacts.
  • The wsdlLocation attribute of the @WebServiceClient annotation must be set correctly. This must either be an absolute URL (if the WSDL is not included in the JAR) or specify the location relative to the root of the module (see section 4.2.2 of JSR109 v1.2). Since the @WebServiceClient annotated interface is part of the code generated by JAX-WS, this can only be done by correctly configuring wsimport, namely using the -wsdlLocation option (or the wsdlLocation configuration element when using jaxws-maven-plugin).
Another important point is that the endpoint URI used by the container to invoke the service defaults to the one specified in the soap:address element in the WSDL. Since the endpoint URI is environment specific, it needs to be overridden at deployment time. This step is container specific. E.g. in WebSphere 7, endpoint URIs can be changed in the settings of the EJB or Web module in the admin console.

To conclude the discussion we should also address the question of where this pattern should be used. It would certainly be wrong to use this pattern for all services and it would also be wrong to use it for all EJBs. It really depends on the granularity of the service. On one end of the spectrum, we have course grained composite services that may potentially be implemented using BPEL or as a mediation flow. Here the pattern is not meaningful and the services should be implemented as Web services. On the other end of the spectrum, we have EJBs representing very fine grained services or components that should not be invoked directly by consumers in a different business domain. These services are best implemented as pure EJBs. The pattern is actually the most useful in the middle of the spectrum, where we find services that in a traditional J2EE architecture would be designed following the session facade pattern. Implementing session facades using the pattern described here makes them highly reusable and allows you to get the best of both worlds. E.g. a Web application co-located with the EJBs may use the local interfaces for maximum efficiency, while the same service is reused in a business process by invoking it as a Web service through a partner link in BPEL.
Interestingly, applying the pattern systematically actually has the additional benefit of enforcing proper usage of the good old Session Facade pattern. To see this, let's recall the goals of using session facades:
  • Provide a simpler interface to the clients by hiding all the complex interactions between business components.
  • Reduce the number of business objects that are exposed to the client across the service layer over the network.
  • Hide from the client the underlying interactions and interdependencies between business components. This provides better manageability, centralization of interactions (responsibility), greater flexibility, and greater ability to cope with changes.
  • Provide a uniform coarse-grained service layer to separate business object implementation from business service abstraction.
  • Avoid exposing the underlying business objects directly to the client to keep tight coupling between the two tiers to a minimum.
Usually the Session Facade pattern is combined with the Transfer Object pattern, i.e. the facade doesn't use entity objects (entity beans in EBJ 1.x and 2.x; JPA entity classes in EJB 3.0) directly, but value objects specifically designed for that facade. It is easy to see how these patterns can be enforced using the pattern described in this article:
  • The WSDL-first approach forces the designer of the service to make the contract independent of the underlying business components and to use the right level of abstraction.
  • The POJOs generated by JAXB are in general not suitable for use as entity classes in JPA. While at first glance this may seem to be a drawback of the pattern, it actually enforces the Transfer Object pattern and avoids exposing the underlying business objects directly to the client. At the same time, the code generation step relieves the developer from the task of manually creating the classes used in the Transfer Object pattern.
  • Keeping the volume of interactions over the network at the right level is a common goal for EJBs and Web services. This concern is probably easier to address in a WSDL-first approach.

2 comments:

  1. Update 1: If you want the server to publish the original WSDL document (instead of a WSDL generated based on the JAX-WS annotations), then you need to provide additional information in the @WebService annotation on the EJB:

    @WebService(endpointInterface="employee.EmployeeService", serviceName = "EmployeeService", portName="EmployeeServicePort", targetNamespace = "urn:employee", wsdlLocation = "employee/employee.wsdl")

    The serviceName, portName and targetNamespace attributes must match the corresponding names in the WSDL.

    ReplyDelete
  2. Update 2: There is one additional argument against using inline JAXB customizations: they don't interact very well with the JAXB feature called "episodes". This feature allows to generate code used by several WSDLs (or other schemas) only once. The way this works is fairly simple: when generating the code from the common schema, xjc generates a file called "sun-jaxb.episode", which is simply a binding file. This file is then used during code generation for schemas or WSDLs that refer to the common schema. It makes sure that the classes for the common schema are not generated twice.
    The problem is that because sun-jaxb.episode is a binding file, it may conflict with the bindings declared inline in the common schema.
    It should also be noted that introduction of SCD (Schema Component Designators) support in xjc makes writing the binding files much easier.

    ReplyDelete