This is Google's cache of http://wso2.org/library/3726. It is a snapshot of the page as it appeared on May 27, 2010 02:58:00 GMT. The current page could have changed in the meantime. Learn more

Text-only version
 

RESTful Web Services with Apache Axis2

In this tutorial, Keith Chapman looks at implementing a REST service in Apache Axis2.

Introduction

Does Apache Axis2 support REST? This is a question that's been asked over and over on the Axis2 mailing List. While some think that Axis2 supports REST, others argue that it doesn't. What I refer to here as REST support is support for truly RESTful services, not the Axis2 default of http://localhost:8080/axis2/services/<serviceName>/<operationName>. My stand is that that Axis2 supports truly RESTful services. This tutorial will take you through the steps needed to write a truly RESTful service in Axis2.

First, let me give you an introduction to the terms we will be looking at, in this tutorial.

What is REST?

REST, an acronym for Representational State Transfer is a term coined by Roy Fielding in his Ph.D. dissertation[1] to describe an architecture style of networked systems. REST is built upon the correct use of the HTTP verbs and unique URIs that identify a resource. The most common HTTP verbs used are GET, POST, PUT and DELETE and they are often compared to CRUD (create, Read, Update and Delete) operations. The following is an approximate mapping for the above HTTP verbs to CRUD operations.

 

HTTP Verb CRUD Operation
POST CREATE
GET READ
PUT UPDATE
DELETE DELETE

WSDL 2.0 and REST

The WSDL 2.0 HTTP Binding[2] introduces a clean approach to describe REST services in a standard way. It is a fact that some REST fanatics do not like having a contract for RESTful services but here are some of advantages of having a contract:

  • A uniform approach to describing a service
  • Machine processable
  • Defines interactions with a service
  • Ability to develop tools to ease development e.g. Axis2 WSDL2Java tool

Whether ot not REST needs a description language is an ongoing debate[3].

The REST support in Axis2 is powered by the WSDL 2.0 HTTP Binding. The WSDL 2.0 HTTP binding allows users to control the following:

  • Which HTTP operation is used (GET, PUT, POST, DELETE)

  • Input, output and fault serialization - content-type to be used

  • Transfer codings

  • Authentication requirements

  • Cookies

  • HTTP over TLS (https)

RESTful Services in Axis2

In order to write truly RESTful services in Axis2, users need to use WSDL 2.0 deployment (as of the 1.4.1 release of Axis2). Plans are under way to provide the ability to control RESTful properties when POJO (Plain Old Java Object) deployment is used as well. Please refer the section on future directions for more details.

Writing a WSDL 2.0 from scratch could be a daunting task for users who are not too familiar with WSDL. The rest of the tutorial, will take you down an alternative path where we write our code first and then use Axis2 java2wsdl tool to generate a WSDL for us. We will then tinker the generated WSDL to suit our needs. (Alternatively, if you are brave enough, you could author your WSDL 2.0 by hand and then use the wsdl2java tool of axis2 to generate the code).

Simple Example - Student Service

Let's take a simple scenario, where I am required to expose some student details in a RESTful manner. This service will be able to add new students, update existing students details, delete student details and List details of existing students. So this service will essentially have CRUD operations. 

Business  Logic of The Sample Service - StudentService Class

package org.apache.axis2;

import org.apache.axis2.context.MessageContext;
import org.apache.axis2.engine.AxisConfiguration;
import org.apache.axis2.description.TransportInDescription;
import org.apache.axis2.addressing.EndpointReference;

import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;

public class StudentService {

    private Map<String, Student> map = new HashMap<String, Student>();
    private String baseURL = null;

    public String[] getStudents() {
        int size = map.size();
        String[] students = new String[size];
        Iterator<String> iterator = map.keySet().iterator();
        int i = 0;
        while (iterator.hasNext()) {
            String studentName = iterator.next();
            students[i] = getBaseURL() + "student/" + studentName;
            i++;
        }
        return students;
    }

    public Student getStudent(String name) throws StudentNotFoundException {
        Student student = map.get(name);
        if (student == null) {
            throw new StudentNotFoundException("Details of student " + name + " cannot be found.");
        }
        return student;
    }

