Showing posts with label axis2. Show all posts
Showing posts with label axis2. Show all posts

Thursday, March 27, 2008

Workaround of bug in integration of db4o and axis2

I have been trying to solve the problem described in my last post.

I tried different versions of tomcat and Db4o which did not help me out. I posted this problem in forum of Db4o to ask for help. However, no one supplied right solution. I tried countless possible solutions. Finally, I got it to work. Following is the procedure how I discovered my solution.

To enhance speed of development , I tried to find some software support. I have been using Eclipse as my IDE. So naturally WTP(Web Tool Platform) is a great plug-in to support development of web applications in Eclipse. WTP provides support for Tomcat and Axis2 which are being used in my project. Here(http://www.eclipsecon.com/webtools/community/tutorials/BottomUpAxis2WebService/bu_tutorial.html) is a great tutorial about how to deploy and start Axis2 web service in Eclipse.

Then I doubt that the problem maybe results from Axis2. So I decided to write a simple servlet to do similar job. In other words, Axis2 was not used. And test result showed that everything worked correctly with Db4o. So this meaned that it is highly possible that Axis2 is cause of the problem. I did further investigation to uncover what is wrong under the hood. I built the same Axis2 service in Eclipse and deployed the service to Eclipse's temporary publish directory. Surprisingly, it worked!!! However, if I built web service using ADB(Apache Databinding) in Axis2, wrapped it to a .aar archive file and deployed it into specific directory (services), it did not work!!! So, I made sure that the procedure of deployment of Axis2 web service in Eclipse MUST be different from what I did before. Finally, I found that Eclipse does not wrap web service implementation into a .aar archive file. Instead the deployment directory layout is:

axis2
    - WEB-INF
        - lib
        - conf
        - services
            - Sample                        //Sample is the name of this web service
                - META-INF
                    - services.xml       //this file describes the information of this web service.
        - classes
            - package                     //this is path corresponding to package
                - *.class                    //These .class files are implementation of web service.
    - META-INF
    - axis2-web

The original layout is:
axis2
    - WEB-INF
        - lib
        - conf
        - services
            - Sample.aar               //the .aar archive file
        -classes
    - META-INF
    - axis2-web

Difference is highlighted in blue.
The services.xml is also different. The new services.xml is:

<service name="Sample" >
    <description>
        Please Type your service description here
    </description>
    <messageReceivers>
        <messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-only" class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver" />
        <messageReceiver  mep="http://www.w3.org/2004/08/wsdl/in-out"  class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
    </messageReceivers>
    <parameter name="ServiceClass" locked="false">package.Sample</parameter>
</service>

Besides, in my previous using of Axis2, I used tool provided by Axis2 to construct automatically a stub class and some other auxiliary classes of client side. Then I added my implementation code to that stub class (this stub class contains Axis2-specific stuff). In this new deployment, I did not rely on any Axis2-specific functionality. I just wrote my implementation and compiled it into .class files. And then copied these .class files to directory ${TOMCAT_ROOT}/axis2/classes/package-path/. In addition, I needed to manually create and edit corresponding services.xml file. In my previous deployment, this file is generated automatically by Axis2 tool.
To sum up, this new deployment method eases development of web service because no Axis2-specific stuff is involved during development. The drawback is it is not compact considering that those files are scattered in different places. With regard to this deployment, I did not find much useful information on web. Maybe this method is not recommended, who knows...

However, I have no choice because only one of them works well.

Monday, March 24, 2008

DB4O corrupts in Tomcat with Axis2

I integrated Db4o in our project. Db4o is used to persist state information of workflow. After I integrated it, it seemed to work OK. State information can be successfully stored into its object database. And information can be retrieved successfully. However, after I restarted Tomcat, Db4o corrupted during retrieval of data from database. I tried different kinds of query language Db4o supports. Unfortunately, none of these methods work.

For Native Query, the error in Tomcat log is:

java.lang.IllegalArgumentException: argument type mismatch
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:585)
    at com.db4o.query.Predicate.appliesTo(Unknown Source)
    at com.db4o.inside.query.PredicateEvaluation.evaluate(Unknown Source)
    at com.db4o.Platform4.evaluationEvaluate(Unknown Source)
    at com.db4o.QConEvaluation.visit(Unknown Source)
    at com.db4o.Tree.traverse(Unknown Source)
    at com.db4o.QCandidates.filter(Unknown Source)
    at com.db4o.QConEvaluation.evaluateEvaluationsExec(Unknown Source)
    at com.db4o.QCon.evaluateEvaluations(Unknown Source)
    at com.db4o.QCandidates.evaluate(Unknown Source)
    at com.db4o.QCandidates.execute(Unknown Source)
    at com.db4o.QQueryBase.executeLocal(Unknown Source)
    at com.db4o.QQueryBase.execute1(Unknown Source)
    at com.db4o.QQueryBase.getQueryResult(Unknown Source)
    at com.db4o.QQueryBase.execute(Unknown Source)
    at com.db4o.inside.query.NativeQueryHandler.execute(Unknown Source)
    at com.db4o.YapStreamBase.query(Unknown Source)
    at com.db4o.YapStreamBase.query(Unknown Source)
    at org.cogkit.cyberaide.axis2ws.StatusDB.getStatusByUID(StatusServiceInterfaceSkeleton.java:1204)
    at org.cogkit.cyberaide.axis2ws.StatusServiceInterfaceSkeleton.getJSONStatusByUID(StatusServiceInterfaceSkeleton.java:184)
    at org.cogkit.cyberaide.axis2ws.StatusServiceInterfaceMessageReceiverInOut.invokeBusinessLogic(StatusServiceInterfaceMessageRece
iverInOut.java:80)
    at org.apache.axis2.receivers.AbstractInOutSyncMessageReceiver.invokeBusinessLogic(AbstractInOutSyncMessageReceiver.java:42)
    at org.apache.axis2.receivers.AbstractMessageReceiver.receive(AbstractMessageReceiver.java:96)
    at org.apache.axis2.engine.AxisEngine.receive(AxisEngine.java:145)
    at org.apache.axis2.transport.http.HTTPTransportUtils.processHTTPPostRequest(HTTPTransportUtils.java:275)
    at org.apache.axis2.transport.http.AxisServlet.doPost(AxisServlet.java:120)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:710)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:263)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:584)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
    at java.lang.Thread.run(Thread.java:595)

