You are on page 1of 19

Chapter 12.

Web Services
Prev Next

Chapter 12. Web Services


The biggest new feature of J2EE 1.4 is the ability of J2EE components to act both as web
service providers and consumers. J2EE applications can expose a web service from the
EJB tier using a stateless session bean or from the web tier using a plain Java object.
Additionally, both server-side components and J2EE client applications have a standard
way of declaring references to external web services.

12.1. JAX-RPC Service Endpoints


JAX-RPC service endpoints (JSEs) provide web services from the web tier. They take the
form of a simple Java objects that masquerade as servlets. To show how simple they are,
we'll jump right in with a trivial hello web service implementation class.

package org.jboss.chap12.hello;

public class HelloServlet


{
public String hello(String name)
{
return "Hello " + name + "!";
}
}

There is nothing remarkable about HelloServlet. It doesn't implement any special


interfaces nor does it need any methods besides the business methods it decides to
provide. The hello method is the operation we will expose as a web service, and it does
nothing but respond with a friendly greeting to the person passed in.

That is our web service implementation. In addition to this, we need a service endpoint
class that defines the interface of the web service. That is shown here as the Hello
interface.

package org.jboss.chap12.hello;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Hello


extends Remote
{
public String hello(String name)
throws RemoteException;
}

The service endpoint interface is declared Remote and the methods must throw
RemoteException. Beyond this, it is a simple expression of the interface to our web
service. This is all the code we need to write to expose a J2EE web service. Deploying it,
however, does require a few additional deployment descriptors.

Although a JSE doesn't bears any direct resemblance to a servlet, it is nonetheless


deployed as a servlet in the web.xml file. We'll need to declare the web service
implementation class as a servlet and provide a servlet mapping that will respond to the
web service invocations. Here is the definition required to deploy the hello web service.

<?xml version="1.0" encoding="UTF-8"?>


<web-app 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/j2
ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">

<servlet>
<servlet-name>HelloWorldServlet</servlet-name>
<servlet-
class>org.jboss.chap12.hello.HelloServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>HelloWorldServlet</servlet-name>
<url-pattern>/Hello</url-pattern>
</servlet-mapping>

</web-app>

The URL pattern in the servlet mapping is the only externally visible configuration
element. It controls what URL the web service lives at. This will be primarily noticed as
the location of the WSDL file for this service.

The web.xml file doesn't contain any web service related configuration. A new
deployment descriptor, webservices.xml, is needed to instruct JBoss to treat this
servlet as a web service and not as a normal servlet. But before we see that, we'll need
two additional configuration files, a WSDL file and a JAX-RPC mapping file. Both of
these files can be generated using the wscompile tool that ships as part of the Java Web
Services Developer Pack (WSDP).

wscompile -classpath <classpath> -gen:server -f:rpcliteral


-mapping mapping.xml config.xml

This generates a WSDL file and a JAX-RPC mapping file based on the supplied
config.xml and the corresponding classes found on the classpath. The config.xml
file for the hello web service is shown below.

<?xml version="1.0" encoding="UTF-8"?>

<configuration xmlns="http://java.sun.com/xml/ns/jax-
rpc/ri/config">
<service name="HelloService"
targetNamespace="http://hello.chap12.jboss.or
g/"
typeNamespace="http://hello.chap12.jboss.org/
types"
packageName="org.jboss.chap12.hello">
<interface name="org.jboss.chap12.hello.Hello"/>
</service>
</configuration>

The service element defines the interface our web service provides. The following
attributes are required:

• name: This is the name of the web service.


• targetNamespace: Web services require namespaces just like Java classes do. It's a
common practice to use a URL namespace that corresponds to the Java namespace given.
• typeNamespace: This specifies the namespace to use for custom types.
• packageName: This is the base package name that your web services classes live under.

Additionally, we need an interface element that tells wscompile what the Java
interface for the webservice is. This interface declares the operations that the web service
provides.

The WSDL file that wscompile generated for our config.xml file is shown below. Note
that the SOAP address isn't provided in the WSDL file. JBoss will insert the correct URL
for the WSDL when it deploys the web service.

<?xml version="1.0" encoding="UTF-8"?>


