JBoss Core Java Web Services
Web Service
Let’s first talk about the Web Service it self. What is a Web Service. People have a tendency to say that the web service is no more then a process of remote method invocation (RMI). In reality, it is not just about RMI. Web Service is a collection of endpoints (or a single endpoint) exposed via HTTP protocol. In order to communicate with these endpoints, we have to create clients to access the end points. The biggest advantage of this kind of communication between the web service end point and a client is that is specified by SOAP protocol which builds on top of HTTP protocol (or sometimes even SMTP protocol).
This protocol defines the structure of the data which is transferred between the end point and the client. The data is exchanged in XML format. As a consequence, web service are said to be ‘platform/technology’ independent. For example a web service can be implemented by using Java CXF framework or plain JBoss Web Services. The client which access this web service can be implemented in, let’s say, non java environment. Web service technology and a technology of the client can be totally different and communication between them is still possible because the SOAP protocol relies on XML message format. Only thing required of this communication to succeed is that both, the end point and the client, agree how the XML message should look like (what kind of fields and what type of content will exist in the xml file) and what are the operations (methods) which client can invoke on the end point.
These requirements are specified by the WSDL. WSDL (Web Services Description Language) is an XML based language which is used to describe the end points of the web service. It can define the HTTP address of the end point, operations which can be executed by the client on this end point, type of the data which can be passed to the operations and what type of data is received back form those operations of the endpoints. It also defines the exact structure of the XML messages which are used in a communication with the endpoint. This kind of information is usually stored in the file with wsdl extension. That is how the famous term ‘wsdl file’ came to the world.
Ok, that is enough about web services. Actually, i could write a lot about web services and SOAP protocol, but that would be outside of the scope of this blog. You are encouraged to do so your self. There are plenty of articles in the internet about this subject.
Creating a simple Web Service on JBoss
Example I am about to show you is in eclipse 3.7 (indigo) and I will use JBoss 4.2.3.GA as a server. As a project build tool i will use maven. Let’s start by creating the most simple web project in eclipse. It will hold the web services implementation (end point).
It is just an maven project for an ordinary web application. Nothing special. If you do not wish to use maven that is also fine. You can use what ever tool you like to create a simple web application.
I will not go into details here in this post on how to create this simple project. Instead, i will provide you a screen shot of the project structure in my eclipse.
Project structure in eclipse. |
Now let’s create the web service. We will do the ‘ bottom-up approach‘. That means we will develop an implementation class first, and define the WSDL later. So let’s write a simple java POJO class. We will just pretend that our web service takes some kind of car id as a parameter and returns a price for that car. If we pass in a non-existing car id, then the web service will return a corresponding error text.
Main elements to spot here are CarWebService interface and CarWebServiceImpl class which actually implements the endpoint interface.
Let’s start from the CarWebService interface. Here is the code:
package ba.codecentric.webservice; import javax.jws.WebMethod; import javax.jws.WebService; import ba.codecentric.webservice.model.CarResponse; @WebService public interface CarWebService { @WebMethod public CarResponse getCarPrice(String carId); }
As you see it is a normal interface definition. Only two things to notice here are two annotations: @WebService and @WebMethod. These annotations belong to the domain of the technology called JAX-WS. JAX-WS is a specification on how to create web services and clients in a way that they are able to communicate with RPC calls and exchange XML based messages. Java has it’s own implementation of JAX-WS and enables us to define a web service end point by using these simple annotations. You can read more about JAX-WS here. Internally this technology in java deals with receiving and parsing XML messages (usually exchanged with SOAP protocol). This shields the developer from many hours of hard work so the developer does not have to spend time writing the code to generate soap message, send it, then receive it on the server side, extract the information from xml, etc.. All this is done internally by JAX-WS mechanism in java.
So when you wish to create plain java web service end point on the server, you simply create the interface. Mark the interface with the WebService annotation and mark all the methods in the interface you wish to expose as the remote methods (with WebMethod annotation). These will then become the operations which can be invoked on the interface. In my example i only have one method but there could be many more.
Next stop is creating the interface implementation. We have exposed the web service methods (operations of the endpoint). These methods must do something in order for the web service to make sense. In java we do this really simple. We will just create the implementation class for the interface.
I will show you the code for the CarWebServiceImpl.
package ba.codecentric.webservice.impl; import java.util.HashMap; import java.util.Map; import javax.jws.WebService; import ba.codecentric.webservice.CarWebService; import ba.codecentric.webservice.model.CarResponse; @WebService(endpointInterface = 'ba.codecentric.webservice.CarWebService') public class CarWebServiceImpl implements CarWebService { private final Map<String, Integer> prices = new HashMap<String, Integer>(); public CarWebServiceImpl() { prices.put('audi', Integer.valueOf(10000)); prices.put('bmw', Integer.valueOf(15000)); prices.put('fiat', Integer.valueOf(5000)); } public CarResponse getCarPrice(String carId) { Integer price = prices.get(carId); CarResponse response = new CarResponse(); if(price == null) { response.setCarPrice(-1); response.setStatus('error: no car with id: ' + carId); } else { response.setCarPrice(price.intValue()); response.setStatus('ok'); } return response; } }
Only thing to notice here again is the special annotation. Same like in the interface but it only has one extra argument. It is the fully qualified name of the endpoint interface. Nothing else.
In the real world this web service would probably have more complicated code. It would take some kind of car id and then it would search for the car in the database. But in here I’ve made much more simple. I have a map for three different car ids and there prices stored with the ids.
If you pass in the existing car id to the service (like audi, bmw) you will get response back from the web service on the server. Response will contain status message (ok) and a car price.
If you pass in a non-existing car id to the service then you will receive a response with the status error and you will also have a detailed string which is telling you that car you searched for was not found. Car price in this case will be set to -1.
Ok. That’s it as far as the web service implementation is concerned. Simple, isn’t it? Only one more thing to do. And this one is even more simple. You need to expose the implementation class as a servlet. You will do this in the web.xml. Here is the code:
<servlet> <servlet-name>carWebservice</servlet-name> <servlet-class>ba.codecentric.webservice.impl.CarWebServiceImpl</servlet-class> </servlet> <servlet-mapping> <servlet-name>carWebservice</servlet-name> <url-pattern>/ws/car</url-pattern> </servlet-mapping>
And we are done. Now we need to package the application and deploy it to JBoss.
The JBoss will take care of publishing this endpoint for you so you do not need to worry about it.
So go ahead. Deploy your application to JBoss. If you are using maven as a build tool here is how your pom.xml should look like:
<build> <finalname>webservices</finalName> <plugins> <plugin> <groupid>org.apache.maven.plugins</groupId> <artifactid>maven-compiler-plugin</artifactId> <version>2.5.1</version> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </build>
Set the source and target version for the maven compiler to java 6 because of the use of annotations.
JBoss has it’s own small web application that allows you to see all the web services which are deployed currently and location of all WSDL files for those web services.
You can access this small application from this url: http://localhost:8080/jbossws/services
Of course, providing that your JBoss server name is localhost and it’s http port is configured to 8080.
Here is the screen shot from my pc which clearly shows that web services we deployed in this example are really running.
You can click on the url of the endpoint. The result will be that you will actually see the WSDL of the endpoint. Inside of that WSDL you will see something like <import location=’……’>. The value of the location attribute is the real url of complete WSDL. So go ahead. Also open that url in the browser. Now you should see the entire WSDL (endpoint definitions, operations which can be invoked, messages that are sent to endpoints and received back from endpoints). I hope this makes it easier to see how actually the web services exchanged the data with the clients. Client simply creates the request based on the WSDL file which it gets from the web service. In that WSDL file, the client can see everything that is necessary to communicate with the endpoint. All the rest is taken care of by SOAP protocol and Java’s JAX-WS mechanism. Developer does not have to see an of these operations being invoked.
Creating a simple web service client (wsconsume utility)
In order to invoke this web service we need to create a client. Client can be a simple java console application. It does not even have to be a web application if you do not want to complicate things. that means you can have a simple java class with the main() method. And all you need in there is a few lines of code and you are done. First thing we need to do here is to generate the so called ‘stub classes’. These classes represent the model request and response classes we use to communicate with the web service. If you remember I’ve created a special wrapper class to return the web service response. But it is not just the model classes which are are in these stub classes. Here you will also find the classes which actually do the invocation of the remote endpoint and some other utility classes. Also you will find some JAXB annotations which are needed when marshaling and un-marshaling java objects to XML message and also the other way round. These stub classes are created based on the WSDL file of the endpoint. If you remember WSDL file contains all the information that a client needs. Based on the WSDL file, we can generate all necessary stub classes (model classes used for communication with the web service and utility classes which are used to facilitate this communication). Lucky for us, JBoss has a small utility program that we will use to generate these classes. We do not have to write a single line of code for these stub classes. And we can also tell this tool to generate the classes immediately in our workspace. Really cool stuff. So let’s give it a shot. The tool is called wsconsume.bat ant it is located in the JBoss folder – bin. for Linux users there is wsconsume.sh.
First i will generate a different project for this in Eclipse. I will call it ws-client. It is a normal Java project. You can just choose File -> New -> Java Project and use all default settings. Ok. Now we are ready to create the stub classes. We want the wsconsume utility to put the classes immediately into the src folder for java source files. And we will also tell wsconsume utility the exact name of the package it should use for these classes. My project is located here: D:\JBossTests\jbossWebServiceWorkspace\ws-client.
So the command for the wsconsume utlity should be: D:\JBossTests\jboss-4.2.3.GA\bin>wsconsume -v -k -p ws.client.impl -s D:\JBossTests\jbossWebServiceWorkspace\ws-client\src
http://127.0.0.1:8080/webservices/ws/car?wsdl
Make sure you are executing this command from the jboss bin folder. Otherwise make sure you have the wsconsume.bat file on your path to reach it from any folder.
Also make sure your jboss is running and the web services for which you are trying to generate the client, are correctly deployed and running.
Let’s explain the parameters i used here:
-v: Verbose (show full error stack trace if there is an error);
-k: Keep/generate java sources;
-p: Package name. The generated classes will immediately get the given package;
-s: Source folder. The directory where to put the source files. I used the source folder my client project, which makes sense.
And finally we have the URL of the endpoint’s WSDL location.
You can open the jboss URL http://localhost:8080/jbossws/services to see all the web services which are currently deployed and see all the WSDL URLs. You can just copy/paste the required WSDL URL to the command prompt for the wsconsume utility . In our case it is the location of the web service we created earlier.
Now go to eclipse and refresh you src folder in the new client project. You should now have some classes generated by the wsconsume utility and they should be in the specified package. All we need to do now is to execute this client and send requests to the web services. Just create one more class. I will call it CarWebserviceClient. And i will put it into separate package. And this class will have the normal java main method. Package is ws.client.runner:
package ws.client.runner; import ws.client.impl.CarResponse; import ws.client.impl.CarWebService; import ws.client.impl.CarWebServiceImplService; public class CarrWebserviceClient { public static void main(String[] args) { CarWebServiceImplService service = new CarWebServiceImplService(); CarWebService port = service.getCarWebServiceImplPort(); CarResponse audiPrice = port.getCarPrice('audi'); System.out.println('audi price: ' + audiPrice.getCarPrice() + ' EUR; web service status: ' + audiPrice.getStatus()); CarResponse fordPrice = port.getCarPrice('ford'); System.out.println('ford price: ' + fordPrice.getCarPrice() + ' EUR; web service status: ' + fordPrice.getStatus()); } }
As you see this code above is really simple. You just need to instantiate the client, then obtain the port to the service and that’s it. We can call the web service operation by calling the java method with the same name. Just pass the parameters you wish. You can use audi, bmw, and fiat as hard coded existing values. You can also try to retrieve the price for some non existing car id. Same way I tried in the example above.
Of course don’t forget to have your jboss running and web services deployed in order for this to work :-)
Well that would be all. This can be a nice starting point to continue your journey into the world of SOAP based web services. This was the most basic example, implemented by using JAX-WS API and with a little help of jboss support for web services.
Reference: Core Java Web Services on JBoss from our JCG partner Branislav Vidovi? at the Geek stuff :-) blog.