    public String addStudent(Student student) throws StudentAlreadyExistsException {
        String name = student.getName();
        if (map.get(name) != null) {
            throw new StudentAlreadyExistsException("Cannot add details of student " + name +
                    ". Details of student " + name + " already exists.");
        }
        map.put(name, student);
        return getBaseURL() + "student/" + name;
    }

    public String updateStudent(Student student) throws StudentNotFoundException {
        String name = student.getName();
        if (map.get(name) == null) {
            throw new StudentNotFoundException("Details of student " + name + " cannot be found.");
        }
        map.put(name, student);
        return getBaseURL() + "student/" + name;
    }

    public void deleteStudent(String name) throws StudentNotFoundException {
        if (map.get(name) == null) {
            throw new StudentNotFoundException("Details of student " + name + " cannot be found.");
        }
        map.remove(name);
    }

    // This method attempts to get the Base URI for this service. This will be used to construct
    // the URIs for the various detsila returned.
    private String getBaseURL() {
        if (baseURL == null) {
            MessageContext messageContext = MessageContext.getCurrentMessageContext();
            AxisConfiguration configuration = messageContext
                    .getConfigurationContext().getAxisConfiguration();
            TransportInDescription inDescription = configuration.getTransportIn("http");
            try {
                EndpointReference[] eprs = inDescription.getReceiver()
                        .getEPRsForService(messageContext.getAxisService().getName(), null);
                baseURL = eprs[0].getAddress();
            } catch (AxisFault axisFault) {                
            }
        }
        return baseURL;
    }
}

This simple service keeps student details in a HashMap with the student name as the key. As student details are kept in a HashMap, we have to make sure that this service is deployed in an application scope. Student details are held in the following student bean, which contains three properties including name ( a String), age (an integer) and subjects (an array of Strings). In order to make things interesting, we will add a couple of custom exception classes as well. The StudentNotFoundException class (thrown when details of a student does not exist) and the StudentAlreadyExistsException class (thrown when duplicate details are attempted).

Student class

package org.apache.axis2;

public class Student {

    private String name;
    private int age;
    private String[] subjects;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String[] getSubjects() {
        return subjects;
    }

    public void setSubjects(String[] subjects) {
        this.subjects = subjects;
    }
}

StudentNotFoundException class

package org.apache.axis2;

public class StudentNotFoundException extends Exception{
    public StudentNotFoundException(String message) {
        super(message);
    }
}

StudentAlreadyExistsException class

package org.apache.axis2;

public class StudentAlreadyExistsException extends Exception {
    public StudentAlreadyExistsException(String message) {
        super(message);
    }
}

Service Descriptor - Services.xml

<serviceGroup>
    <service name="StudentService" scope="application">
        <parameter name="ServiceClass">org.apache.axis2.StudentService</parameter>
        <messageReceivers>
        <messageReceiver mep="http://www.w3.org/ns/wsdl/in-only" class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver"/>
        <messageReceiver mep="http://www.w3.org/ns/wsdl/in-out" class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
    </messageReceivers>
    </service>
</serviceGroup> 

The services.xml (service descriptor for a Axis2 Service) simply states the ServiceClass and and the fact that this application is deployed in application scope (remember we keep all the student information in a HashMap, so we want just one instance of the service class to be created). It also states which message receivers will be used for for the two message exchange patterns we will be using. Our application contains four operations, which use an in-out (request-response) message exchange pattern and a single operation that uses a in-only (request only) message exchange pattern.

Try Deploying this Service

Structure of the Service Archieve File

Structure of the Service Archive file

If you were to package this service up in a .aar file (the structure of this archive will be as given above) and deploy it, Axis2 would make all operations available at <serviceName>/<operationName> by default. Which means, you will be able to perform a http GET on http://localhost:8080/axis2/services/StudentService/getStudent?name=keith. Now, this is not truly REST as you can clearly see that you have no control over the URL the operation was exposed under. Also, the only way you can pass in parameters to this service is via query parameters. Is there a way that we can change the URl where this operation is exposed at? Even send parameters into this service in the path segment of the URI? You sure can and that's where WSDL 2.0 comes into play. Let's have a look at how this could be achieved using WSDL 2.0.