It seems that the error results from incompatible type conversion. I am sure I followed the instructions elaborated in the official document.

Then, I tried SODA. The output was weirder. The program could not function as I expected. State information could not be retrieved successfully. However, when I investigated tomcat log, no error report existed in log!!! So, I had no way to tell what happened under the hood. This was pretty annoying. It seemed that Db4o indeed retrieved something. However, the fields of the object retrieved are invalid.

I read almost all posts in Db4o forum/community. I tried all suggested solutions. But it still doesn't work. I tried different versions of Db4o, none of them solved my problem.

It has taken me lots of time. Currently, I am not sure whether I can solve it finally...

Tuesday, October 30, 2007

Web Service Development and Deployment using Axis2

Goal
Develop and deploy web service in Apache Tomcat by using Axis2 1.2/1.3.
Prerequisite:
(1) Install JDK, Tomcat and Axis2.
      Keys:
         (a) Versions of these packages must match.
         (b) JDK is recommended. Though JRE works most of the time, some features of the additional libraries/packages may not work well.
(2) Configure JDK. I think the most important part is to set the environment.
     There are already lots of tutorials around the internet. Typically, you should set environment variables : JAVA_HOME and CLASSPATH.
(3) Configure Tomcat
     Tomcat must be run on JVM. So path of JVM needs to be configured correctly.
(4) Deploy Axis2
     Axis2 is deployed in Tomcat. You can put the axis2.war file to webapps directory of Tomcat. Tomcat automatically unextracts and deploys it.
     In addition, you should set AXIS2_HOME environment variable to point to the location of axis2.
