Skip to main content

JAX-WS Web Services Without Java EE Containers

July 3, 2007

{cs.r.title}



One justification for including "https://jax-ws.dev.java.net/">JAX-WS 2.0 as part of Java SE
6.0 instead of Java EE 5 is that web service delivery with JAX-WS
2.0 does not require a servlet or EJB container. This makes HTTP
more or less an equal peer of RMI as an intrinsic protocol for
distributed computing on the Java platform, and further extends the
reach of web services.

This article shows how to use the JAX-WS class "http://java.sun.com/javaee/5/docs/api/javax/xml/ws/Endpoint.html">

javax.xml.ws.Endpoint
to easily publish web services,
and demonstrates how to configure the service runtime environment
in this situation and use a customized HTTP server.

Run Web Services in Java SE 6.0

We will work through an example to demonstrate how to develop
and run a simple web service within the Java SE 6.0
environment.

The following shows the WSDL for this example,
StockQuoteService.wsdl.

[prettify]
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:tns="http://service.stockquote.jaxws.company.com/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    targetNamespace="http://service.stockquote.jaxws.company.com/"
    name="StockQuoteService">
  <types>
    <xsd:schema>
      <xsd:import schemaLocation="StockQuoteService.xsd" namespace="http://service.stockquote.jaxws.company.com/"></xsd:import>
    </xsd:schema>
  </types>
  <message name="getQuote">
    <part element="tns:getQuote" name="parameters"></part>
  </message>
  <message name="getQuoteResponse">
    <part element="tns:getQuoteResponse" name="parameters"></part>
  </message>
  <portType name="StockQuote">
    <operation name="getQuote">
      <input message="tns:getQuote"></input>
      <output message="tns:getQuoteResponse"></output>
    </operation>
  </portType>
  <binding name="StockQuotePortBinding" type="tns:StockQuote">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"></soap:binding>
    <operation name="getQuote">
      <soap:operation soapAction=""></soap:operation>
      <input>
        <soap:body use="literal"></soap:body>
      </input>
      <output>
        <soap:body use="literal"></soap:body>
      </output>
    </operation>
  </binding>
  <service name="StockQuoteService">
    <port name="StockQuotePort" binding="tns:StockQuotePortBinding">
      <soap:address location="http://localhost:1970/StockQuote/StockQuoteService"></soap:address>
    </port>
  </service>
</definitions>
[/prettify]

This WSDL is of the Document/Literal/Wrapped style, and
it defines one single operation, getQuote, in the single
port type StockQuote. The XML schema file referred to in
the WSDL, StockQuoteService.xsd, is as follows:

[prettify]
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:tns="http://service.stockquote.jaxws.company.com/"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://service.stockquote.jaxws.company.com/"
    version="1.0">
  <xs:element name="getQuote" type="tns:getQuote"></xs:element>
  <xs:element name="getQuoteResponse" type="tns:getQuoteResponse"></xs:element>
  <xs:complexType name="getQuote">
    <xs:sequence>
      <xs:element name="arg0" type="xs:string" minOccurs="0"></xs:element>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="getQuoteResponse">
    <xs:sequence>
      <xs:element name="return" type="xs:double"></xs:element>
    </xs:sequence>
  </xs:complexType>
</xs:schema>
[/prettify]

To implement the web service with JAX-WS according to such
contracts, we need to implement the getQuote operation
specified in the StockQuote port type. Here is
the simple implementation class we are going to work with in this
article, StockQuoteImpl.java:

[prettify]
@WebService(name = "StockQuote", serviceName = "StockQuoteService")
public class StockQuoteImpl {
    @WebMethod(operationName = "getQuote")
    public double getQuote(String ticker) {
        double result = 0.0;
        if (ticker.equals("MHP")) {
            result = 50.0;
        } else if (ticker.equals("IBM")) {
            result = 83.0;
        }
        return result;
    }
}
[/prettify]

Note that we annotate this class with several annotations
defined in "http://jcp.org/aboutJava/communityprocess/pfd/jsr181/index.html">JSR
181
, which JAX-WS implementations are required to support. To
generate the artifacts required by the service runtime, we need the
apt tool (actually the corresponding Ant task), which
is part of the JDK since 5.0, to process those annotations. The
target for this step, as defined in build.xml, is as
follows:

[prettify]
    <target name="build-service" depends="setup">
        <apt
                fork="true"
                debug="${debug}"
                verbose="${verbose}"
                destdir="${build.classes.home}"
                sourcedestdir="${build.classes.home}"
                sourcepath="${basedir}/src">
            <classpath>
                <path refid="jaxws.classpath"/>
                <pathelement location="${basedir}/src"/>
            </classpath>
            <option key="r" value="${build.home}"/>
            <source dir="${basedir}/src">
                <include name="**/service/*.java"/>
            </source>
        </apt>
    </target>
[/prettify]

After compiling the service implementation class and all the
generated classes, we have completed building the example web
service.

If we are going to run this web service in, say, a servlet
container such as Tomcat, we need to perform the following further
steps, at a minimum:

  1. Create a proprietary web service deployment descriptor (for
    example, sun-jaxws.xml) and the web application deployment
    descriptor (web.xml).
  2. Package the service implementation class, the
    apt-generated artifacts, and those descriptors into a
    .war archive.
  3. Deploy the .war archive into the servlet container.

For an example of this, see my earlier article, " "http://today.java.net/pub/a/today/2006/09/19/asynchronous-jax-ws-web-services.html">
Asynchronous Web Service Invocation with JAX-WS 2.0
."

However, with Java SE 6.0, and with the help of
javax.xml.ws.Endpoint from JAX-WS, we do not need a
servlet container to run this web service. Instead, we can come up
a bootstrap class (which can be the same service implementation
class, if we prefer) that calls one of the publish(...)
methods in the Endpoint class. Here are the essential
lines of code in the bootstrap class we will use,
StockQuoteService.java:

[prettify]
public class StockQuoteService {
    private Endpoint endpoint = null;
    ...
    public StockQuoteService() {
        endpoint = Endpoint.create(new StockQuoteImpl());
    }
    ...
    private void publish() {
        ...
        endpoint.publish("http://localhost:1970/StockQuote/StockQuoteService");
    }
    ...
    public static void main(String[] args) {
        StockQuoteService sqs = new StockQuoteService();
        ...
        sqs.publish();
        System.out.println("StockQuoteService is open for business at http://localhost:1970/StockQuote/StockQuoteService!");
        ...
    }
}
[/prettify]

We can run this class from the command line, or execute the
publish-service target in build.xml, to get
the web service up and running--no servlet container or EJB
container needed.

The web service client is the same, whether the web
service is running in a container or not. Here is a simple one for
illustration purposes:

[prettify]
public class StockQuoteClient {
    public static void main(String[] args) {
        try {
            StockQuote port = new StockQuoteService().getStockQuotePort();
            System.out.println("\nInvoking getQuote():");

            //client code for service published with the default HTTP server
            String ticker1 = "MHP";
            double result1 = port.getQuote(ticker1);
            System.out.printf("The stock price of %s is $%f.\n", ticker1, result1);
            ...
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
[/prettify]

To actually (compile and) invoke the web service with this
client, we need to use the wsimport tool to generate
the artifacts required by the client runtime. But this is a
standard step when developing with JAX-WS RI. Interested readers
may have a look at the target generate-client in
build.xml to see how to accomplish this.

The Ant task supplied in build.xml for invoking the
client is run-client.

Configure the Service Runtime

As demonstrated in the previous section, running a simple web
service in Java SE 6.0 environment is straightforward. Despite this
simplicity, we still have certain control over the web service
runtime. This section will show two examples.

One is to use a customized executor to manage threads handling
client requests. The Endpoint class provides a pair of
methods, getExecutor() and
setExecutor(...), for this purpose.

As an example, we can create an executor that prints out the
execution time for each invocation: TimingTheadPool.java
(see the example application source in the "#">Resources section). The code is adapted from an example from
Java Concurrency in Practice by Brian Goetz. To use this
executor for handling requests to the simple web service we created
in previous section, we only need to add the following lines to the
bootstrap class, StockQuoteService.java:

[prettify]
    private TimingThreadPool executor = new TimingThreadPool();
    ...
    private void publish() {
        endpoint.setExecutor(executor);
        ...
        endpoint.publish("http://localhost:1970/StockQuote/StockQuoteService");
    }
[/prettify]

On my desktop, with the service runtime configured with this
executor, the service-side console prints two lines whenever the
run-client target is run. Here's an example:

[prettify]
     [java] $$$$$$$$$$$$$$$ Runnable com.sun.xml.internal.ws.transport.http.server.WSHttpHandler$HttpHandlerRunnable@160a26f starts in Thread[pool-1-thread-1,5,main].
     [java] $$$$$$$$$$$$$$$ Runnable com.sun.xml.internal.ws.transport.http.server.WSHttpHandler$HttpHandlerRunnable@160a26f ends after 5618744ns.
[/prettify]

The Endpoint class also provides a pair of methods
for working with the javax.xml.ws.Binding interface:
getBinding() and setBinding(...). With
these, we can take advantage of the powerful handler framework in
JAX-WS for web services in the Java SE 6.0 environment.

As an example, we create a SOAPHandler that logs
all SOAP request and response messages--EnvelopeLoggingSOAPHandler.java. To plug this handler into
the service runtime for the example web service, we can add the
following lines in the publish() method of
StockQuoteService.java:

[prettify]
    Binding binding = endpoint.getBinding();
    List<Handler> handlerChain = new LinkedList<Handler>();
    handlerChain.add(new EnvelopeLoggingSOAPHandler());
    binding.setHandlerChain(handlerChain);
[/prettify]

Republishing the service, and invoking the client, we can see
the log messages in the service-side console as follows:

[prettify]
 [java] ------------------------------------
 [java] In SOAPHandler EnvelopeLoggingSOAPHandler:handleMessage()

 [java] direction = inbound

 [java] <?xml version="1.0" ?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
         xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns1="http://service.stockquote.jaxws.company.com/"><soapenv:Body><ns1:getQuote><arg0>MHP</arg0></ns1:getQuote></soapenv:Body></soapenv:Envelope>
 [java] Exiting SOAPHandler EnvelopeLoggingSOAPHandler:handleMessage()
 [java] ------------------------------------
 [java] ------------------------------------
 [java] In SOAPHandler EnvelopeLoggingSOAPHandler:handleMessage()

 [java] direction = outbound

 [java] <?xml version="1.0" ?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
         xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns1="http://service.stockquote.jaxws.company.com/"><soapenv:Body><ns1:getQuoteResponse><return>50.0</return></ns1:getQuoteResponse></soapenv:Body></soapenv:Envelope>
 [java] Exiting SOAPHandler EnvelopeLoggingSOAPHandler:handleMessage()
 [java] ------------------------------------
 [java] ------------------------------------
 [java] In Handler EnvelopeLoggingSOAPHandler:close()
 [java] Exiting Handler EnvelopeLoggingSOAPHandler:close()
 [java] ------------------------------------
[/prettify]

EnvelopingLoggingSOAPHandler.java is taken from the
example application of "http://www.javaworld.com/javaworld/jw-02-2007/jw-02-handler.html">an
article
I wrote recently, and that article provides a
comprehensive discussion of the handler framework in JAX-WS.

With the Binding interface available from the
Endpoint class, we can also configure other aspects of
the service runtime; for example, enabling/disabling MTOM for
SOAPBinding, etc.

Take Control of the Underlying HTTP Server

The class javax.xml.ws.Endpoint is defined in the JAX-WS
2.0 specification, and therefore all implementations should support
it.

In this section, we will demonstrate how we can further take
control of the underlying HTTP server when working with the
Endpoint class in JDK 6.0, which uses a lightweight
HTTP server supplied by Sun. The API for working this server is
defined in the package "http://java.sun.com/javase/6/docs/jre/api/net/httpserver/spec/index.html?com/sun/net/httpserver/package-summary.html">

com.sun.net.httpserver
.

When publishing web services with the Endpoint
class, we can actually provide a customized HTTPServer
instance to support the HTTP communications between clients and the
service. The method in Endpoint that makes this
possible is:

[prettify]
abstract void publish(Object serverContext)
[/prettify]

What is not obvious from this method signature is that the input
parameter serverContext can be a
com.sun.net.httpserver.HTTPContext. With this
knowledge, it is easy to see, by looking at the APIs in
com.sun.net.httpserver, that we can control several
very useful aspects of the HTTP communications for web services
published by the Endpoint class, such as applying
filters and using a customized executor managing threads handling
HTTP requests, or using a customized HTTP handler, etc.

As an example, we will demonstrate how to enable HTTP basic
authentication for web services running in Java SE 6.0.

For this purpose, we will first create a HTTPServer
instance (see the server() method in
StockQuoteService.java):

[prettify]
    private HttpServer server = null;
    ...
    server = HttpServer.create(new InetSocketAddress(1970), 5);
    server.start();
[/prettify]

And, then from this server instance, we can create the
HTTPContext we will use, by providing the context path
(to which request URLs will be translated):

[prettify]
context = server.createContext("/StockQuote/StockQuoteService");
[/prettify]

To enable basic authentication for this HTTP context, we need an
authenticator. An easy way to achieve this is to extend the support
class com.sun.net.httpserver.BasicAuthenticator. The
simple authenticator we will create is
TestBasicAuthenticator.java. It only authenticates the
user yyang with the password #$$1x5Y7z. (This
specific username/password pair is just an example and is of no
significance by itself!)

A single line of code provides basic authentication for the
previously created HTTP context with a
TestBasicAuthenticator instance:

[prettify]
context.setAuthenticator(new TestBasicAuthenticator("test"));
[/prettify]

As the last step, we need to configure the Endpoint
to use our own HTTP server instance and publish the example web
service to the previously created HTTP context.

[prettify]
endpoint.publish(context);
[/prettify]

To run this web service, we can invoke the
start-server target in build.xml.

Running the original client, we will see a message in the client
side console indicating that the request is unauthorized:

[prettify]
    [java] Invoking getQuote():
    [java] javax.xml.ws.WebServiceException: request requires HTTP authentication: Unauthorized
    [java]     at com.sun.xml.internal.ws.util.SOAPConnectionUtil.getSOAPMessage(SOAPConnectionUtil.java:83)
    [java]     at com.sun.xml.internal.ws.encoding.soap.client.SOAPXMLDecoder.toSOAPMessage(SOAPXMLDecoder.java:102)
    [java]     at com.sun.xml.internal.ws.protocol.soap.client.SOAPMessageDispatcher.receive(SOAPMessageDispatcher.java:440)
    ...
[/prettify]

To make the call succeed, we need to modify the client a little
bit so that it can send the user/name password pair. This code
snippet follows the standard approach in JAX-WS for sending client
credentials:

[prettify]
    BindingProvider bp = (BindingProvider)port;
    Map<String,Object> map = bp.getRequestContext();
    map.put(BindingProvider.USERNAME_PROPERTY, "yyang");
    map.put(BindingProvider.PASSWORD_PROPERTY,"#$$1x5Y7z");
    //map.put(BindingProvider.PASSWORD_PROPERTY,"password");
    String ticker2 = "IBM";
    double result2 = port.getQuote(ticker2);
    System.out.printf("The stock price of %s is $%f.\n", ticker2, result2);
[/prettify]

We can also use a different username/password pair other than
yyang/#$$1x5Y7z to see how an authentication failure
behaves.

Conclusion

In use cases that do not require the full power of a commercial
HTTP server and servlet or EJB container, the Endpoint
class provides a convenient mechanism for components in software or
systems in tightly controlled environment to easily communicate
through web services. A second use of this mechanism is to
prototype and to develop production web services to be finally
deployed in Java EE containers. We can also use it to mock or
simulate web services to decouple client development and test from
service delivery. In summary, I believe this tool can be very
helpful for organizations adopting SOA, not only simplifying some
production system configuration, but also facilitating the service
delivery lifecycle.

Resources

width="1" height="1" border="0" alt=" " />
Young Yang works in software, consulting and corporate IT as a tech lead, architect, development and project manager.
Related Topics >> Web Services and XML   |   

Comments

Excellent, thank you very

Excellent, thank you very much. Take this opportunity to mention that unfortunately the link to the source code is broken.

Excelent Post Young

Excelent Post Young Yang...but i have a question. I developed a WebServices with jaxws-spring(org.jvnet.jax-ws-commons.spring) and DataBase comunication, then this WS return some data. So the question is:!!! How can i work with this Endpoint class?. I know this post is very old, but i would like to know what happens in that case Thanks!

Wonderful! the link to the

Wonderful! the link to the source (http://www.java.net/today/2007/07/03/ws_endpoint.zip) is broken. Can you kindly fix that? thanks.