In Order To Be RESTful What Should Be My URLs and HTTP Verbs?

The table below lists a brief approximation as to how CRUD operations would map to HTTP verbs. The first step of making the StudentService we've developed RESTful is, to list down HTTP verbs and URLs that these operations will be exposed under. The following table illustrates this:

Operation Name HTTP Verb URL Pattern
addStudent POST /services/studentService/students
updateStudent PUT /services/studentService/student/{name}
deleteStudent DELETE /services/studentService/student/{name}
getStudent GET /services/studentService/student/{name}
getStudents GET /services/studentService/students

You would have noticed that the URLs I have selected contains certain patterns. These URI patterns will be later mapped to WSDL 2.0 properties. Let's get a WSDL 2.0 document gererated for our service so that we can tinker with it and make our service RESTful.

Using java2wsdl tool to generate a WSDL 2.0 document

You can use the java2wsdl commandline utility that ships with Axis2 to generate a WSDL 2.0 document for the StudentService we've written. The java2wsdl command line utility can be found in the bin directory of the Axis2 distribution. For our example scenario we won't be using all posible options that java2wsdl offers. Our usage of java2wsdl is,

sh java2wsdl.sh -wv 2.0 -o <output directory> -of <output file name> -sn <Name of the service> -cp <classpath uri> -cn <fully qualified name of the service class>

The java2wsdl utiliy has more options. You can check all of them out by executing "sh java2wsdl.sh". Here is what you see when the above command is executed:

keith@keith:/opt/axis2-1.4.1/bin$ sh java2wsdl.sh
 Using AXIS2_HOME:   /opt/axis2-1.4.1
 Using JAVA_HOME:       /opt/software/java/jdk1.5.0_06
Usage: java2wsdl [options] -cn <fully qualified class name>

where [options] include:
  -o <output location>                    output directory
  -of <output file name>                  output file name for the WSDL
  -sn <service name>                      service name
  -l <soap address>                       address of the port for the WSDL
  -cp <class path uri>                    list of classpath entries - (urls)
  -tn <target namespace>                  target namespace for service
  -tp <target namespace prefix>           target namespace prefix for service
  -stn <schema target namespace>          target namespace for schema
  -stp <schema target namespace prefix>   target namespace prefix for schema
  -st <binding style>                     style for the WSDL
  -u <binding use>                        use for the WSDL
  -nsg <class name>                       fully qualified name of a class that implements NamespaceGenerator
  -sg <class name>                        fully qualified name of a class that implements SchemaGenerator
  -p2n [<java package>,<namespace] [<java package>,<namespace]...
                                          java package to namespace mapping for argument and return types
  -p2n [all, <namespace>]                 to assign all types to a single namespace
  -efd <qualified/unqualified>            setting for elementFormDefault (defaults to qualified)
  -afd <qualified/unqualified>            setting for attributeFormDefault (defaults to qualified)
  -xc class1 -xc class2...                extra class(es) for which schematype must be generated. 
  -wv <1.1/2.0>                           wsdl version - defaults to 1.1 if not specified
  -dlb                                    generate schemas conforming to doc/lit/bare style

Now, let's get the WSDL 2.0 document generated for our StudentService.

keith@keith:/opt/axis2-1.4.1/bin$ sh java2wsdl.sh -wv 2.0 -o /home/keith/projects/axis2_rest/resources/ -of StudentService.wsdl -sn StudentService -cp /home/keith/projects/axis2_rest/classes/ -cn org.apache.axis2.StudentService
 Using AXIS2_HOME:   /opt/axis2-1.4.1
 Using JAVA_HOME:       /opt/software/java/jdk1.5.0_06
[ERROR] Required MessageReceiver couldn't be found, thus, default MessageReceiver has been used

 Note : You can safely ignore the error it displays.

This will generate a file called StudentService.wsdl in the output directory. The WSDL generated will look similar to this,

Making Our Service RESTful

The WSDL generated will have 3 bindings cause this is the default setting in Axis2. It will have a SOAP 1.1 binding, a SOAP 1.2 binding and a HTTPBinding. We are interested in making our service RESTful, hence, we will concentrate on the HTTPBinding and ignore the rest (we can even take them off the WSDL).

