In the Using javax.xml.ws.Dispatch for REST Client entry i talked about how you can use the javax.xml.ws.Dispatch class for making call to the REST service. As you can see it makes calling the REST service difficult and it might not make sense to use this approach for calling service in most cases but it might be good idea to think about this approach when you want to make asynchronous calls, i wanted to try this approach so i made following changes to the DispatcherTest1.java
package com.webspherenotes.jaxrs;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.http.HTTPBinding;
public class DispatcherTest1 {
public static void main(String[] args) {
try {
QName serviceName = new QName("http://www.webspherenotes.com/rest", "svc");
QName portName = new QName("http://www.webspherenotes.com/rest", "port");
Service svc = Service.create(serviceName);
svc.addPort(portName, HTTPBinding.HTTP_BINDING,
"http://localhost:9080/MyWebService/rest/contact/1");
Dispatch dis =
svc.createDispatch(portName, Source.class, Service.Mode.PAYLOAD);
Map requestContext = dis.getRequestContext();
requestContext.put(MessageContext.HTTP_REQUEST_METHOD, "GET");
Future f= dis.invokeAsync(null, new DemoAyncHandler());
System.out.println("After making async call to request");
f.get();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
We have to change only couple of lines here, first change the Dispatcher method that your calling from invoke to invokeAsync. The invokeAsync method returns object of java.util.concurrent.Future, calls its get method so that the current thread does not end before it gets calls response callback.
Then create DemoAsyncHandler class that gets called when the response for REST service is ready.
I use the following XMLHelper.java code whenever i have a requirement to pretty print XML, it can handle String, Document and Source, the common objects that i need to print
The JAX-WS has a javax.xml.ws.Dispatch class that you can use for making HTTP call, So i can use it even for making call to REST service and i wanted to see how that works.
I do have a Contact REST service that allows me to make CRUD calls on the Contact table, For example i can call http://localhost:9080/MyWebService/rest/contact/1 URL to get a record from CONTACT table where contact Id is 1. This is the output i get when i call it for contactId 1
The REST service returns CONTACT object in XML format for contactId equal to 1, I wanted to try making the same call using the javax.xml.ws.Dispatch object, this is the code that i built for it.
In this class first i am creating a Service object pointing to http://localhost:9080/MyWebService/rest/contact/1 URL and then i am getting the Dispatcher object pointing to it. Next i use the RequestContext object to set the HTTP request method to GET and then i call the Dispatcher.invoke() which actually makes HTTP request and it returns Source object representing the response. Once i have the response i am uing the XMLHelper.prettyPrintXML() for pretty printing the XML, this is how the output looks like
You might want to return a binary file such as image or pdf from a REST service, if thats the case then JAX-RS provides you with 3 options. I wanted to try those options to i did create a sample application, that you can download from here.
I did create a HelloBinaryService.java file which is a resource class that has 3 methods in each of these methods i am reading a static image file from c:/temp and returning it
package com.webspherenotes.rest;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.StreamingOutput;
@Path("/hellobinary")
public class HelloBinaryService {
@GET
@Path("/file")
@Produces("image/png")
public File getFile(){
File samplePDF = new File("c:/temp/distributedmap.png");
return samplePDF;
}
@GET
@Path("/fileis")
@Produces("image/png")
public InputStream getFileInputStream()throws Exception{
FileInputStream fileIs = new FileInputStream("c:/temp/distributedmap.png");
return fileIs;
}
@GET
@Path("/fileso")
@Produces("image/png")
public StreamingOutput getFileStreamingOutput() throws Exception{
return new StreamingOutput() {
@Override
public void write(OutputStream outputStream) throws IOException,
WebApplicationException {
FileInputStream inputStream = new FileInputStream("c:/temp/distributedmap.png");
int nextByte = 0;
while((nextByte = inputStream.read()) != -1 ){
outputStream.write(nextByte);
}
outputStream.flush();
outputStream.close();
inputStream.close();
}
};
}
}
The first and easiest option would be to return a object of java.io.File directly and the JAX-RS will figure out how to read the file and return binary content of the file, but you cannot use this option if your returning a file that is not stored on the file system.
Second option would be to return object of InputStream, in that case the InputStream could be pointing to file on local system or file stored in database or something like that, in this case JAX-RS container will figure out how to return bytes from InputStream and return it
The last option would be to return object of StreamingOutput, this gives you most flexibility in this case JAX-RS container will give you control at the time of writing body of the message, this is little more work for you since you have to take care of reading the bytes from InputStream and write into output stream but then you get chance to compress the content,....
If you want to build a REST service and deploy it in one of the Java EE 5 compliant containers for example WebSphere APplication Server 7.0, then you will have to use one of the external JAXRS containers such as Jersey, which is very popular among REST service developers.
I wanted to try this option out so i built a HelloJersey application that you can download from here. I followed these steps for building my sample application
First create a Dynamic Web APplication project called HelloJersey
Download following .jar files and add it to the WEB-INF/lib folder of your web application
asm-3.1.jar
jersey-core-1.5.jar
jersey-server-1.5.jar
jsr311-api-1.1.1.jar
Next create a HelloJerseyRESTService.java file like this
After you deploy the application in container you should see that when the server is starting it will scan all the resource and in our case it should find only com.webspherenotes.rest.HelloJerseyRESTService [10/16/11 17:17:47:250 PDT] 0000000e ApplicationMg I WSVR0228I: User initiated module stop operation request completed on Module, HelloJersey.war, of application, DynaCacheSample1EAR [10/16/11 17:17:49:932 PDT] 0000000e ApplicationMg I WSVR0225I: User initiated module start operation requested on Module, HelloJersey.war, of application, DynaCacheSample1EAR [10/16/11 17:17:50:466 PDT] 0000000e webapp I com.ibm.ws.webcontainer.webapp.WebGroupImpl WebGroup SRVE0169I: Loading Web Module: HelloJersey. [10/16/11 17:17:50:521 PDT] 0000000e WASSessionCor I SessionContextRegistry getSessionContext SESN0176I: Will create a new session context for application key default_host/HelloJersey [10/16/11 17:17:50:602 PDT] 0000000e PackagesResou I Scanning for root resource and provider classes in the packages: com.webspherenotes.rest [10/16/11 17:17:50:666 PDT] 0000000e ScanningResou I Root resource classes found: class com.webspherenotes.rest.HelloJerseyRESTService
[10/16/11 17:17:50:667 PDT] 0000000e ScanningResou I No provider classes found. [10/16/11 17:17:50:850 PDT] 0000000e WebApplicatio I Initiating Jersey application, version 'Jersey: 1.5 01/14/2011 12:36 PM' [10/16/11 17:17:51:816 PDT] 0000000e servlet I com.ibm.ws.webcontainer.servlet.ServletWrapper init SRVE0242I: [DynaCacheSample1EAR] [/HelloJersey] [Jersey REST Service]: Initialization successful. [10/16/11 17:17:51:817 PDT] 0000000e webcontainer I com.ibm.ws.wswebcontainer.VirtualHost addWebApplication SRVE0250I: Web Module HelloJersey has been bound to default_host[*:9080,*:80,*:9443,*:5060,*:5061,*:443]. [10/16/11 17:17:51:824 PDT] 0000000e ApplicationMg I WSVR0226I: User initiated module start operation request completed on Module, HelloJersey.war, of application, DynaCacheSample1EAR [10/16/11 17:17:51:825 PDT] 0000000e AppBinaryProc I ADMA7021I: Distribution of application DynaCacheSample1EAR completed successfully.
You can test the service by either directly going to the http://localhost:9080/HelloJersey/rest/hellojersey?name=sunil URL or using the REST client
The WAS 8.0 is Java EE 6.0 compliant that means it allows you to deploy JAX-RS compliant service and you dont have to go through painful steps for setting up external JAXRS container such as Jersey in your web application
I wanted to figure out what it takes to create HelloREST application in WAS 8.0 so i followed this steps to create Hello JAX-RS service. You can download the source code for sample from here
First i used RAD 8.0 to create Servlet Technology 3.0 compliant web application( Which means no need for web.xml or no need of declaring servlets)
Next create a HelloRestService class that is basically a REST service and is available at /hellorest url
@ApplicationPath("/rest/") public class HelloApplication extends Application{
@Override public Set<Class<?>> getClasses() { Set<Class<?>> s = new HashSet<Class<?>>(); s.add(HelloRESTService.class); return s; }
}
The HelloApplication class extends javax.ws.rs.core.Application which helps you to declare the JAX-RS application, in this class i am overriding the getClasses() method which returns set of JAX-RS classes that act as JAX-RS service. In my case i do have only one HelloRESTService class so i am only adding that
Now deploy the application on your WAS 8.0 server, if you try accessing it by sending GET request to http://localhost:9080/HelloRESTService/rest/hellorest?name=Sunil URL and you should get Hello Sunil as response
If your dealing with SAAJ API or you want to create a Debug Message Handler that prints the SOAP Message then you can call SOAPmessage.writeTo(System.out), but this method writes the full SOAP message in one line and which can be little hard to read this is sample output
In the Installing WAS 8 Beta on your machine, entry i talked about how IBM has changed the way we installed WebSphere Application Server starting from V8.0. Now we have to use the IBM Installation Manager,
One advantage of using the IBM Installation manager for installing/managing the WAS 8.0 is that updating it has become very easy, I had to use simple wizard to install latest WAS 8.0 recommended fixes on my machine, no need to go through the process of finding recommended fixes, downloading them and the using update installer to install fixes.
The WebSphere Application Server 8.0 introduces this new feature, in which you can copy enterprise application file in the monitoredDirectory and it automatically gets installed on the server. This feature is similar to concept of copying .war file in the webapps directory. I wanted to try this feature, so i followed these steps
The monitored directory feature is disabled by default so the first step would be to enable this feature, for that login into WAS Admin Console and go to Applications -> Global Deployment settings.
On this page the Monitor directory to automatically deploy applications check box is unchecked by default, so check it. Then you can either use the default path for the monitored directory which is app_profile_root/monitoredDeployableApps or you can change this path to something else, save the changes and restart the server
Once the server is restarted you will notice that app_profile_root/monitoredDeployableApps gets created for you
I did export HelloMonitoredDirectoryEAR.ear into the monitoredDirectory
After copying the .ear into monitoredDirectory i was looking at the logs and i could notice that it is being picked up for deployment [7/5/11 12:32:35:944 PDT] 00000013 WatchService I CWLDD0007I: Event id 1937700630-1. Start of processing. Event type: Added, File path: C:\IBM\WebSphere\AppServer\profiles\AppSrv01\monitoredDeployableApps\servers\server1\HelloMonitoredDirectoryEAR.ear. [7/5/11 12:32:36:022 PDT] 00000013 ModelMgr I WSVR0801I: Initializing all server configuration models [7/5/11 12:32:37:303 PDT] 00000013 WorkSpaceMana A WKSP0500I: Workspace configuration consistency check is disabled. [7/5/11 12:32:38:475 PDT] 00000013 AppManagement I CWLDD0028I: Event id 1937700630-1. The target of the current operation is [WebSphere:cell=ascendan-ggbz6iNode01Cell,node=ascendan-ggbz6iNode01,server=server1]. [7/5/11 12:32:38:553 PDT] 00000013 AppManagement I CWLDD0014I: Event id 1937700630-1. Installing application HelloMonitoredDirectoryEAR... [7/5/11 12:32:40:022 PDT] 00000013 annotations I ArchiveInputStreamData mapData Collision on [ WEB-INF/classes/com/webspherenotes/test/HelloMonitoredServlet.class ] in [ HelloMonitoredDirectory.war ] [7/5/11 12:32:43:631 PDT] 00000014 annotations I ArchiveInputStreamData mapData Collision on [ WEB-INF/classes/com/webspherenotes/test/HelloMonitoredServlet.class ] in [ HelloMonitoredDirectory.war ] [7/5/11 12:32:55:645 PDT] 00000014 webcontainer I com.ibm.ws.webcontainer.internal.WebContainer addExtensionFactory SRVE0239I: Extension Factory [class com.ibm.ws.soa.sca.web.extension.SCAWebExtensionFactory] was registered successfully. [7/5/11 12:32:55:676 PDT] 00000014 webcontainer I com.ibm.ws.webcontainer.internal.WebContainer addExtensionFactory SRVE0240I: Extension Factory [class com.ibm.ws.soa.sca.web.extension.SCAWebExtensionFactory] has been associated with patterns [""]. [7/5/11 12:32:55:692 PDT] 00000014 WebSphereSCAS I Added Servlet mapping: /dojo [7/5/11 12:32:55:692 PDT] 00000014 WebSphereSCAS I Added Servlet mapping: /dojo [7/5/11 12:32:55:692 PDT] 00000014 WebSphereSCAS I Added Servlet mapping: /tuscany [7/5/11 12:32:55:692 PDT] 00000014 WebSphereSCAS I Added Servlet mapping: /tuscany [7/5/11 12:32:58:848 PDT] 00000014 annotations I ArchiveInputStreamData mapData Collision on [ WEB-INF/classes/com/webspherenotes/test/HelloMonitoredServlet.class ] in [ HelloMonitoredDirectory.war ] [7/5/11 12:32:58:957 PDT] 00000014 InstallSchedu I ADMA5013I: Application HelloMonitoredDirectoryEAR installed successfully. [7/5/11 12:33:03:223 PDT] 00000013 annotations I ArchiveInputStreamData mapData Collision on [ WEB-INF/classes/com/webspherenotes/test/HelloMonitoredServlet.class ] in [ HelloMonitoredDirectory.war ] [7/5/11 12:33:04:144 PDT] 00000013 annotations I ArchiveInputStreamData mapData Collision on [ WEB-INF/classes/com/webspherenotes/test/HelloMonitoredServlet.class ] in [ HelloMonitoredDirectory.war ] [7/5/11 12:33:04:207 PDT] 00000013 annotations E CWWAM0001E: An exception occurred during annotation processing: java.lang.IllegalArgumentException: The feature 'annotated-classes' is not a valid feature
I did wait for couple of minutes and then i checked the WAS Admin console and i could see newly installed application like this
I wanted to see where exactly the application is getting installed so i looked at the value of application binaries and i can see that it got installed in regular installedApp directory
The TCP/IP monitor view in Rational Application developer lets you monitor HTTP traffic. It makes it easier to monitor the Web Service request and response. The basic idea is you create a tunnel for example the default HTTP port for WAS is 9080, and i want to monitor traffic on localhost:9080, so i did create a monitor for port localhost:80 and now whenever i make a request to localhost:80, the TCP/IP monitor will forward that request to localhost:9080 and also log it. This is same principle as that of Tcpmon
I wanted to try this feature so i used these steps.
In the TCP/IP montior view click on properties sub menu like this
On the next window i did create a monitor that will forward requests sent to localhost:8080 to localhost:9080 like this After creating the monitor start it, if its not already started.
Create a web service client using RAD, when you create the web service client, RAD copies the wsdl file in the META-INF/wsdl folder and this file has the service binding section, which has the URL for actual service. The Web Service client created by RAD reads URL of the service from wsdl to communicate with the service.
@WebServiceClient(name = "HelloWebServiceService", targetNamespace = "http://services.webspherenotes.com/", wsdlLocation = "META-INF/wsdl/HelloWebServiceService.wsdl") public class HelloWebServiceService extends Service {
private final static URL HELLOWEBSERVICESERVICE_WSDL_LOCATION; private final static Logger logger = Logger.getLogger(com.webspherenotes.services.HelloWebServiceService.class.getName());
if (url == null) throw new MalformedURLException("/META-INF/wsdl/HelloWebServiceService.wsdl does not exist in the module."); } catch (MalformedURLException e) { logger.warning("Failed to create URL for the wsdl Location: 'META-INF/wsdl/HelloWebServiceService.wsdl', retrying as a local file"); logger.warning(e.getMessage()); } HELLOWEBSERVICESERVICE_WSDL_LOCATION = url; }
public HelloWebServiceService(URL wsdlLocation, QName serviceName) { super(wsdlLocation, serviceName); }
public HelloWebServiceService() { super(HELLOWEBSERVICESERVICE_WSDL_LOCATION, new QName("http://services.webspherenotes.com/", "HelloWebServiceService")); }
/** * * @param features * A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy. Supported features not in the features parameter will have their default values. * @return * returns HelloWebService */ @WebEndpoint(name = "HelloWebServicePort") public HelloWebService getHelloWebServicePort(WebServiceFeature... features) { return super.getPort(new QName("http://services.webspherenotes.com/", "HelloWebServicePort"), HelloWebService.class, features); }
}
In the static block of the HelloWebServiceService, it is reading the wsdl and using the URL for making request.
Now in order for TCP/IP monitor to monitor the traffic, we will have to change the value of the URL so that it goes through the port monitored by TCP/IP monitor. I changed the value to 80 like this
Now when i run the client i can see the traffic using TCP/IP monitor like this