How to implement a SOAP client using JAX-WS Liferay infrastructure
1. Overview
-
Implementation of the calculator-api module. This module defines the APIs that each application can invoke to use the services of arithmetic operations (addition, subtraction, division and multiplication);
-
Implementation of the calculator-service module. This module implements the APIs defined by the calculator-api module and acts as a client to the SOAP Calculator service;
-
Implementation of the calculator-gogo-shell module. This module implements the Gogo Shell commands that use the APIs defined by the calculator-api module to perform the arithmetic operations;
-
Implementation of the calculator-web module. This module implements a standard MVC portlet that uses the APIs defined by the calculator-api module to perform the arithmetic operations.
-
calculator-api
-
Calculator is the interface that defines the arithmetic operations between two integers;
-
-
calculator-service
-
CalculatorClientImpl implements the APIs defined by the Calculator interface and acts as a client to the SOAP Calculator service;
-
-
calculator-web
-
AddOperationMVCActionCommand: MVC Action Command of the Calculator Web Module for the request of the addition operation;
-
-
calculator-gogo-shell
-
CalculatorCommand: Gogo Shell Commands of the Calculator Gogo Shell Module.
-
1 - Implementation of the SOAP client
buildscript { repositories { maven { url "https://plugins.gradle.org/m2/" } } dependencies { classpath "gradle.plugin.cz.swsamuraj:gradle-jaxws-plugin:0.6.1" } } apply plugin: "cz.swsamuraj.jaxws" dependencies { compileOnly group: "com.liferay.portal", name: "com.liferay.portal.kernel", version: "4.4.0" compileOnly group: "org.osgi", name: "org.osgi.service.component.annotations", version: "1.3.0" compileOnly project(":modules:calculator:calculator-api") } jaxws { wsdlDir = 'src/main/resources/META-INF/wsdl' generatedSources = 'generatedsources/src/main/java' }
package it.dontesta.labs.liferay.webservice.calculator.client.soap; ... /** * @author Antonio Musarra */ @Component(immediate = true, property = {}, service = Calculator.class) public class CalculatorClientImpl implements Calculator { @Override public int add(int number1, int number2) throws CalculatorOperationException { try { return _getService().add(number1, number2); } catch (CalculatorServiceException cse) { throw new CalculatorOperationException(cse.getMessage(), cse); } } @Override public int divide(int number1, int number2) throws CalculatorOperationException { try { return _getService().divide(number1, number2); } catch (CalculatorServiceException cse) { throw new CalculatorOperationException(cse.getMessage(), cse); } } @Override public int multiply(int number1, int number2) throws CalculatorOperationException { try { return _getService().multiply(number1, number2); } catch (CalculatorServiceException cse) { throw new CalculatorOperationException(cse.getMessage(), cse); } } @Override public int subtract(int number1, int number2) throws CalculatorOperationException { try { return _getService().subtract(number1, number2); } catch (CalculatorServiceException cse) { throw new CalculatorOperationException(cse.getMessage(), cse); } } private CalculatorSoap _getService() throws CalculatorServiceException { if (Validator.isNull(_calculatorSoap)) { try { org.tempuri.Calculator calculator = new org.tempuri.Calculator(); _calculatorSoap = calculator.getCalculatorSoap(); } catch (WebServiceException wse) { throw new CalculatorServiceException(wse.getMessage(), wse); } return _calculatorSoap; } else { return _calculatorSoap; } } private CalculatorSoap _calculatorSoap; }
From this moment we can use the Calculator service on any module. Who uses the service is absolutely abstract from the fact that the implementation uses external SOAP services.
2 - Implementation of Gogo Shell commands
package it.dontesta.labs.liferay.webservice.calculator.gogoshell; import it.dontesta.labs.liferay.webservice.calculator.api.Calculator; ... /** * @author Antonio Musarra */ @Component( property = { "osgi.command.function=add", "osgi.command.function=divide", "osgi.command.function=multiply", "osgi.command.function=subtract", "osgi.command.scope=calculator" }, service = Object.class ) public class CalculatorCommand implements Calculator { public int add(int number1, int number2) throws CalculatorOperationException { return getCalculatorService().add(number1, number2); } ... @Reference(policyOption = ReferencePolicyOption.GREEDY) private volatile Calculator _calculator; }
Once the module has been deployed, we have the commands to make the arithmetic operations available. Just connect to the Gogo Shell and try.
3 - Implementation of Calculator Web
package it.dontesta.labs.liferay.web.calculator.portlet.action; import it.dontesta.labs.liferay.webservice.calculator.api.Calculator; ... /** * @author Antonio Musarra */ @Component( immediate = true, property = { "javax.portlet.name=" + CalculatorAppPortletKeys.CALCULATOR_SOAP_APP, "mvc.command.name=/calculator/add-operation" }, service = MVCActionCommand.class ) public class AddOperationMVCActionCommand extends BaseMVCActionCommand { @Override protected void doProcessAction( ActionRequest actionRequest, ActionResponse actionResponse) throws Exception { _handleActionCommand(actionRequest); } private void _handleActionCommand(ActionRequest actionRequest) { try { int term1 = ParamUtil.getInteger( actionRequest, CalculatorAppWebKeys.ADD_OPERATION_TERM_1); int term2 = ParamUtil.getInteger( actionRequest, CalculatorAppWebKeys.ADD_OPERATION_TERM_2); int result = _calculator.add(term1, term2); actionRequest.setAttribute( CalculatorAppWebKeys.ADD_OPERATION_RESULT, result); } catch (SOAPFaultException soapfe) { SessionErrors.add(actionRequest, soapfe.getClass(), soapfe); } catch (WebServiceException wse) { SessionErrors.add(actionRequest, wse.getClass(), wse); } catch (Exception e) { SessionErrors.add( actionRequest, "calculatorOperationError", e.getMessage()); } } @Reference(policyOption = ReferencePolicyOption.GREEDY) private Calculator _calculator; }
The following figures show the simple calculator in action.
4 - Configuration of the JAX-WS API and CXF EndPoints
- CXF EndPoints
- JAX-WS API
If we want to be picky, we could run the services "(objectClass=javax.xml.ws.spi.Provider)" command from Gogo Shell to verify the JAX-WS API bridge has been properly configured. You should get it in output when shown in the figure below.
5 - Conclusions
-
The SOAP service we want to use requires an SSL/TLS Mutual authentication
-
The SOAP service we want to access requires WS-Security