We did decide upon the URL patterns and the HTTP verbs we will be using for are sample service. Let's wire them together now.

The property that allows us to state the HTTP method that an operation will be exposed under is "whttp:method", which is defined at the binding operation level. If this property is not present, it will look for the "whttp:methodDefault" property which can be defined at the binding level. If this too is not present, WSDL 2.0 defaulting rules will be used. We have two operations that we want to expose over GET, hence lets make whttp:methodDefault="GET" and specify whttp:method for the others at the binding operation level.

The property that allows us to state the URL pattern that an operation will be exposed under is "whttp:location", which is defined at the binding operation level. Please refer[4] for more details on what is whttp:location. You may also refer[5] for some tips on deciding what your whttp:location should be.

The following is how we want out HTTP binding to look like:

<wsdl2:binding name="StudentServiceHttpBinding" whttp:methodDefault="GET" interface="tns:ServiceInterface" type="http://www.w3.org/ns/wsdl/http">
    
    <wsdl2:fault ref="tns:StudentAlreadyExistsException"/>
    
    <wsdl2:fault ref="tns:StudentNotFoundException"/>
    
    <wsdl2:operation ref="tns:deleteStudent" whttp:location="student/{name}" whttp:method="DELETE">
        <wsdl2:outfault ref="tns:StudentNotFoundException"/>
    </wsdl2:operation>
    
    <wsdl2:operation ref="tns:updateStudent" whttp:location="student/{name}" whttp:method="PUT">
        <wsdl2:outfault ref="tns:StudentNotFoundException"/>
    </wsdl2:operation>
    
    <wsdl2:operation ref="tns:addStudent" whttp:location="students" whttp:method="POST">
        <wsdl2:outfault ref="tns:StudentAlreadyExistsException"/>
    </wsdl2:operation>
    
    <wsdl2:operation ref="tns:getStudent" whttp:location="student/{name}">
        <wsdl2:outfault ref="tns:StudentNotFoundException"/>
    </wsdl2:operation>
    
    <wsdl2:operation ref="tns:getStudents" whttp:location="students"/>
    
</wsdl2:binding>

A Few More Tweaks to The WSDL

In our example, we are using Axis2's built-in RPCMessageReceivers, and deploying the service using the WSDL. In order for this to work out-of-the-box, there are a few tweaks needed in the WSDL. (This is needed only because we are using a seperate bean class (and a custom Exception classes) in our service. If your service does not use any bean classes this section can be overlooked safely).

