Upgrade to Axis2.0 for client and server
communication:
Considering existing
application is based on Axis1 which you plan to upgrade to Axis2. Axis2 provide
benefits in area of speed in processing and low memory footprints while
serialization and deserialization.
Following are the server and client side changes required for
migration to Axis 2:
Server side changes:
In order to expose ejb
as web services we need to add all the jar provided with Axis2 distribution to
lib folder under WEB-INF and also need to create a new folder with name services- WEB-INF
+ lib ----------- containing Axis2 jars
- services
- MyServiceEjb
- META-INF
services.xml ----------- this contain the actual the details of exposing our services
- conf
axis2.xml
Sample services.xml for exposing MyServiceEJB
<serviceGroup>
<service name="MyService" >
<description>MyServiceEJB</description>
<messageReceivers>
<messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-only"
class="org.apache.axis2.rpc.receivers.ejb.EJBInOnlyMessageReceiver"/>
<messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-out"
class="org.apache.axis2.rpc.receivers.ejb.EJBMessageReceiver"/>
</messageReceivers>
<parameter name="ServiceClass">com.MyServiceEjbLocal</parameter>
<parameter name="localInterfaceName">com.MyServcieEjbLocal</parameter>
<parameter name="localHomeInterfaceName">com.MyServcieEjbHome</parameter>
<parameter name="beanJndiName">ejb:MyService.MyServiceEjbHome</parameter>
</service>
</serviceGroup>
And some changes required in axis2.xml which need to be placed in WEB-INF/conf folder
<parameter name="servicePath">services/MyServices</parameter>
This parameter specifies service url on which our services are exposed
Handlers
For handlers Axis2 has introduces a new concept of modules which is similar to handlers what we already have in Axis1. Now instead of extending AbstractHandler we will be using BasicHandler
Sample Example
public
class AuthenticateHandler extends BasicHandler{
@Override
public
void invoke(MessageContext messageContext) throws AxisFault {
//
TODO Auto-generated method stub
System.out.println("YourLogic");
messageContext.getRequestMessage().getSOAPEnvelope()
.getHeaderByName("username","user");
}
}
@Override
public
void invoke(MessageContext messageContext) throws AxisFault {
//
TODO Auto-generated method stub
System.out.println("YourLogic");
messageContext.getRequestMessage().getSOAPEnvelope()
.getHeaderByName("username","user");
}
}
After this we need to add an entry in modules.xml which provides information of handler under
WEB-INF/modules/Authenticate/modules.xml
Logging is also done through handler.
Sample modules.xml for Authentication handler
<module name="Authenticate" class="handler.AuthenticateModule">
<inflow>
<handler name="InFlowAuthenticationHandler" class="handler.AuthenticationHandler">
<order phase="AuthenticationPhase"/>
</handler>
</inflow>
<outflow>
<handler name="OutFlowAuthenticationHandler" class="handler.AuthenticationHandler">
<order phase="AuthenticationPhase"/>
</handler>
</outflow>
<Outfaultflow>
<handler name="FaultOutFlowAuthenticationHandler" class="handler.AuthenticationHandler">
<order phase="AuthenticationPhase"/>
</handler>
</Outfaultflow>
<INfaultflow>
<handler name="FaultInFlowAuthenticationHandler" class="handler.AuthenticationHandler">
<order phase="AuthenticationPhase"/>
</handler>
</INfaultflow>
</module>
Add this phase(AuthenticationPhase) in axis2.xml after that this phase is available to be engaged in service, now an entry is made in services.xml for which service we want to add this module.
<phase name="AuthenticationPhase"/> --------in axis2.xml
<module ref="Authenticate"/> --------in services.xml
Logging is also done through handler.
Sample modules.xml for Authentication handler
<module name="Authenticate" class="handler.AuthenticateModule">
<inflow>
<handler name="InFlowAuthenticationHandler" class="handler.AuthenticationHandler">
<order phase="AuthenticationPhase"/>
</handler>
</inflow>
<outflow>
<handler name="OutFlowAuthenticationHandler" class="handler.AuthenticationHandler">
<order phase="AuthenticationPhase"/>
</handler>
</outflow>
<Outfaultflow>
<handler name="FaultOutFlowAuthenticationHandler" class="handler.AuthenticationHandler">
<order phase="AuthenticationPhase"/>
</handler>
</Outfaultflow>
<INfaultflow>
<handler name="FaultInFlowAuthenticationHandler" class="handler.AuthenticationHandler">
<order phase="AuthenticationPhase"/>
</handler>
</INfaultflow>
</module>
Add this phase(AuthenticationPhase) in axis2.xml after that this phase is available to be engaged in service, now an entry is made in services.xml for which service we want to add this module.
<phase name="AuthenticationPhase"/> --------in axis2.xml
<module ref="Authenticate"/> --------in services.xml
· Care needs to be taken for beans because for
axis2 we should have a getter if we have a setter implemented in the bean;
otherwise we get a generic error message which can be mistaken for a jar
conflict kind of an issue.
· Collection frame work is not supported; we
need to refactor such cases.
· Overloaded methods are not supported in axis2,
need to refactor such method.
· java.lang.Object is not supported in axis2, we
should be transferring proper data type.
With these changes we are done with exposing our services.
List of Services:
And unlike Axis1 where we we get the list of all
the services exposed automatically when we sent a request to localhost:8080/services/MyServices,
we are required to handle this explicitly in our code in Axis2 by implementing
a servlet which will display the list.
axisConfig = ConfigurationContextFactory.createConfigurationContextFromFileSystem(PATH_TO_WEB-INF, PATH_TO_AXIS2.XML).getAxisConfiguration();
StringBuffer output = new StringBuffer("<html><body><h1>Available Services:</h1>");
int index = 1;
for (Iterator iter = axisConfig.getServices().values().iterator(); iter.hasNext();){
AxisService service = (AxisService) iter.next();
output.append("<p>");
output.append(index).append(". ");
output.append("<a href=\"" + "Services/MyServices/" + service.getName() + "?wsdl\">");
output.append(service.getName());
output.append("</a>");
for(Iterator iter1 = service.getOperations(); iter1.hasNext();){
AxisOperation a = (AxisOperation)iter1.next();
output.append("<BR>"+a.getName());
}
output.append("<p/>");
}
output.append("</body></html>");
axisConfig = ConfigurationContextFactory.createConfigurationContextFromFileSystem(PATH_TO_WEB-INF, PATH_TO_AXIS2.XML).getAxisConfiguration();
StringBuffer output = new StringBuffer("<html><body><h1>Available Services:</h1>");
int index = 1;
for (Iterator iter = axisConfig.getServices().values().iterator(); iter.hasNext();){
AxisService service = (AxisService) iter.next();
output.append("<p>");
output.append(index).append(". ");
output.append("<a href=\"" + "Services/MyServices/" + service.getName() + "?wsdl\">");
output.append(service.getName());
output.append("</a>");
for(Iterator iter1 = service.getOperations(); iter1.hasNext();){
AxisOperation a = (AxisOperation)iter1.next();
output.append("<BR>"+a.getName());
}
output.append("<p/>");
}
output.append("</body></html>");
Client side changes
For generating client stubs axis2 supports 4
binding techniques
1. adb (axis2 data binding)
2. XmlBeans
3. Jibx
4. Jaxbri
For adb we can create stubs through wsdl2java tools provided by axis2
wsdl2java -oD:\workSpace\YourProjectClient\ -S src\ -R resource\ -d adb -uw -u -sp -or --noBuildXML --noWSDL -urihttp://localhost:8080/services/MyServices/MyServices?wsdl
1. adb (axis2 data binding)
2. XmlBeans
3. Jibx
4. Jaxbri
For adb we can create stubs through wsdl2java tools provided by axis2
wsdl2java -oD:\workSpace\YourProjectClient\ -S src\ -R resource\ -d adb -uw -u -sp -or --noBuildXML --noWSDL -urihttp://localhost:8080/services/MyServices/MyServices?wsdl
Short
Option
|
Long
Option
|
Description
|
-uri
<Location of WSDL>
|
None
|
WSDL
file location. This should point to a WSDL file in the local file system.
|
-o <output Location>
|
--output
<output Location>
|
Output
file location. This is where the files would be copied once the code
generation is done. If this option is omitted the generated files would be
copied to the working directory.
|
-l
<language>
|
--language
<language>
|
Output
language. Currently the code generator can generate code in Java but it has
the ability to be extended to support other languages.
|
-p
<package name>
|
--package
<package name>
|
The
target package name. If omitted, a default package (formed using the target
namespace of the WSDL) will be used.
|
-a
|
--async
|
Generate
code only for async style. When this option is used the generated stubs will
have only the asynchronous invocation methods. Switched off by default.
|
-s
|
--sync
|
Generate
code only for sync style . When this option is used the generated stubs will
have only the synchronous invocation methods. Switched off by default. When
used with the -a option, this takes precedence.
|
-t
|
--test-case
|
Generates
a test case. In the case of Java it would be a JUnit test case.
|
-ss
|
--server-side
|
Generates
server side code (i.e. skeletons). Default is off.
|
-sd
|
--service-description
|
Generates
the service descriptor (i.e. server.xml). Default is off. Only valid with
-ss, the server side code generation option.
|
-d
<databinding>
|
--databinding-method
<databinding>
|
Specifies
the Databinding framework. Valid values are xmlbeans, adb, jibx, and none.
Default is adb.
|
-g
|
--generate-all
|
Generates
all the classes. This option is valid only with the -ss (server side code
generation) option. When on, the client code (stubs) will also be generated
along with the skeleton.
|
-u
|
--unpack-classes
|
Unpack
classes. This option specifies whether to unpack the classes and generate
separate classes for the databinders.
|
-sn
<service name>
|
--service-name
<service name>
|
Specifies
the service name to be code generated. If the service name is not specified,
then the first service will be picked.
|
-pn
<port name>
|
--port-name
<port name>
|
Specifies
the port name to be code generated. If the port name is not specified, then
the first port (of the selected service) will be picked.
|
-ns2p
|
--namespace2package
|
Specifies
a comma separated list of namespaces and packages where the given package will
be used in the place of the auto generated package for the relevant
namespace. The list will be the format of ns1=pkg1,ns2=pkg2.
|
-ssi
|
--serverside-interface
|
Generate
an interface for the service skeleton.
|
-wv
|
--wsdl-version
|
WSDL
Version. Valid Options : 2, 2.0, 1.1
|
-S
|
--source-folder
|
Specify
a directory path for generated source
|
-R
|
--resource-folder
|
Specify
a directory path for generated resources
|
-em
|
--external-mapping
|
Specify
an external mapping file
|
-f
|
--flatten-files
|
Flattens
the generated files
|
-uw
|
--unwrap-params
|
Switch
on un-wrapping
|
-xsdconfig
|
Use
XMLBeans .xsdconfig file. Valid only with -d xmlbeans
|
|
-ap
|
--all-ports
|
Generate
code for all ports
|
-or
|
--over-ride
|
Overwrite
the existing classes
|
-b
|
--backword-compatible
|
Generate
Axis 1.x backward compatible code
|
-sp
|
--suppress-prefixes
|
Suppress
namespace prefixes (Optimization that reduces size of soap request/response)
|
--noBuildXML
|
Don't
generate the build.xml in the output directory
|
|
--noWSDL
|
Don't
generate WSDL's in the resources directory
|
|
--noMessageReceiver
|
Don't
generate a MessageReceiver in the generated sources
|
The stubs generated are different from Axis1 stubs, following are the differences compared to Axis2 stubs:
1.
Unlike Axis1 we cannot
control package structure for beans it will be similar to what we have on
server side.
2.
The custom exception
from the application will get wrapped inside another class which has its name
as SERVICE_NAME+YourAppException
a.
eg. for MyException it
was throwing MyServiceMyException
3.
This resulted in
different exception for different services
Code on client side for axis2.
try{
MyServiceStub d = new MyServiceStub();
System.out.println(d.helloWorld());
System.out.println(d.say("hi"));
}
catch (MyServiceMyException e) {
e.printStackTrace();
e.getFaultMessage().getMyException().getErrorMsg();
}
And as XmlBeans does not support unwrapped format of stubs generation this binding technique results in putting extra efforts while invoking the service methods. For eg. “say” is a method in MyServiceStub in order to call this method we needed to instantiate this
and set parameters in this if our method requires parameter
MyServiceStub d = new MyServiceStub();
com.Say k = new Say();---------- method instantiation
k.setArgs0("hi");---------- setting parameter
System.out.println(d.say(k).get_return());---------- retrieving return type
Following is the summarized list for changes required to make the server and client axis 2 communication possible:
1.
Above
mentioned steps.
2.
Refactoring
all client exception handling code.
Using ADB databinding with the changes as suggested above server client communication is achieved.
Issue related to exception for which we tried
different approaches.
Issues:
Different alternatives that were tried on client
side in order to get the exception same as on Server side
1. As this issue was already reported in axis2 https://issues.apache.org/jira/browse/AXIS2-3262, so we implemented a work around as suggested in this jira for this we implemented a handler which intercepts every request and response and checks if exception has been thrown or not from server side. In case of exception it unwraps exception and extracts the error code and message and then throws it as MyException.
public InvocationResponse invoke(MessageContext msgContext) throws AxisFault {
if (msgContext.isFault()) {
org.apache.axiom.soap.SOAPFactory fac =
msgContext.isSOAP11() ? OMAbstractFactory.getSOAP11Factory() : OMAbstractFactory
.getSOAP12Factory();
System.out.println();
org.apache.axiom.soap.SOAPFaultCode PlaceNewLocalNameHere = msgContext.getEnvelope().getBody().getFault().getCode();
msgContext.setProperty(SOAP12Constants.SOAP_FAULT_CODE_LOCAL_NAME, PlaceNewLocalNameHere);
SOAPFaultReason PlaceNewReasonHere = msgContext.getEnvelope().getBody().getFault().getReason();
msgContext.setProperty(SOAP12Constants.SOAP_FAULT_REASON_LOCAL_NAME, PlaceNewReasonHere);
org.apache.axiom.soap.SOAPFaultDetail PlaceNewDetailHere = msgContext.getEnvelope().getBody().getFault().getDetail();
msgContext.setProperty(SOAP12Constants.SOAP_FAULT_DETAIL_LOCAL_NAME, PlaceNewDetailHere);
throw new MyException(1 , msgContext.getFailureReason().getMessage());
}
return InvocationResponse.CONTINUE;
}
And then we need to add following piece of code before instantiating stub and need to pass this configuration context to it.
ConfigurationContext c = ConfigurationContextFactory.createConfigurationContextFromFileSystem("src\\client\\FaultHandler.java", "src\\conf\\axis2.xml");
List phases_list = c.getAxisConfiguration().getInFlowPhases();
Phase phaseOne=(Phase) phases_list.get(0);
phaseOne.addHandler(new FaultHandler()); ------- this is the handler which converts exception
phases_list.add(phaseOne);
c.getAxisConfiguration().setInFaultPhases(phases_list);
MyServiceStub d = new MyServiceStub(c);
But limitation with this approach are:
a) We are still have to catch MyServiceMyException as the stubs have methods which throw this exception.
b) We need to create MyException class on client side similar to server side separately as generated stubs do not have this as exception.
c) We are not forced to handle MyException since this is not an exception being thrown from the method itself so it gets treated as runtime exception.
try{
MyServiceStub d = new MyServiceStub(c);
System.out.println(d.helloWorld());
System.out.println(d.say("hi"));
}
catch (MyServiceMyException e) { --------- we are forced to catch this
} catch (MyException e) { ----------need to create MyException class on Client side
e.printStackTrace();
}
2. We also tried contract first approach where we converted the axis1 web services from rpc-encoded to document literals style by making changes in server-config.wsdd
<service name="MyServices/MyServices" provider="java:EJB" style="document" use="literal">
<parameter name="allowedMethods" value="*"/>
<parameter name="localInterfaceName" value="com.MyServicelEjbLocal"/>
<parameter name="localHomeInterfaceName" value="com.MyServiceEjbHome"/>
<parameter name="beanJndiName" value=" ejb:MyService.MyServiceEjbHome "/>
<beanMapping languageSpecificType="java:exception.MyException" qname="common:MyException"/>
<typeMapping
qname="common:MyException"
languageSpecificType="java:exception.MyException"
serializer="org.apache.axis.encoding.ser.ArraySerializerFactory"
deserializer="org.apache.axis.encoding.ser.ArrayDeserializerFactory"/>
</service>
and created server side stubs from the generated wsdl. When we exposed our service again with Axis2 we were still getting wrapped exception.
3. We tried Jibx data binding technique which required binding.xml along with wsdl which provides details of the beans and the exception structure that we are using in our service and this will be used while creating stubs.
For this we required jibx jar and used a tool to create binding.xml provided by jibx
org.jibx.binding.generator.BindGen
D:\workSpace\testProject>java -cp "D:\jibx\lib\*";"D:\project\lib\*" org.jibx.binding.generator.BindGenexception.MyException
sample binding.xml
<binding name="binding" package="exception">
<mapping abstract="true" type-name="MyException" class="exception.MyException">
<value style="element" name="errorCode" field="errorCode" usage="optional"/>
<value style="element" name="errorMessage" field="errorMessage " usage="optional"/>
</mapping>
<mapping class="exception.MyException" name="MyException">
<structure map-as="MyException"/>
</mapping>
</binding>
And while creating stubs we will require to provide this binding.xml
wsdl2java -o D:\workSpace\testProjectClient\ -S src\ -R resource\ -d jibx -Ebindingfile D:\binding.xml-uw -u -sp -or --noBuildXML --noWSDL -uri http://localhost:8080/services/MyServices /MyService?wsdl
With this binding technique also we were facing same wrapped exception issue.
4. We also tried one more suggested approach with ADB data binding which required changes to be made in axis2-adb-codegen.jar which is provided with other axis2 jar.In this approach we edited schema-compiler.properties file which determines bean generation template.
(A bean writer provider
# may choose not to use a template at all!) However the property loader will
# load the following templates (reference) and provide it and in the case when the
# bean writer is not statically bound to a template, this'll be useful.
#
schema.bean.writer.template=/org/apache/axis2/schema/template/ADBBeanTemplate.xsl
# schema.bean.writer.template=/org/apache/axis2/schema/template/PlainBeanTemplate.xsl
# The type map to be used by the schema compiler
#
schema.bean.typemap=org.apache.axis2.schema.typemap.JavaTypeMap
But this thing resulted in only plain bean instead of ADB bean.
Exception handling mechanism of Axis2 we will require handling exception separately for every service.
1. As this issue was already reported in axis2 https://issues.apache.org/jira/browse/AXIS2-3262, so we implemented a work around as suggested in this jira for this we implemented a handler which intercepts every request and response and checks if exception has been thrown or not from server side. In case of exception it unwraps exception and extracts the error code and message and then throws it as MyException.
public InvocationResponse invoke(MessageContext msgContext) throws AxisFault {
if (msgContext.isFault()) {
org.apache.axiom.soap.SOAPFactory fac =
msgContext.isSOAP11() ? OMAbstractFactory.getSOAP11Factory() : OMAbstractFactory
.getSOAP12Factory();
System.out.println();
org.apache.axiom.soap.SOAPFaultCode PlaceNewLocalNameHere = msgContext.getEnvelope().getBody().getFault().getCode();
msgContext.setProperty(SOAP12Constants.SOAP_FAULT_CODE_LOCAL_NAME, PlaceNewLocalNameHere);
SOAPFaultReason PlaceNewReasonHere = msgContext.getEnvelope().getBody().getFault().getReason();
msgContext.setProperty(SOAP12Constants.SOAP_FAULT_REASON_LOCAL_NAME, PlaceNewReasonHere);
org.apache.axiom.soap.SOAPFaultDetail PlaceNewDetailHere = msgContext.getEnvelope().getBody().getFault().getDetail();
msgContext.setProperty(SOAP12Constants.SOAP_FAULT_DETAIL_LOCAL_NAME, PlaceNewDetailHere);
throw new MyException(1 , msgContext.getFailureReason().getMessage());
}
return InvocationResponse.CONTINUE;
}
And then we need to add following piece of code before instantiating stub and need to pass this configuration context to it.
ConfigurationContext c = ConfigurationContextFactory.createConfigurationContextFromFileSystem("src\\client\\FaultHandler.java", "src\\conf\\axis2.xml");
List phases_list = c.getAxisConfiguration().getInFlowPhases();
Phase phaseOne=(Phase) phases_list.get(0);
phaseOne.addHandler(new FaultHandler()); ------- this is the handler which converts exception
phases_list.add(phaseOne);
c.getAxisConfiguration().setInFaultPhases(phases_list);
MyServiceStub d = new MyServiceStub(c);
But limitation with this approach are:
a) We are still have to catch MyServiceMyException as the stubs have methods which throw this exception.
b) We need to create MyException class on client side similar to server side separately as generated stubs do not have this as exception.
c) We are not forced to handle MyException since this is not an exception being thrown from the method itself so it gets treated as runtime exception.
try{
MyServiceStub d = new MyServiceStub(c);
System.out.println(d.helloWorld());
System.out.println(d.say("hi"));
}
catch (MyServiceMyException e) { --------- we are forced to catch this
} catch (MyException e) { ----------need to create MyException class on Client side
e.printStackTrace();
}
2. We also tried contract first approach where we converted the axis1 web services from rpc-encoded to document literals style by making changes in server-config.wsdd
<service name="MyServices/MyServices" provider="java:EJB" style="document" use="literal">
<parameter name="allowedMethods" value="*"/>
<parameter name="localInterfaceName" value="com.MyServicelEjbLocal"/>
<parameter name="localHomeInterfaceName" value="com.MyServiceEjbHome"/>
<parameter name="beanJndiName" value=" ejb:MyService.MyServiceEjbHome "/>
<beanMapping languageSpecificType="java:exception.MyException" qname="common:MyException"/>
<typeMapping
qname="common:MyException"
languageSpecificType="java:exception.MyException"
serializer="org.apache.axis.encoding.ser.ArraySerializerFactory"
deserializer="org.apache.axis.encoding.ser.ArrayDeserializerFactory"/>
</service>
and created server side stubs from the generated wsdl. When we exposed our service again with Axis2 we were still getting wrapped exception.
3. We tried Jibx data binding technique which required binding.xml along with wsdl which provides details of the beans and the exception structure that we are using in our service and this will be used while creating stubs.
For this we required jibx jar and used a tool to create binding.xml provided by jibx
org.jibx.binding.generator.BindGen
D:\workSpace\testProject>java -cp "D:\jibx\lib\*";"D:\project\lib\*" org.jibx.binding.generator.BindGenexception.MyException
sample binding.xml
<binding name="binding" package="exception">
<mapping abstract="true" type-name="MyException" class="exception.MyException">
<value style="element" name="errorCode" field="errorCode" usage="optional"/>
<value style="element" name="errorMessage" field="errorMessage " usage="optional"/>
</mapping>
<mapping class="exception.MyException" name="MyException">
<structure map-as="MyException"/>
</mapping>
</binding>
And while creating stubs we will require to provide this binding.xml
wsdl2java -o D:\workSpace\testProjectClient\ -S src\ -R resource\ -d jibx -Ebindingfile D:\binding.xml-uw -u -sp -or --noBuildXML --noWSDL -uri http://localhost:8080/services/MyServices /MyService?wsdl
With this binding technique also we were facing same wrapped exception issue.
4. We also tried one more suggested approach with ADB data binding which required changes to be made in axis2-adb-codegen.jar which is provided with other axis2 jar.In this approach we edited schema-compiler.properties file which determines bean generation template.
(A bean writer provider
# may choose not to use a template at all!) However the property loader will
# load the following templates (reference) and provide it and in the case when the
# bean writer is not statically bound to a template, this'll be useful.
#
schema.bean.writer.template=/org/apache/axis2/schema/template/ADBBeanTemplate.xsl
# schema.bean.writer.template=/org/apache/axis2/schema/template/PlainBeanTemplate.xsl
# The type map to be used by the schema compiler
#
schema.bean.typemap=org.apache.axis2.schema.typemap.JavaTypeMap
But this thing resulted in only plain bean instead of ADB bean.
Exception handling mechanism of Axis2 we will require handling exception separately for every service.
No comments:
Post a Comment