[JAX-WS] Simple Dispatch Client Example

Here is an example of a Dispatch type Java SE WebService client.
1. A sample Web Service (Running as a Dynamic Web Application in Glassfish):

package com.vvirlan;
import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.xml.ws.soap.Addressing;
/**
 *
 * @author vladimir
 */
@Addressing(enabled = true, required = true)
@WebService(serviceName = "NewWebService")
public class NewWebService {

    @WebMethod(operationName = "hello")
    public String hello(@WebParam(name = "name") String txt) {
        return "Hello " + txt + " !";
    }
}

2. Here is the client (A simple Java SE application). Make sure to start the previous application first:

package dispatchclient;

import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPBodyElement;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import javax.xml.ws.soap.AddressingFeature;
import javax.xml.ws.soap.SOAPBinding;

/**
 *
 * @author vladimir
 */
public class DispatchClient {

    public static void main(String[] args) {

        QName port = new QName("http://vvirlan.com/", "PortName");
        Service service = Service.create(port);
        service.addPort(port, SOAPBinding.SOAP11HTTP_MTOM_BINDING, "http://localhost:8080/SimpleJaxWs/NewWebService");
        Dispatch<SOAPMessage> dispatch = service.createDispatch(port, SOAPMessage.class, Service.Mode.MESSAGE, new AddressingFeature());
        SOAPBinding binding = (SOAPBinding) dispatch.getBinding();
        
        binding.setMTOMEnabled(true);
        SOAPMessage request = null;
        try {
            request = MessageFactory.newInstance().createMessage();
            SOAPBody reqSoapBody = request.getSOAPBody();
            SOAPElement methodName;
            QName method = new QName("http://vvirlan.com/", "hello", "vvir");
            QName name = new QName("name");

            methodName = reqSoapBody.addChildElement(method);
            SOAPElement nameParam = methodName.addChildElement(name);
            nameParam.addTextNode("Vladimir");

        } catch (SOAPException e) {
            e.printStackTrace();
        }

        SOAPMessage response = dispatch.invoke(request);
        try {
            SOAPBody resSoapBody = response.getSOAPBody();
            System.out.println(resSoapBody.getTextContent());
        } catch (SOAPException e) {
            e.printStackTrace();
        }

    }
}

This will generate a SOAP request like:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:vvir="http://vvirlan.com/">
    <soapenv:Header/>
    <soapenv:Body>
        <vvir:hello>
            <name>Vladimir</name>
        </vvir:hello>
    </soapenv:Body>
</soapenv:Envelope>

And the response will be

<?xml version='1.0' encoding='UTF-8'?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
    <S:Body>
        <ns2:helloResponse xmlns:ns2="http://vvirlan.com/">
            <return>Hello Vladimir !</return>
        </ns2:helloResponse>
    </S:Body>
</S:Envelope>
Advertisements

Low Level SOAP manipulation and JAX-WS invokation (SAAJ)

1. The Client Code

package com.vvirlan;

import java.io.IOException;
import java.net.URL;

import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPBodyElement;
import javax.xml.soap.SOAPConnection;
import javax.xml.soap.SOAPConnectionFactory;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
public class MessageFactoryExample {
	public static void main(String[] args) throws SOAPException, IOException {
		MessageFactoryExample ex = new MessageFactoryExample();
		ex.run();
	}
	private void run() throws SOAPException, IOException {
		MessageFactory factroy = MessageFactory.newInstance();
		SOAPMessage message = factroy.createMessage();
		SOAPPart soapPart = message.getSOAPPart();
		SOAPEnvelope envelope = soapPart.getEnvelope();
		SOAPHeader header = envelope.getHeader();
		SOAPBody body = envelope.getBody();
		//header.detachNode();
		QName bodyName = new QName("http://vvirlan.com/", "sayHello", "PRE");
		SOAPBodyElement bodyElement = body.addBodyElement(bodyName);
		QName name = new QName("arg0");
		SOAPElement arg0 = bodyElement.addChildElement(name);
		arg0.addTextNode("Vlad");
		message.writeTo(System.out);
		System.out.println();
		SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();
		SOAPConnection soapConnection = soapConnectionFactory.createConnection();
		URL endpoint = new URL("http://localhost:8080/JaxWsServiceExample/MyCalculatorService");
		SOAPMessage response = soapConnection.call(message, endpoint);
		response.writeTo(System.out);
soapConnection.close();
	}
}