In the WSDL generated, Axis2 puts all bean classes into a namespace called " http://axis2.apache.org/xsd", while other elements are left in "http://axis2.apache.org". We need to have all data types in the same namespace to use the RPCMessageReceivers out-of-the-box. Here is how you accomplish this task:

  1. In the WSDL generated, you will notice two schema sections under the types section. The schema section which defines StudentAlreadyExistsException, Student, and StudentNotFoundException is in the namespace http://axis2.apache.org/xsd. Get the three complex types that defines these three data types, and move them to the other schema section (the one which defines elements in the http://axis2.apache.org namespace). Once this is done, you can get rid of the schema that was in the http://axis2.apache.org/xsd namespace. 
  2. Once you do the above alteration, your schema becomes a bit inconsistent (as some elements still refer to stuff in the http://axis2.apache.org/xsd namespace). Let's fix that now. In the description section of the WSDL, there will be a namespace prefix that refers to http://axis2.apache.org/xsd (its xmlns:ax21="http://axis2.apache.org/xsd" in my case). Edit it so that it refers to http://axis2.apache.org (make it xmlns:ax21="http://axis2.apache.org").
  3. Two of the custom exception classes that we moved (in step 1), extend off the Exception class, which is defined in the http://axis2.apache.org namespace . The namespace prefix ax22 which refers to this namespace was defined in the schema section that we god rid of. Now we need to restore that link. This can be done by making dataTypes refer to ax21:Exception (instead of ax22:Exception, We just defined xmlns:ax21="http://axis2.apache.org" in the previous step.)
  4. Get rid of the import statement at the top of the schema section (get rid of  <xs:import namespace="http://axis2.apache.org/xsd"/>).

Now, your edited WSDL should look similar to this.

Deploy your service

Now we are all set. package the service up in the same manner we did previously and deploy your service. There is a bit of an extra step that you need to do this time though. Make sure you drop in the WSDL we edited into the META-INF directory of the service archieve. Verify that your service is up and running by tryng to get its WSDL 2.0 document at http://localhost:8080/axis2/services/StudentService?wsdl2. You can even try to get the list of students at http://localhost:8080/axis2/services/StudentService/students.

Future Directions

  • Currently (As of Axis2 1.4.1) WSDL 2.0 deployment is the only way to author a truly RESTful services. We plan to extend this to POJO deployment as well. This will alow users to control the HTTPBinding properties in the WSDL through annotations of the service class or parameters in the services.xml. Will keep this article updated once that feature is added to Axis2.
  • Consider adding the helper method we used to generate the links (getBaseURL()) as a helper method into Axis2.

Conclusions

Question: Does Axis2 support REST?

Answer: It sure does.

This article has taken you through the process of writing RESTfull services in Axis2. It also illustrates how WSDL 2.0 fits into the picture and demonstrates its ability to describe truly RESTful services. Yes, there are improvements that could be done to make life easy and we do plan to do so in future releases. 

[1] Dr. Roy Fielding's Ph.D. dissertation - http://roy.gbiv.com/pubs/dissertation/top.htm

[2] WSDL 2.0 HTTP Binding - http://www.w3.org/TR/wsdl20-primer/#more-bindings-http

[3] Does REST need a description language - http://www.infoq.com/news/2007/06/rest-description-language

[4] What is whttp:location? - http://wso2.org/library/3715

[5] Things to note when deciding on values to use for whttp:location - http://wso2.org/library/3725

Author

Keith Chapman, Senior Software Engineer, WSO2 Inc. keith at wso2 dot com

AttachmentSize
StudentService-src.zip5.71 KB
StudentService.aar5.58 KB
originalWSDL.xml11.55 KB
StudentService.wsdl10.91 KB
sanivella's picture

Providing custom authentication….

This tutorial was very helpful for quickly developing RESTful web services. Any inputs/suggestions on how to provide custom authentication for these REST services.

Thanks

mahanagarjuna's picture

Axis2 in OSGi ServletBridge Container

Hi,

I deployed the same application in Axis2 + OSGi Servlet Bridge container. Though I modified the WSDL, I am not getting the modified one when I am accessing: "http://localhost:8080/bridge/services/StudentService?wsdl2".

Please let me know, how to fix this issue?

- Nagarjuna

ogattaz's picture

Problem with axis2 1.5

Hello Keith

Does your "StudentService" sample still runs with axis2 1.5 ?

a) If I try to put it in the "META-INF/services" folder, the Axis2 v1.5 WebApp seems to be blocked when it tries to deploy the service

b) If I try to upload the service "aar" archive file with the admin page, I get this messages in the tomcat log file :

Woden[Warning],0:0,WSDL504,Could not locate the schema document at URL "http://www.w3.org/2001/XMLSchema.xsd",java.net.SocketException:Connection reset
Woden[Warning],0:0,WSDL504,Could not locate the schema document at URL "http://www.w3.org/2001/XMLSchema",java.net.SocketException:Connection reset
Woden[Warning],0:0,Description-1001,The targetNamespace 'http://axis2.apache.org' is not dereferencable.
[INFO] Deploying Web service: StudentService.aar - file:/D:/_devs_intradon/X3_SERVERWSJULIET/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/com.sage.x3.serverws.axis2/WEB-INF/services/StudentService.aar

As the url "http://www.w3.org/2001/XMLSchema.xsd is valid Axis2 needs access to the Internet?

many thanks.

Olivier

roopesh's picture

Need Client

Hi,