<definitions name="HelloService"
targetNamespace="http://hello.chap12.jboss.org/"
xmlns:tns="http://hello.chap12.jboss.org/"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<types/>
<message name="Hello_hello">
<part name="String_1" type="xsd:string"/>
</message>
<message name="Hello_helloResponse">
<part name="result" type="xsd:string"/>
</message>
<portType name="Hello">
<operation name="hello" parameterOrder="String_1">
<input message="tns:Hello_hello"/>
<output message="tns:Hello_helloResponse"/>
</operation>
</portType>
<binding name="HelloBinding" type="tns:Hello">
<soap:binding
transport="http://schemas.xmlsoap.org/soap/http"
style="rpc"/>
<operation name="hello">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"
namespace="http://hello.chap12.jboss.org/"/>
</input>
<output>
<soap:body use="literal"
namespace="http://hello.chap12.jboss.org/"/>
</output>
</operation>
</binding>
<service name="HelloService">
<port name="HelloPort" binding="tns:HelloBinding">
<soap:address
location="REPLACE_WITH_ACTUAL_URL"/>
</port>
</service>
</definitions>

We also asked wscompile to generate a JAX-RPC mapping file. This is shown below.

<?xml version="1.0" encoding="UTF-8"?>


<java-wsdl-mapping version="1.1"
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">
<package-mapping>
<package-type>org.jboss.chap12.hello</package-
type>
<namespaceURI>http://hello.chap12.jboss.org/types<
/namespaceURI>
</package-mapping>
<package-mapping>
<package-type>org.jboss.chap12.hello</package-
type>
<namespaceURI>http://hello.chap12.jboss.org/</name
spaceURI>
</package-mapping>
<service-interface-mapping>
<service-
interface>org.jboss.chap12.hello.HelloService</service-
interface>
<wsdl-service-name
xmlns:serviceNS="http://hello.chap12.jboss.org/">serviceNS
:HelloService</wsdl-service-name>
<port-mapping>
<port-name>HelloPort</port-name>
<java-port-name>HelloPort</java-port-name>
</port-mapping>
</service-interface-mapping>
<service-endpoint-interface-mapping>
<service-endpoint-
interface>org.jboss.chap12.hello.Hello</service-endpoint-
interface>
<wsdl-port-type
xmlns:portTypeNS="http://hello.chap12.jboss.org/">portType
NS:Hello</wsdl-port-type>
<wsdl-binding
xmlns:bindingNS="http://hello.chap12.jboss.org/">bindingNS
:HelloBinding</wsdl-binding>
<service-endpoint-method-mapping>
<java-method-name>hello</java-method-name>
<wsdl-operation>hello</wsdl-operation>
<method-param-parts-mapping>
<param-position>0</param-position>
<param-type>java.lang.String</param-type>
<wsdl-message-mapping>
<wsdl-message
xmlns:wsdlMsgNS="http://hello.chap12.jboss.org/">wsdlMsgNS
:Hello_hello</wsdl-message>
<wsdl-message-part-
name>String_1</wsdl-message-part-name>
<parameter-mode>IN</parameter-mode>
</wsdl-message-mapping>
</method-param-parts-mapping>
<wsdl-return-value-mapping>
<method-return-
value>java.lang.String</method-return-value>
<wsdl-message
xmlns:wsdlMsgNS="http://hello.chap12.jboss.org/">wsdlMsgNS
:Hello_helloResponse</wsdl-message>
<wsdl-message-part-name>result</wsdl-
message-part-name>
</wsdl-return-value-mapping>
</service-endpoint-method-mapping>
</service-endpoint-interface-mapping>
</java-wsdl-mapping>

Once the extra files are generated, we need to bundle them up in a webservices.xml
file. This file links to our WSDL file with the wsdl-file element and to the mapping
file using the jaxrpc-mapping-file element.

In addition to this, a port-component element is needed that maps a port in the WSDL file
to a particular service implementation. For our JSE, this is done with a servlet-link
inside the service-impl-bean element. The servlet link must be the same as the name
of the pseudo-servlet we declared in the web.xml file.

<webservices 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_web_services_1_1.x
sd" version="1.1">
<webservice-description>
<webservice-description-
name>HelloService</webservice-description-name>
<wsdl-file>WEB-INF/wsdl/HelloService.wsdl</wsdl-
file>
<jaxrpc-mapping-file>WEB-INF/mapping.xml</jaxrpc-
mapping-file>
<port-component>
<port-component-name>Hello</port-component-
name>
<wsdl-port>HelloPort</wsdl-port>
<service-endpoint-
interface>org.jboss.chap12.hello.Hello</service-endpoint-
interface>
<service-impl-bean>
<servlet-link>HelloWorldServlet</servlet-
link>
</service-impl-bean>
</port-component>
</webservice-description>
</webservices>