2. The server (a very simple Dynamic Web App in eclipse running on Glassfish):

package com.vvirlan;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService
public class MyCalculator {
	@WebMethod
	public String sayHello(String msg) {
		return "Hi" + msg;
	}
}

3. Invokation results:
3.1. Request

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
	<SOAP-ENV:Header />
	<SOAP-ENV:Body>
		<PRE:sayHello xmlns:PRE="http://vvirlan.com/">
			<arg0>Vlad</arg0>
		</PRE:sayHello>
	</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

3.2. Response

<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
	<S:Body>
		<ns2:sayHelloResponse xmlns:ns2="http://vvirlan.com/">
			<return>HiVlad</return>
		</ns2:sayHelloResponse>
	</S:Body>
</S:Envelope>

[JAX-WS] Basic Authnetication example (Glassfish) and the client

This example will demonstrate how to configure and use Container provided BASIC authentication for a JAX-WS web service. To note that this is the real 🙂 BASIC authentication.

There are 3 parts:

I. Configure Glassfish realms
II. Implement the actual web service class and configure the deployment descriptor
III. Test authentication from SoapUI
IV. Java Client

I. Configure Glassfish realms
In order to define the realms in Glassfish, it is necessary to do 2 things:
I.1. Enable Default Principal To Role Mapping
Go to the glassfish console: localhost:4848 And open:
Configurations->Server Config -> Security and set enabled for Default Principal To Role Mapping as shown in the picture

1

I.2. Define the file based realm user
Go to Security -> Realms -> file and click on Manage Users as shown in the picture
2

Then define your user name and password. And most importantly type into Group List the value USER. See picture

3

Now you have your user acv with password acv and who is in the Group USER.

II. Implement the actual web service class and configure the deployment descriptor
II.1. Create a new Dynamic web project in Eclipse and set Glassfish as the target runtime. Don’t forget to select create web.xml on the last step of the wizard

II.2. Create a new java class inside a package. Note the user of the @RolesAllowed annotation which should have the value of USER, the very one that you have just defined in the server configuration:

package com.vvirlan;
import javax.annotation.security.RolesAllowed;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService
public class Main {
	@WebMethod
	@RolesAllowed("USER")
	public String sayHello() {
		return "Hello";
	}
	@WebMethod
	@RolesAllowed("USER")
	public String sayNothing() {
		return "";
	}
}

II.3 Edit web.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
	id="WebApp_ID" version="3.1">
	<display-name>JaxWsBasicSecurity</display-name>
	<servlet>
		<servlet-name>Main</servlet-name>
		<servlet-class>com.vvirlan.Main</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>Main</servlet-name>
		<url-pattern>/user</url-pattern>
	</servlet-mapping>
	<security-constraint>
		<web-resource-collection>
			<web-resource-name>WRCollection</web-resource-name>
			<url-pattern>/user</url-pattern>
			<http-method>GET</http-method>
			<http-method>POST</http-method>
		</web-resource-collection>
		<auth-constraint>
			<role-name>USER</role-name>
		</auth-constraint>
		<user-data-constraint>
			<transport-guarantee>NONE</transport-guarantee>
		</user-data-constraint>
	</security-constraint>
	<login-config>
		<auth-method>BASIC</auth-method>
	</login-config>
	<security-role>
		<role-name>USER</role-name>
	</security-role>
</web-app>

So what we do here. First we define a servlet and point it to our class: Main. Next we map this servlet called Main to a url pattern. From now on our web service will be available not at the usual MainService, but at the url /user. Now we define the security constraint and instruct the container to require authentication for GET and POST request for the path /user. Next we define the role name USER again and set auth-method to BASIC. Finally we again use the name USER to declare the security role.