Procedure
Now, let's start to write our service prototype.
(1) Define interface in Java.
My sample interface:
File Add.java
package test;
interface Add{
   int addoperation( int op1, int op2 ) ;
}
(2) Now, compile Add.java to .class file. Command:
     javac Add.java
(3) Then, we will generate WSDL document based on the interface defined above.
     The tool is Java2WSDL which is included in Axis2 package.  My command used to generate WSDL document is:
          Java2WSDL -cn test.Add
     The newly generated WSDL document is Add.wsdl.
     Note: (1) Java2WSDL generates WSDL document based on .class file, not .java source file. So step (2) is necessary.
              (2) Java2WSDL command must be invoked in right directory. You must obey that package name must match
                   the directory hierarchy. In my case,directory tree is: D:/apps/demo/test/Add.java. When I invoke Java2WSDL,
                   current working directory is D:/apps/demo/.
(4) Use WSDL2Java (included in Axis2 package) to generate stub Java functions based on WSDL document.
There are two parts: client side and server side.
Detailed information about WSDL2Java/Java2WSDL is here: http://ws.apache.org/axis/java/user-guide.html#UsingWSDLWithAxis
(4.1) Server side
First you need to copy the generated Add.wsdl to the machine where service is to be located. In my case, the Add.wsdl is stored at D:/apps/demo/service.
(4.1.1) Generate stub functions
Again, WSDL2Java is used. Command is:    WSDL2Java -uri C:/apps/demo/test/Add.wsdl -p test -d adb -s -ss -sd -ssi
After excution of this command, directory tree looks like:
D:/apps/demo/service/
   Add.wsdl 
   resource
      Add.wsdl
      services.xml
   src
      test
         AddMessageReceiverInOut.java
         Addoperation.java
         AddoperationResponse.java
         AddSkeleton.java
         AddSkeletonInterface.java
         ExtensionMapper.java
   build.xml
(4.1.2) Add functionality of your service to generated methods
File AddSkeleton.java contains code directly related to your web service implementation. Initial content of AddSkeleton.java is:
/**
 * AddSkeleton.java
 *
 * This file was auto-generated from WSDL
 * by the Apache Axis2 version: 1.3  Built on : Aug 10, 2007 (04:45:47 LKT)
 */
package test;

/**
 *  AddSkeleton java skeleton for the axisService
 */
public class AddSkeleton implements AddSkeletonInterface {
    /**
     * Auto generated method signature
     * @param addoperation0
     */
    public test.AddoperationResponse addoperation(
        test.Addoperation addoperation0) {
        //TODO : fill this with the necessary business logic
        throw new java.lang.UnsupportedOperationException("Please implement " +
            this.getClass().getName() + "#addoperation");
    }
}

I modify AddSkeleton.java to add my web service implementation. New content is:
package test;

public class AddSkeleton implements AddSkeletonInterface {
    public test.AddoperationResponse addoperation(
        test.Addoperation addoperation0) {
         //first, get two parameters
         int op1 = addoperation0.getParam0();
         int op2 = addoperation0.getParam1();
         int sum = op1 + op2; //calculate summation
         AddoperationResponse resp = new AddoperationResponse();//construct response object
         resp.set_return( sum );//set the return value
         return resp;
    }
}
(4.1.3) Build a service
Change directory to D:/apps/demo/service/, then execute the following command:
ant jar.server
After execution, directory tree looks like:
D:/apps/demo/service/
   Add.wsdl 
   resource
      Add.wsdl
      services.xml
   src
      test
         AddMessageReceiverInOut.java
         Addoperation.java
         AddoperationResponse.java
         AddSkeleton.java
         AddSkeletonInterface.java
         ExtensionMapper.java
   build.xml
   build
      classes
         META-INF
            Add.wsdl
            services.xml
         test
            some .class files
      lib
         Add.aar