With these completed we can deploy the WAR file containing our web service. All the
deployment descriptors go in the WEB-INF directory, as shown in Figure 12.1, “The
structure of hello-servlet.war”. It's important to note that the WSDL file is required to be
in the wsdl subdirectory.

Figure 12.1. The structure of hello-servlet.war

To deploy and test the hello web service, run the following from the examples directory:

[examples]$ ant -Dchap=chap12 -Dex=1 run-example


...
run-example1:
[echo] Waiting for 5 seconds for deploy...
[java] Contacting webservice at
http://localhost:8080/hello-servlet/Hello?wsdl
[java] hello.hello(JBoss user)
[java] output:Hello JBoss user!

Note the URL the JBoss publishes the WSDL file at. Our web application name is
hello-servlet and we mapped the servlet to /Hello in the web.xml file so the web
service is mappend to /hello-servlet/Hello. The ?wsdl query returns the WSDL
file.

If you aren't sure what the URL of the WSDL file will be, JBoss provides a way to list the
web services available on the system at /ws4ee/services. Figure 12.2, “The web
services list” shows a view of the services list.

Figure 12.2. The web services list

The services list shows all of the deployed web services along with the name of the
deployment unit and a link to the WSDL file for that service.

12.2. EJB Endpoints


Web services can also be provided from the EJB tier. Any stateless session bean can serve
as the endpoint for a web service in almost the same way as the JAX-RPC endpoints. To
see how this works, we will adapt the HelloServlet example into a session bean. Here
is the code:

package org.jboss.chap12.hello;

import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;

public class HelloBean


implements SessionBean
{
public String hello(String name)
{
return "Hello " + name + "!";
}

public void ejbCreate() {};


public void ejbRemove() {};

public void ejbActivate() {}


public void ejbPassivate() {}

public void setSessionContext(SessionContext ctx) {}


}

This is a very trivial session bean. Session beans normally require a home interface and
either a local or remote interface. However, it is possible to omit them if the session bean
is only serving as a web services endpoint. However, we do still need the Hello service
endpoint interface that we used in the JSE example.

The ejb-jar.xml file is very standard for a session bean. The normal session bean
parameters are explained in Chapter 5, EJBs on JBoss. The only new element is the
service-endpoint element, which declares the service endpoint interface for the web
service.

<?xml version="1.0" encoding="UTF-8"?>


<ejb-jar xmlns="http://java.sun.com/xml/ns/j2ee"
version="2.1"
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">
<display-name>chapter 12 EJB JAR</display-name>
<enterprise-beans>
<session>
<ejb-name>HelloBean</ejb-name>
<service-
endpoint>org.jboss.chap12.hello.Hello</service-endpoint>
<ejb-
class>org.jboss.chap12.hello.HelloBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>
<assembly-descriptor>
<method-permission>
<unchecked/>
<method>
<ejb-name>HelloBean</ejb-name>
<method-name>*</method-name>
</method>
</method-permission>
<container-transaction>
<method>
<ejb-name>HelloBean</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>

Accompanying this there needs to be a supporting webservices.xml. The file, shown


below, looks almost identical to the webservices.xml for the WAR file.

<webservices 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_web_services_1_1\.
xsd" version="1.1">
<webservice-description>
<webservice-description-
name>HelloService</webservice-description-name>

<wsdl-file>META-INF/wsdl/HelloService.wsdl</wsdl-
file>
<jaxrpc-mapping-file>META-INF/mapping.xml</jaxrpc-
mapping-file>

<port-component>
<port-component-name>Hello</port-component-
name>
<wsdl-port>HelloPort</wsdl-port>
<service-endpoint-
interface>org.jboss.chap12.hello.Hello</service-endpoint-
interface>
<service-impl-bean>
<ejb-link>HelloBean</ejb-link>
</service-impl-bean>
</port-component>
</webservice-description>
</webservices>

The first difference is that the WSDL file should be in the META-INF/wsdl directory
instead of the WEB-INF/wsdl directory. The second difference is that the service-
impl-bean element contains an ejb-link that refers to the ejb-name of the session
bean. The WSDL file and JAX-RPC mapping files remain unchanged from the previous
example.

To package and deploy the application, run the following command in the examples
directory:

[examples]$ ant -Dchap=chap12 -Dex=2 run-example


...
run-example2:
[copy] Copying 1 file to /tmp/jboss-
4.0.1/server/default/deploy
[echo] Waiting for 5 seconds for deploy...
[java] Contacting webservice at
http://localhost:8080/hello-ejb/Hello?wsdl
[java] hello.hello(JBoss user)
[java] output:Hello JBoss user!