I am using your example and created a Client for the same.
But I am not sure as the StudentServiceStub class contains the soap envelop also. Is ther a way by which i can make my stub SOAP free. here is the client created: Please let me know if this is correct or needs some correction. Also if i change whttp:method to something like PORT instead of POST then it also works fine after generating client using wsdl2java. I am not sure if i am getting result because of soap binding in STUB class though i have removed all the soap related things from wsdl before generating Stub class.
public class Client {

/**
* @param args
* @throws StudentAlreadyExistsExceptionException0
* @throws RemoteException
* @throws StudentNotFoundExceptionException1
*/
public static void main(String[] args) throws RemoteException, StudentAlreadyExistsExceptionException0, StudentNotFoundExceptionException1 {
// TODO Auto-generated method stub

StudentServiceStub service = new StudentServiceStub();
addStudent(service);
//deleteStudent(service);
//updateStudent(service);
getStudent(service);
//updateStudent(service);
//getStudent(service);
}

public static void addStudent(StudentServiceStub stub){
try{
AddStudent add = new AddStudent();
Student student = new Student();
student.setName("Ashu11");
student.setAge(12);
add.setStudent(student);
stub.addStudent(add);
} catch(Exception e){
e.printStackTrace();
System.out.println("nnn");
}
}
public static void deleteStudent(StudentServiceStub stub){
try{
DeleteStudent delete = new DeleteStudent();
delete.setName("name");
stub.deleteStudent(delete);
} catch(Exception e){
e.printStackTrace();
System.out.println("nnnsdasdasd");
}
}
public static void updateStudent(StudentServiceStub stub){
try{
UpdateStudent update = new UpdateStudent();
Student student = new Student();
student.setName("Ashu11");
student.setAge(79);
update.setStudent(student);
stub.updateStudent(update);
} catch(Exception e){
e.printStackTrace();
System.out.println("nnnsdasdasd");
}
}
public static void getStudent(StudentServiceStub stub){
try{
GetStudent get = new GetStudent();
get.setName("Ashu11");
// stub.getStudent(get);
GetStudentResponse res = stub.getStudent(get);
System.out.println(res.get_return().getName()+ "/" + res.get_return().getAge());
} catch(Exception e){
e.printStackTrace();
System.out.println("nnnsdasdasd");
}
}

}

rajiv_mayani's picture

How to create a client.

Hi Keith,

From my understanding of Axis it can support expose the same service both a common web service and a REST service.

My question is how do you use the wsdl2java utility to generate a client that can call a service both as a web service and as a restful service?

When I use the WSDL 2.0 file to generate a client it throws an error.
Exception in thread "main" org.apache.axis2.wsdl.codegen.CodeGenerationException: Error parsing WSDL
at org.apache.axis2.wsdl.codegen.CodeGenerationEngine.(CodeGenerationEngine.java:156)
at org.apache.axis2.wsdl.WSDL2Code.main(WSDL2Code.java:35)
at org.apache.axis2.wsdl.WSDL2Java.main(WSDL2Java.java:24)
Caused by: javax.wsdl.WSDLException: WSDLException (at /wsdl2:description): faultCode=INVALID_WSDL: Expected element '{http://schemas.xmlsoap.org/wsdl/}definitions'.

I know we can set an option to make the client as a REST client, but would this automatically choose the PUT, GET, POST methods for HTTP, if so does it get this information from the WSDL 2.0 format file?

Some of the replies to your blog say that data should be sent as XML .. etc How is this xml mapped to an object? Would the above process work if say I choose JiBX data binding provided by Axis2?

keith's picture

Here are the answers

Hi,

When using a WSDL 2.0 document with wsdl2java you need to pass in the -wv 2 option. This is the reason you got this error message. wsdl2java expects a WSDL 1.1 document otherwise.

Although your service (your business logic) is binding independent (REST or SOAP) your stub is. So if you wanna talk to the service using REST you should generate a stub for the HTTPEndpoint using the -pn option and if you wanna use SOAP you should use one of the SOAP endpoints. If you use the HTTPEndpoint the stub would automatically take care of the HTTP Methods to use etc...

I would assume that JIBX should work as well (Thinking about how Axis2 works) but I haven't tried it out honestly. I would like to know your feedback on this (If you could try it out using JIBX).

In this blog post [1] I explained how Axis2 differentiates REST vs SOAP on the server. I would post in a few days on what happens on the client side for clarity.

Thanks,
Keith.
Blog : http://www.keith-chapman.org

[1] http://www.keith-chapman.org/2009/02/how-does-apache-axis2-differentiate.html

nalini's picture

Hi, But what would be the

Hi,