III. Run the application and ideally you should be able to open it in the browser and see a BASIC authentication form requiring username and password. Type acv and acv and you should see the endpoint. Somewhere here:
http://localhost:8080/JaxWsBasicSecurity/user

To test in SoapUI:
III.1 Create a new SOAP project
III.2 Paste in the WSDL URL: http://localhost:8080/JaxWsBasicSecurity/user?wsdl
III.3 You will be required to type username and password put twice acv and acv (in 2 forms)
III.4 Now trying to run a request you need to define Auth as in the picture
4
Clicking on play should give you the response.

IV. Java Client
IV.1. The first step is of course wsimport. But you have authentication enabled so you need to do this: Create a file called myXauthFile.txt in tmp folder (anywhere you like). Set the content to:

http://acv:acv@localhost:8080/JaxWsBasicSecurity/user?wsdl

IV.2 Run wsimport from the tmp folder:

C:\tmp\wsbasic>wsimport -keep -Xauthfile myXauthFile.txt http://localhost:8080/JaxWsBasicSecurity/user?wsdl

You should get the client artifacts.
IV.2 Copy your newly generated artifacts into eclipse in your client project
IV.3 Implement the Client class:

package com.vvirlan;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
public class TheClient {
	public static void main(String[] args) {
		TheClient cl = new TheClient();
		cl.run();
	}
	public void run() {
		Authenticator myAuth = new Authenticator() {
			@Override
			protected PasswordAuthentication getPasswordAuthentication() {
				return new PasswordAuthentication("acv", "acv".toCharArray());
			}
		};
		Authenticator.setDefault(myAuth);
		MainService service = new MainService();
		Main port = service.getMainPort();
		// This would work, but you will get a 401 error on MainService service
		// = new MainService();
		// BindingProvider bp = (BindingProvider) port;
		// bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "acv");
		// bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "acv");
		String result = port.sayHello();
		System.out.println(result);
	}
}

Troubleshooting.
In case you get 403, try setting the server realms for both default-config and server-config. Also try to set the password again in glassfish. After this you need to restart the server and redeploy the app.

[JAX-WS] 3 ways to enable MTOM in JAX-WS on the server side

1. Enable using @javax.xml.ws.soap.MTOM annotation on the endpoint implementation class

@javax.xml.ws.soap.MTOM
@WebService(endpointInterface = "mtom.server.Hello")
public class HelloImpl implements Hello {
// ...
}

2. MTOM can be also be enabled on an endpoint by specifying enable-mtom attribute to true on an endpoint
element in sun-jaxws.xml deployment descriptor.

<?xml version="1.0" encoding="UTF-8"?>
<endpoints xmlns='http://java.sun.com/xml/ns/jax-ws/ri/runtime'
version='2.0'>
<endpoint name="Mtom"
implementation="mtom.server.HelloImpl"
url-pattern="/hello"
enable-mtom="true"/>
</endpoints>

3. Enable using @BindingType on the endpoint (SEI) implementation class

@BindingType(value=javax.xml.ws.soap.SOAPBinding.SOAP11HTTP_MTOM_BINDING)

will enable MTOM on the deployed endpoint for SOAP 1.1 binding

@BindingType(value=javax.xml.ws.soap.SOAPBinding.SOAP12HTTP_MTOM_BINDING)

will enable MTOM on the deployed endpoint for SOAP 1.2 binding

[JAX-WS] (Metro) 3 ways to customize package in WSDL

By default wscompile generates WSDL artifacts in a package computed from the WSDL targetNamespace. For example, a WSDL file with the targetNamespace http://duke.example.org without any package customization will be mapped to the org.duke package. To customize the default package mapping you would use a jaxws:package customization on the wsdl:definitions node or it can directly appear inside the top level bindings element.

1. Bindings

<bindings xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
        wsdlLocation="http://localhost:8080/jaxws-external-customize/addnumbers?WSDL"
        xmlns="http://java.sun.com/xml/ns/jaxws">
    <package name="external_customize.client">
        <javadoc>Mathutil package</javadoc>
    </package>