The test program run here is the same as with the servlet example, except that we use a
different URL for the WSDL. JBoss composes the WSDL using the base name of the EJB
JAR file and the name of the service interface. However, as with all web services in
JBoss, you can use the http://localhost:8080/ws4ee/services service view
shown in Figure 12.2, “The web services list” to verify the deployed URL of the WSDL.

12.3. Web Services Clients


We will now turn our attention from providing web services to consuming them.

12.3.1. A JAX-RPC client

The full JAX-RPC programming model is available to J2EE applications and clients. We
won't cover the full range of client programming techniques, but we swill look briefly at
the client we've used so far to test the web services we've deployed. The client, shown in
the following listing, illustrates the dynamic proxy invocation mechanism.

package org.jboss.chap12.client;
import org.jboss.chap12.hello.Hello;

import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;

import javax.xml.namespace.QName;

import java.net.URL;

public class HelloClient


{
public static void main(String[] args)
throws Exception
{
String urlstr = args[0];
String argument = args[1];

System.out.println("Contacting webservice at " +


urlstr);

URL url = new URL(urlstr);

QName qname = new


QName("http://hello.chap12.jboss.org/",
"HelloService");

ServiceFactory factory =
ServiceFactory.newInstance();
Service service =
factory.createService(url, qname);

Hello hello = (Hello)


service.getPort(Hello.class);

System.out.println("hello.hello(" + argument +
")");
System.out.println("output:" +
hello.hello(argument));
}
}

This JAX-RPC client uses the Hello service endpoint interface and creates a dynamic
proxy to speak to the service advertised by the WSDL at the URL that is passed in as a
command line argument. For illustrative purposes, we'll show another variation of web
services invocation that doesn't use the service endpoint interface. This is known as the
Dynamic Invocation Interface (DII). Using DII, it is possible to refer to a specific port
and operation by name. Think of it as reflection for web services. The client code is
shown in the following listing.

package org.jboss.chap12.client;

import org.jboss.chap12.hello.Hello;

import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
import javax.xml.rpc.Call;

import javax.xml.namespace.QName;

import java.net.URL;

public class HelloClientDII


{
public static void main(String[] args)
throws Exception
{
String urlstr = args[0];
String argument = args[1];

System.out.println("Contacting webservice at " +


urlstr);

URL url = new URL(urlstr);

String ns =
"http://hello.chap12.jboss.org/";
QName qname = new QName(ns, "HelloService");
QName port = new QName(ns, "HelloPort");
QName operation = new QName(ns, "hello");

ServiceFactory factory =
ServiceFactory.newInstance();
Service service =
factory.createService(url, qname);
Call call = service.createCall(port,
operation);

System.out.println("hello.hello(" + argument +
")");
System.out.println("output:" + call.invoke(new
Object[] {argument}));
}
}

The following two commands can be used to run DII client against both the JSE and EJB
web services we have created.

[examples]$ ant -Dchap=chap12 -Dex=1b run-example


[examples]$ ant -Dchap=chap12 -Dex=2b run-example

12.3.2. Service references

The JAX-RPC examples in Section 12.3.1, “A JAX-RPC client” all required manual
configuration of the WSDL URL and knowledge of the XML nature of the web services
in question. This can be a configuration nightmare, but if your code is a J2EE component
there is another option. J2EE components can declare service references and look up
JAX-RPC Service objects in JNDI without needing to hardcode any web service
references in the code.

To show how this works, let's first look at a session bean that needs to make a call to the
hello web service:

package org.jboss.chap12.example;

import javax.ejb.*;
import javax.naming.*;
import java.rmi.RemoteException;

import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceException;

import org.jboss.chap12.hello.Hello;

public class ExampleBean


implements SessionBean
{
public String doWork()
{
try {
Context ctx = new InitialContext();

Service service = (Service)


ctx.lookup("java:comp/env/services/hello");
Hello hello = (Hello)
service.getPort(Hello.class);

return hello.hello("example bean");


} catch (NamingException e) {
throw new EJBException(e);

} catch (ServiceException e) {
throw new EJBException(e);

} catch (RemoteException e) {
throw new EJBException(e);
}
}

public void ejbCreate() {};


public void ejbRemove() {};

public void ejbActivate() {}


public void ejbPassivate() {}

public void setSessionContext(SessionContext ctx) {}


}

ExampleBean invokes the hello web service in its doWork method. We've used the
dynamic proxy invocation method here, but any of the JAX-RPC supported invocation
methods would be fine. The interesting point here is that the bean has obtained the
Service reference from a JNDI lookup in its ENC.