But what would be the client implementation to invoke this service without soap..

florinmarcus's picture

A client example would be excelent

Hello,

I am new with webservices and the REST concept, therefore when I've found your tutorial, I have start dancing. Everything runs nice and clean when I am trying to access the webservice via GET.
I understand that should I provide an XML message in case of add/edit/delete in the request body.
I don't understand what is the structure of the XML message I have to provide, I have tried a simple one:
<student>
<name>john</name>
<age>23</age>
</student>

But I get error saying:

"First Element must contain the local name, Envelope , but found student"

It might be a trivial problem,

Thanks
Florin

florinmarcus's picture

Hello again, We have managed

Hello again,

We have managed to find the way the xml should look like:

<addStudent xmlns="http://axis2.apache.org">
<student >
<name>marcus florin</name>
<age>12</age>
</student>
</addStudent>

Still, we have problems making delete method to work,

Thanks,

Florin

keith's picture

What is the method you used

What is the method you used to send the request?

Thanks,
Keith.

florinmarcus's picture

Hello Keith, Thanks for

Hello Keith,

Thanks for replying.

On Delete problem, initially we got an error such as:

Java.lang.UnsupportedOperationException: An access occurred that is not valid.at org.apache.axis2.description.InOnlyAxisOperation.getMessage(InOnlyAxisOperation.java:109)

This was a problem solved by changing services.xml by updating to "robust-in-only" from "in-only" for RPCInOnlyMessageReceiver configuration.

Right now we are able to delete the Student from the collection using the webservice, the only thing is that we get an error back:

xception in thread "main" org.apache.axis2.AxisFault: The input stream for an incoming message is null.
[java] at org.apache.axis2.transport.TransportUtils.createSOAPMessage(TransportUtils.java:72)

Thanks,
Florin

keith's picture

How did you author your client?

How did you author your client?

From your exception it looks like the client was expecting a response and the server did not send anything.

So got to look at how your client was authored (in-only or in-out) and how this service method is authored (in-only or in-out).

could well be that the client expects it to be in-out whereas for the server its in-only.

Thanks,
Keith.

pratibhasoni's picture

Regarding your example of RESTful web services using AXIS2

private String getBaseURL() {
if (baseURL == null) {
MessageContext messageContext = MessageContext.getCurrentMessageContext();
AxisConfiguration configuration = messageContext
.getConfigurationContext().getAxisConfiguration();
TransportInDescription inDescription = configuration.getTransportIn("http");
try {

here the statement MessageContext messageContext = MessageContext.getCurrentMessageContext(); is giving NULL.
i am unable to find the solution.
pls help.

keith's picture

Hi, You need to deploy your

Hi,

You need to deploy your class in Axis2 as a service. MessageContext.getCurrentMessageContext() can be used only from within a service, its only then that it will return the current MessageContext.

restnewbie's picture

REST URL - need clarification

Hi Keith,

Regarding the URL : http://localhost:8080/axis2/services/StudentService/getStudent?name=keith, it's mentioned "..not truly REST .. no control over the URL the operation was exposed under." -- i couldn't fully understand this statement. How does the REST-style URL /services/studentService/student/{name} allow control?. Can you please clarify?

Thanks,
J

keith's picture

Here is the Clarification

Hi,

Its all about what would feel more RESTfull (and it certainly depends on the situation). In the situation you've highlighted above http://localhost:8080/axis2/services/StudentService/student/keith is more RESTfull than http://localhost:8080/axis2/services/StudentService/getStudent?name=keith.

This is what I mean by no control over the URL. In the second case (the default is) serviceName/operationName?paramName=paramValue and thats it u cannot change it in anyway. This article explains how you could take control of the URL in that sense. It shows how you could map a URL to an operation so instead of having the operation name in the URL you could have what ever you want.

Thanks,
Keith.
Blog : http://www.keith-chapman.org/

restnewbie's picture

Follow-up : url - operation mapping

Thanks Keith, I see how RESTful URLs don't expose the operation name to the service consumer ; but say, we want to change the operation that an URL maps to, it also means changing the WSDL, which would obviously have an impact on the consumer end as well. In this sense, the mapping is not fully transparent. Can you please clarify?

-J