EJB Server Side Programming Model
The EJB server side programming model is about exposing Stateless Session Beans (SLSB) as service endpoints.
According to the spec, Stateful Session Beans, Message Driven Beans, or Entity Beans cannot be exposed as
Web Services. The EJB service endpoint is deployed as a standard ejb archive plus an additional
webservices.xml deployment descriptor.
Service Endpoint Interface
The EJB-2.1 specification introduces a new type of interface for a SLSB, the service endpoint interface (SEI).
The SEI declares the endpoint implementation methods that are to be exposed as Web Service operations.
I would like to discuss the concepts using the example of a Crime Portal, which you might be familiar with
from the JBoss Advanced Training.
In times of a recession the boss' of various gangster organizations, decide to expose some information about their
organizations using a Web Service. When a potential customer needs to get a job done, the boss' hope that the customer
will find this portal useful when choosing between the various organizations.
The gangster CTO Roberto Amazzo decides to expose the following information about an organization.
package com.underworld.crimeportal; public interface OrganizationEndpoint extends java.rmi.Remote { public String getContactInfo(String organization) throws java.rmi.RemoteException; }
Because webservices are all about interoperability, we need a description language that tells the
(possibly non java) world about our new webservice. This language is WSDL, the webservice description language.
Web Service Description Language
To describe the functionality of a service endpoint, we use an interoperable document format, the WSDL.
The Java Web Service Development Pack comes
with the tools needed to generate the WSDL from the service endpoint interface.
To generate a WSDL that uses rpc/literal, you would use something similar to:
wscompile -cp ../../output/classes -gen:server -f:rpcliteral -d META-INF/wsdl -mapping META-INF/OrganizationServiceEJB_Mapping.xml config.xml <configuration xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config"> <service name="OrganizationService" targetNamespace="http://com.underworld.crimeportal" typeNamespace="http://com.underworld.crimeportal/types" packageName="com.underworld.crimeportal"> <interface name="com.underworld.crimeportal.OrganizationEndpoint"></interface> </service> </configuration>
The options to wscompile are described as part
of the jaxrpc documentation that comes with the JWSDP.
The webservice tutorial is also a good starting point.
Here is a WSDL, that corresponds to the service endpoint interface from above.
<definitions name="OrganizationServiceEJB" targetNamespace="http://com.underworld.crimeportal" xmlns:tns="http://com.underworld.crimeportal" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"> <types></types> <message name="OrganizationEndpoint_getContactInfo"> <part name="String_1" type="xsd:string"></part> </message> <message name="OrganizationEndpoint_getContactInfoResponse"> <part name="result" type="xsd:string"></part> </message> <portType name="OrganizationEndpoint"> <operation name="getContactInfo" parameterOrder="String_1"> <input message="tns:OrganizationEndpoint_getContactInfo"/> <output message="tns:OrganizationEndpoint_getContactInfoResponse"></output> </operation> </portType> <binding name="OrganizationEndpointBinding" type="tns:OrganizationEndpoint"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"></soap:binding> <operation name="getContactInfo"> <soap:operation soapAction=""></soap:operation> <input> <soap:body use="literal" namespace="http://com.underworld.crimeportal"></soap:body> </input> <output> <soap:body use="literal" namespace="http://com.underworld.crimeportal"></soap:body> </output> </operation> </binding> <service name="OrganizationServiceEJB"> <port name="OrganizationEndpointPort" binding="tns:OrganizationEndpointBinding"> <soap:address location="REPLACE_WITH_ACTUAL_URL"></soap:address> </port> </service> </definitions>
Note, the server side WSDL may define a dummy webservice location. JBoss will replace the location attribute
after successful deployment.
JAX-RPC Mapping Files
In addition to a service endpoint interface and a WSDL file, the Web Service must include a JAX-RPC mapping file.
It maps endpoint interfaces, methods, and parameters to WSDL portType, operation, and message definitions.
It also maps a JAX-RPC service interface to a particular WSDL service definition.
JAX-RPC mapping can get quite complicated, but Roberto opts for simplicity and only provides mapping for the java
package name. Remember WSDL is a portable format and therefore has no notion of the java package structure.
Mr. Amazzos simplistic JAX-RPC mapping file would look like this.
<java-wsdl-mapping xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://www.ibm.com/webservices/xsd/j2ee_jaxrpc_mapping_1_1.xsd" version="1.1"> <package-mapping> <package-type>com.underworld.crimeportal</package-type> <namespaceURI>http://com.underworld.crimeportal</namespaceURI> </package-mapping> </java-wsdl-mapping>
Note, the mapping file generated by wscompile is more complete.
Packaging the service endpoint
The standard deployment descriptor for EJB components ejb-jar.xml, declares the service endpoint.
<ejb-jar xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd" version="2.1"> <enterprise-beans> <session> <ejb-name>OrganizationBean</ejb-name> <service-endpoint>com.underworld.crimeportal.OrganizationEndpoint</service-endpoint> <ejb-class>com.underworld.crimeportal.ejb.OrganizationSLSB</ejb-class> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type> </session> </enterprise-beans> </ejb-jar>
Additionally, we need the J2EE Web Service deployment descriptor, webservices.xml
<webservices xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:impl="http://com.underworld.crimeportal/ws4ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://www.ibm.com/webservices/xsd/j2ee_web_services_1_1.xsd" version="1.1"> <webservice-description> <webservice-description-name>OrganizationServiceEJB</webservice-description-name> <wsdl-file>META-INF/wsdl/OrganizationServiceEJB.wsdl</wsdl-file> <jaxrpc-mapping-file>META-INF/OrganizationServiceEJB_Mapping.xml</jaxrpc-mapping-file> <port-component> <port-component-name>Organization</port-component-name> <wsdl-port>OrganizationEndpointPort</wsdl-port> <service-endpoint-interface>com.underworld.crimeportal.OrganizationEndpoint</service-endpoint-interface> <service-impl-bean> <ejb-link>OrganizationSLSB</ejb-link> </service-impl-bean> </port-component> </webservice-description> </webservices>
Deploy the Web Service to JBoss
When you deploy the jar that contains all the artifacts we just taked about, you should see something like this
in the JBoss console
14:49:49,656 INFO [EjbModule] Deploying OrganizationSLSB 14:49:49,906 INFO [EJBDeployer] Deployed: file:/.../server/default/deploy/samples-server-ejb.jar 14:49:49,921 INFO [WSDLFilePublisher] WSDL published to: file:/.../server/default/data/wsdl/samples-server-ejb.jar/OrganizationServiceEJB.wsdl 14:49:50,062 INFO [AxisService] WSDD published to: ...\server\default\data\wsdl\samples-server-ejb.jar\Organization.wsdd 14:49:50,062 INFO [AxisService] Web Service deployed: http://TDDELL:8080/samples-server-ejb/Organization
To see the WSDL that describes this service, point your browser to
http://localhost:8080/samples-server-ejb/OrganizationServiceEJB?wsdl
you will see the WSDl from above, with a corrected location.
Accessing the Service from Java
This service can now be accessed from any webservice client. Here is a simple test case, that shows how a java client
would first obtain the WSDL from the remote location, and then invoke getContactInfo on the service endpoint
interface.
public void testWebServiceAccess() throws Exception { URL url = new URL("http://localhost:8080/samples-server-ejb/Organization?wsdl"); QName serviceName = new QName("http://com.underworld.crimeportal", "OrganizationServiceEJB"); ServiceFactory factory = ServiceFactory.newInstance(); Service service = factory.createService(url, serviceName); OrganizationEndpoint endpoint = (OrganizationEndpoint)service.getPort(OrganizationEndpoint.class); String info = endpoint.getContactInfo("mafia"); assertEquals("The 'mafia' boss is currently out of office, please call again.", info); }
Comments