Note: Add.aar is the the archive.
(4.1.4) Deploy the service
Copy Add.arr to the "services" directory (Tomcat_directory/webapps/axis2/WEB-INF/services) of deployed axis2. Axis2 provides a web-based interface to manage the deployed services. You can use that interface to upload your .arr file.
(4.2) Client side
(4.2.1) Generate stub functions
My command used to generate client-side Web service stub functions is:
    WSDL2Java -uri Add.wsdl -p test.axis2.add -d adb -s
After execution of this command, directory tree should look like:
D:/apps/demo/ 
   Add.wsdl 
   test
      Add.java 
      Add.class
   src
      test
         AddStub.java
   build.xml
The file AddStub.java contains the newly generated client-side stub functions.
(4.2.2) develop your code to use web service
(4.2.2.1) Write source code to invoke remote web service
Create a new file called Client.java (Actually, you can use any name you like) in the directory D:/apps/demo/src/test/.
Content of the file:
package test;

import java.io.*;
import test.AddStub.Addoperation;
import test.AddStub.AddoperationResponse;

public class Client {
 public static int op1 = 0;
 public static int op2 = 0;
    public static void main(java.lang.String[] args) {
        try {
            AddStub stub = new AddStub("http://localhost:8080/axis2/services/Add");
            BufferedReader   in=new   BufferedReader(new  InputStreamReader System.in));  
            System.out.println("Please input the first operand:");
            String input = in.readLine();
            op1 = Integer.parseInt( input );
            System.out.println("Please input the second operand:");
            input = in.readLine();
            op2 = Integer.parseInt( input );
            add(stub);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("\n\n\n");
        }
    }

    /* invoke the 'add' web service */
    public static int add(AddStub stub) {
        try {
            Addoperation req = new Addoperation();
            req.setParam0(op1);
            req.setParam1(op2);
            AddoperationResponse resp = new AddoperationResponse();
            resp = stub.addoperation(req);//invoke the web service
            System.out.println("done");
            System.out.println(op1 + "+"+ op2 + " is " + resp.get_return());
            return resp.get_return();
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("\n\n\n");
        }
        return 0;
    }
}
Note: In axis2 1.4, the generated code is different from code generated by axis2 1.3.
In axis2 1.3, parameters of a request are set using setParam0, setParam1,...
In axis2 1.4, parameters of a request are set using setter functions of those properties, e.g. setName, setId.
Also the method to get return value is different. There may be some other changes I don't know, so I encourage to consult official Axis2 document.
(4.2.2.2) compile the source file and execute it.
Change your current working directory to: C:/apps/demo/, then execute following command:
    ant jar.client
Then, a new directory "build" is created. Now the directory looks like:
D:/apps/demo/ 
   Add.wsdl 
   test
      Add.java 
      Add.class
   src
      test
         AddStub.java
   build.xml
   build
      classes
         test
            some .class files
      lib
         Add-test-client.jar
Now, you can use java command to execute the program.
Remeber: first you must set CLASSPATH to include axis2 .jar libraries and newly created Add-test-client.jar. One alternative way is to use option -cp of command java. Command to execute your program is:
    java test.Client      #with CLASSPATH being set
or
  java -cp <axis2_jars>:./Add-test-client.jar test.Client      #without CLASSPATH being set
To relieve programmers from these fussy stuff, axis2 provides a tool axis2.bat/axis2.sh. This script automatically set CLASSPATH to include all the .jar files of axis2. How can the script know where your axis2 is installed? Ha, you must create a new environment variable called AXIS2_HOME which contains the path where axis2 is installed. However, the newly created Add-test-client.jar is not included in the classpath by the axis2.bat/axis2.sh script. So you still need to add it to CLASSPATH. Execute your program using command:
    axis2.sh -cp ./Add-test-client.jar test.Client
In fact, I wrote my own script to ease invocation of that program. Now, I don't need to set long CLASSPATH or input long command line with -cp option.