</bindings>

2. Bindings / bindings

<bindings xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
        wsdlLocation="http://localhost:8080/jaxws-external-customize/addnumbers?WSDL"
        xmlns="http://java.sun.com/xml/ns/jaxws">
    <bindings node="wsdl:definitions">
        <package name="external_customize.client">
            <javadoc>Mathutil package</javadoc>
        </package>
        
        ...
        
    </bindings>
    
    ...
    
</bindings>

3. By using the -p command line option in wsimport
-p option on commandline wsimport.sh tool (package attribute on wsimport ant task), overrides the jaxws:package customization,it also overrides the schema package customization specified using jaxb schema customization.

[JAX-WS] Simple webservice and client

1. Create a new Dynamic Web Project LibraryWS.
2. Create a package webservice
3. Create a class BookWebService

package webservice;
import java.util.ArrayList;
import java.util.List;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService
public class BookWebService {
	List<Book> allBooks = new ArrayList<>();
	@WebMethod
	public List<Book> getAllBooks() {
		return allBooks;
	}
	@WebMethod
	public void addBook (Book book) {
		this.allBooks.add(book);
	}
}

4. Create a class Book

package webservice;
public class Book {
	private String title;
	private int pages;
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public int getPages() {
		return pages;
	}
	public void setPages(int pages) {
		this.pages = pages;
	}
	@Override
	public String toString() {
		return "Book [title=" + title + ", pages=" + pages + "]";
	}
}

5. Run on Server (Glassfish!).
6. You should be able to access the service here:
http://localhost:8080/LibraryWs/BookWebServiceService
7. Go to http://localhost:8080/LibraryWs/BookWebServiceService?wsdl for the wsdl (copy this URL)
8. Create a new Simple Java Project LibraryClient
9. Go to command line to a tmp folder and type:

wsimport -keep http://localhost:8080/LibraryWs/BookWebServiceService?wsdl

10. Copy the generated folder webservice into eclipse under src so that you get the package webservice
11. Create the MainClient class in eclipse:

package root;
import java.util.List;
import javax.xml.ws.WebServiceRef;
import webservice.Book;
import webservice.BookWebService;
import webservice.BookWebServiceService;
public class MainClient {
	@WebServiceRef(wsdlLocation="http://localhost:8080/LibraryWs/BookWebServiceService?wsdl")
	private static BookWebServiceService service;
	public static void main(String[] args) {
		MainClient cl = new MainClient();
		cl.start();
	}
	public void start() {
		service = new BookWebServiceService();
		BookWebService port = service.getBookWebServicePort();
		System.out.println("All books: "+port.getAllBooks());
		Book book = new Book();
		book.setTitle("Journey");
		book.setPages(233);
		port.addBook(book);
		System.out.println("All books: "+port.getAllBooks());
	}
}

[JAX-WS] @HandlerChain example with unit test

JAX-WS @WebService component use a handler chain to alter incoming and outgoing SOAP messages. SOAP Handlers are similar to Servlet Filters or EJB/CDI Interceptors.

1. Project Layout (you can create an example WebService project)
layout

2. LogService interface (this is the interface for our web service)

package org.me.calculator;
import javax.jws.WebParam;
import javax.jws.WebService;

@WebService
public interface LogService {
    int multiply(int mul1, int mul2);
    int sum(@WebParam(name = "add0") int add1, @WebParam(name = "add1") int add2);
}

3. The implementing class

package org.me.calculator;

import javax.ejb.Singleton;
import javax.jws.HandlerChain;
import javax.jws.WebParam;
import javax.jws.WebService;

@Singleton
@WebService (name="LogService", portName="LogPort", serviceName="LogWebService")
@HandlerChain (file = "handlers.xml")
public class LogServiceImpl implements LogService {
    /**
     * Web service operation
     * @param add1
     * @param add2
     * @return 
     */
    @Override
    public int sum(@WebParam(name = "add0") int add1, @WebParam(name = "add1") int add2) {
        return add1 + add2;
    }
    