Web service references are declared using a service-ref element in inside an ejb-
jar.xml file.
Figure 12.3. The service-ref content model

The following elements are supported by the service-ref:

• service-ref-name: This is the JNDI name that the service object will be bound under in
the bean's ENC. It is relative to java:comp/env/.
• service-interface: This is the name of JAX-RPC service interface the client will use.
Normally this is javax.xml.rpc.Service, but it's possible to provide your own
service class.
• wsdl-file: This is the location of the WSDL file. The WSDL file should be under META-
INF/wsdl.
• jaxrpc-mapping-file: This is the location of the JAX-RPC mapping file.
• service-qname: This element specifies the name of the service in the web services file. It
is only mandatory if the WSDL file defines multiple services. The value must by a
QName, which means it needs to be a namespace qualified value such as
ns:ServiceName where ns is an XML namespace valid at the scope of the service-
qname element.
• port-component-ref: This element provides the mapping between a service endpoint
interface and a port in a web service.
• handler: This allows the specification of handlers, which act like filters or interceptors
on the current request.

The following service-ref declares a reference to the hello web service for the
Example session bean.
<session>
<ejb-name>Example</ejb-name>
<home>org.jboss.chap12.example.ExampleHome</home>
<remote>org.jboss.chap12.example.Example</remote>
<ejb-class>org.jboss.chap12.example.ExampleBean</ejb-
class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
<service-ref>
<service-ref-name>services/hello</service-ref-
name>
<service-interface>javax.xml.rpc.Service</service-
interface>
<wsdl-file>META-INF/wsdl/hello.wsdl</wsdl-file>
<jaxrpc-mapping-file>META-INF/mapping.xml</jaxrpc-
mapping-file>
<service-qname
xmlns:hello="http://hello.chap12.jboss.org">hello:HelloSer
vice</service-qname>
</service-ref>
</session>

This instructs the EJB deployer to make a Service object available for the bean in JNDI
under the name java:comp/env/services/hello that talks to our hello web service.
The session bean can then invoke normal web services operations on the service.

Since most of the web services configuration options are completely standard, there's
little need to go into great depths here. However, JBoss does provide several additional
web services configuration options through the service-ref element in the
jboss.xml deployment descriptor. The content model for the service-ref element is
shown in Figure 12.4, “The jboss.xml service-ref content model”.

Figure 12.4. The jboss.xml service-ref content model


The configurable elements are:

• service-ref-name: This element should match the service-ref-name in the ejb-


jar.xml file that is being configured.
• port-component-ref: The port-component-ref element provides additional
information for a specific port. This includes properties that should be associated with the
JAX-RPC stub for the port.
• wsdl-override: This provides an alternate location for the WSDL file. The value can be
any valid URL. This can be used in co-ordination with the wsdl-publish-location
to get the final WSDL file for a locally published web service. It could also be the URL
of a remotely published WSDL that you don't want duplicated in the deployment file.
• call-property: This sets properties on the JAX-RPC stub.

Since the WSDL file generated by wscompile doesn't contain the SOAP address of our
web service, we'll use the WSDL override feature to dynamically download the correct
WSDL file from the server. While this might not be the best technique to use in a
production application, it does illustrate the WSDL override functionality very well. The
following jboss.xml file links the published URL for the hello-servlet version of
the hello web service..

<!DOCTYPE jboss PUBLIC


"-//JBoss//DTD JBOSS 4.0//EN"
"http://www.jboss.org/j2ee/dtd/jboss_4_0.dtd">
<jboss>
<enterprise-beans>
<session>
<ejb-name>Example</ejb-name>
<service-ref>
<service-ref-name>services/hello</service-
ref-name>
<wsdl-
override>http://localhost:8080/hello-
servlet/Hello?wsdl</wsdl-override>
</service-ref>
</session>
</enterprise-beans>
</jboss>

This example can be run as shown below:

[examples]$ ant -Dchap=chap12 -Dex=2 run-example


...
run-example3:
[echo] Waiting for 5 seconds for deploy...
[copy] Copying 1 file to /tmp/jboss-
4.0.1/server/default/deploy
[echo] Waiting for 5 seconds for deploy...
[java] output:Hello example bean!

The service-ref element is not limited to the ejb-jar.xml file. It's available to any
J2EE component. A service reference can be placed in the web.xml file for use by web
tier components or in the application-client.xml file for use by J2EE client
applications.

Prev Up Next
Chapter 11. The CMP Engine Home Chapter 13. Hibernate

You might also like