    @Override
    public int multiply(int mul1, int mul2) {
        return mul1 * mul2;
    }
}

4. Handlers.xml (in the same package)

<?xml version="1.0" encoding="UTF-8"?>
<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
<handler-chain>
    <handler>
        <handler-name>org.me.calculator.Inflate</handler-name>
        <handler-class>org.me.calculator.Inflate</handler-class>
    </handler>
    <handler>
        <handler-name>org.me.calculator.Increment</handler-name>
        <handler-class>org.me.calculator.Increment</handler-class>
    </handler>
</handler-chain>
</handler-chains>

5. One of the SOAPHandlers (Inflate)

package org.me.calculator;

import java.util.Collections;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import org.w3c.dom.Node;

public class Inflate implements SOAPHandler<SOAPMessageContext> {

    @Override
    public Set<QName> getHeaders() {
        return Collections.emptySet();
    }

    @Override
    public boolean handleMessage(SOAPMessageContext context) {
        try {
            final SOAPMessage message = context.getMessage();
            final SOAPBody body = message.getSOAPBody();
            final String localName = body.getFirstChild().getLocalName();

            if ("sumResponse".equals(localName) || "multiplyResponse".equals(localName)) {
                final Node responseNode = body.getFirstChild();
                final Node returnNode = responseNode.getFirstChild();
                final Node intNode = returnNode.getFirstChild();
                final int value = new Integer(intNode.getNodeValue());
                intNode.setNodeValue(Integer.toString(value * 1000));
            }
            return true;
        } catch (SOAPException ex) {
            return false;
        }
    }

    @Override
    public boolean handleFault(SOAPMessageContext context) {
        return true;
    }

    @Override
    public void close(MessageContext context) {
        
    }
}

6. The other SOAPHandler (Increment)

package org.me.calculator;


import java.util.Collections;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import org.w3c.dom.Node;

public class Increment implements SOAPHandler<SOAPMessageContext> {

    @Override
    public Set<QName> getHeaders() {
        return Collections.emptySet();
    }

    @Override
    public boolean handleMessage(SOAPMessageContext context) {
        try {
            final SOAPMessage message = context.getMessage();
            final SOAPBody body = message.getSOAPBody();
            final String localName = body.getFirstChild().getLocalName();

            if ("sumResponse".equals(localName) || "multiplyResponse".equals(localName)) {
                final Node responseNode = body.getFirstChild();
                final Node returnNode = responseNode.getFirstChild();
                final Node intNode = returnNode.getFirstChild();
                final int value = new Integer(intNode.getNodeValue());
                intNode.setNodeValue(Integer.toString(value + 999));
            }
            return true;
        } catch (SOAPException ex) {
            return false;
        }
    }

    @Override
    public boolean handleFault(SOAPMessageContext context) {
        return true;
    }

    @Override
    public void close(MessageContext context) {
        
    }
}

7. And the unit test

package org.me.calculator;

import java.net.URL;
import java.util.Properties;
import javax.ejb.embeddable.EJBContainer;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;


public class NewEmptyJUnitTest {
    
    public NewEmptyJUnitTest() {
    }
    
    @BeforeClass
    public static void setUpClass() {
        Properties properties = new Properties();
        properties.setProperty("openejb.embedded.remotable", "true");
        EJBContainer.createEJBContainer(properties);
    }
    
    @AfterClass
    public static void tearDownClass() {
    }
    
    @Before
    public void setUp() {
    }
    
    @After
    public void tearDown() {
    }
   
     @Test
     public void hello() throws Exception{
         final Service calculatorService = Service.create(
                 new URL("http://localhost:8080/CalculatorApp/LogWebService?WSDL"),
                 new QName("http://calculator.me.org/", "LogWebService"));
         assertNotNull(calculatorService);
         final LogService calculator = calculatorService.getPort(LogService.class);
         
         assertEquals(10999, calculator.sum(4,6));
         assertEquals(12999, calculator.multiply(3, 4));

     }
}