Making asynchronous call to REST service

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.


package com.test;

import java.util.concurrent.ExecutionException;

import javax.xml.transform.Source;
import javax.xml.ws.AsyncHandler;
import javax.xml.ws.Response;

public class DemoAyncHandler implements AsyncHandler{

  @Override
  public void handleResponse(Response response) {
    try {
      System.out.println("Inside DemoAsyncHandler.handleResponse "
   + response);
      Source source = response.get();
      XMLHelper.prettyPrintXML(source, System.out);
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (ExecutionException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }

}
Only thing that i am doing in the DemoAsyncHandler is to print the response on console, this is how the output looks like

Pretty print XML document

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


package com.webspherenotes.xml;

import java.io.OutputStream;
import java.io.StringReader;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Document;

public class XMLHelper {

  public static void prettyPrintXML(Source source, OutputStream stream) {
    try {
      Transformer transformer = TransformerFactory.newInstance()
          .newTransformer();
      transformer.setOutputProperty(OutputKeys.INDENT, "yes");
      transformer.
   setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
      transformer.transform(source, new StreamResult(stream));
    } catch (Exception e) {
      e.printStackTrace(System.out);
    }
  }
  
  public static void prettyPrintXML(Document doc, OutputStream stream) {
    prettyPrintXML(new DOMSource(doc),stream);
  }
  
  public static void prettyPrintXML(String xmlString, OutputStream stream){
    prettyPrintXML(new StreamSource(new StringReader(xmlString)),stream);
  }
}

Using javax.xml.ws.Dispatch for REST Client

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.

package com.webspherenotes.rest;

import java.util.Map;

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) {
    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");
      Source responseSource = dis.invoke(null);
     XMLHelper.prettyPrintXML(responseSource, System.out);
  }
}
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

Returning a binary file from REST service

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,....

Createing Hello JAXRS service for deployment using Sun Jersey

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

    1. asm-3.1.jar

    2. jersey-core-1.5.jar

    3. jersey-server-1.5.jar

    4. jsr311-api-1.1.1.jar




  • Next create a HelloJerseyRESTService.java file like this

    package com.webspherenotes.rest;

    import javax.ws.rs.GET;
    import javax.ws.rs.Path;
    import javax.ws.rs.Produces;
    import javax.ws.rs.QueryParam;
    import javax.ws.rs.core.MediaType;

    @Path("/hellojersey")
    public class HelloJerseyRESTService {

    @GET
    @Produces(MediaType.TEXT_HTML)
    public String sayHello(@QueryParam("name") String name ){
    return "Hello " + name;
    }
    }

    The HelloJerseyRESTService only handles GET request and returns HTML markup


  • Next declare the Jersey servlet in the web.xml and create servlet mapping

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app id="WebApp_ID" version="2.5"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <display-name>DynaCacheSample1</display-name>
    <servlet>
    <servlet-name>Jersey REST Service</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
    <param-name>com.sun.jersey.config.property.packages</param-name>
    <param-value>com.webspherenotes.rest</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
    <servlet-name>Jersey REST Service</servlet-name>
    <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
    </welcome-file-list>
    </web-app>



  • 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

Create Hello JAX-RS service in WAS 8.0

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


    package com.webspherenotes.rest;

    import javax.ws.rs.GET;
    import javax.ws.rs.Path;
    import javax.ws.rs.Produces;
    import javax.ws.rs.QueryParam;

    @Path("/hellorest")
    public class HelloRESTService {

    @GET
    @Produces("text/html")
    public String sayHello(@QueryParam("name") String name){
    return "Hello " + name;
    }

    }




  • Next create HelloApplication class which is basically a REST servlet class like this.

    package com.webspherenotes.rest;

    import java.util.HashSet;
    import java.util.Set;

    import javax.ws.rs.ApplicationPath;
    import javax.ws.rs.core.Application;

    @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

Pretty printing SOAP messages

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


<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><wn:sayHello SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wn="http://ws.websphrenotes.com/"><arg0>Sunil</arg0></wn:sayHello></SOAP-ENV:Body></SOAP-ENV:Envelope>


If you want to pretty print the SOAPMessage then you can use the following method.


package com.webspherenotes.ws;
import java.io.ByteArrayOutputStream;

import javax.xml.soap.SOAPMessage;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
public class SOAPHelper {
public static String getSOAPMessageAsString(SOAPMessage soapMessage) {
try {

TransformerFactory tff = TransformerFactory.newInstance();
Transformer tf = tff.newTransformer();

// Set formatting

tf.setOutputProperty(OutputKeys.INDENT, "yes");
tf.setOutputProperty("{http://xml.apache.org/xslt}indent-amount",
"2");

Source sc = soapMessage.getSOAPPart().getContent();

ByteArrayOutputStream streamOut = new ByteArrayOutputStream();
StreamResult result = new StreamResult(streamOut);
tf.transform(sc, result);

String strMessage = streamOut.toString();
return strMessage;
} catch (Exception e) {
System.out.println("Exception in getSOAPMessageAsString "
+ e.getMessage());
return null;
}

}
}


It generates the output which looks like this

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<wn:sayHello SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wn="http://ws.websphrenotes.com/">
<arg0>Sunil</arg0>
</wn:sayHello>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>



I ended up doing this because i could not find SaajOutputer.java class

Updating WAS 8

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.








Installing enterprise application files by adding them to a monitored directory

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



How to use TCP/IP monitor for web service

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());

    static {
    URL url = null;
    try {

    url = com.webspherenotes.services.HelloWebServiceService.class.getResource("/META-INF/wsdl/HelloWebServiceService.wsdl");

    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"));
    }

    /**
    *
    * @return
    * returns HelloWebService
    */
    @WebEndpoint(name = "HelloWebServicePort")
    public HelloWebService getHelloWebServicePort() {
    return super.getPort(new QName("http://services.webspherenotes.com/", "HelloWebServicePort"), HelloWebService.class);
    }

    /**
    *
    * @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


Using Ajax Proxy in the Connections

The Connections server has Ajax Proxy that you can use to make cross domain calls. In order to try the Ajax proxy i did create a iWidget that makes call to http://www.atech.com from the page http://wpconnections.atech.com/. In order to test iWidget i did add it to Connections HomePage. You can download the AjaxProxy iWidget from here

This is how my AjaxProxy.xml file the iWidget xml definition file looks like

<?xml version="1.0" encoding="UTF-8" ?>
<iw:iwidget id="AjaxProxy"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:iw="http://www.ibm.com/xmlns/prod/iWidget"
supportedModes="view" mode="view" lang="en" iScope="com.webspherenotes.ajaxproxy"
allowInstanceContent="true" >
<iw:resource uri="ajaxproxy.js" />
<iw:content mode="view">
<![CDATA[
<div>Ajax proxy</div>
<span id="replacecontent">Replace content</span>
]]>
</iw:content>
</iw:iwidget>


The ajaxproxy.xml declares ajaxproxy.js as a resource, which has all the JavaScript logic for the widget. The ajaxproxy widget displays a place holder div, replacecontent

This is how my ajaxproxy.js file looks like

dojo.provide("com.webspherenotes.ajaxproxy");
dojo.declare("com.webspherenotes.ajaxproxy", [], {
onview: function(){
console.log("Inside the onView function()");
var currContext = this.iContext;
try{
dojo.xhrPost({
url: "/homepage/web/proxy/http/www.atech.com",

load: function(data, ioargs){
currContext.getElementById("replacecontent").innerHTML = data;
console.log(data);
},
error: function(error,ioargs){
alert("Error :" + data);
}
});
}catch(error){
console.log("Error in the dojo.xhrPost " + error );
}
}
});


The onview() method of the ajaxproxy widget will get called to generate the VIEW mode markup, in this method i am making HTTP POST to /homepage/web/proxy/http/www.atech.com which actually means http://www.atech.com, once the response is back i will get control in the load() method and i am using the response to display markup in the widget, which looks like this



The markup looks unformatted because it does not include the necessary resources. When you deploy the widget on your server it will fail with 403 forbidden error at the time of making xhrPost() call unless your ajax proxy is configured to make POST request.

Troubleshooting Ajax Proxy

I was trying to configure the Ajax proxy in the Connections product but i ran into bunch of issues, so i had to figure out how to turn the trace for Ajax proxy by turning trace for com.ibm.ws.ajaxproxy.*=all but once i did i can see it does generate good trace like this.


[5/19/11 22:02:38:407 PDT] 00000087 ProxyServlet 3 com.ibm.ws.ajaxproxy.servlet.ProxyServlet service entering service(..)com.ibm.ws.ajaxproxy.servlet.ProxyServlet
[5/19/11 22:02:38:501 PDT] 00000087 ProxyServlet 3 com.ibm.ws.ajaxproxy.servlet.ProxyServlet service
com.ibm.ws.ajaxproxy.servlet.ProxyServlet service(..) Method:GET; URI:/homepage/web/proxy/ibm/us/en/sandbox/ver1/; QueryString:null; ContextPath:/homepage; ServletPath:/web/proxy; PathInfo:/ibm/us/en/sandbox/ver1/
[5/19/11 22:02:38:501 PDT] 00000087 ProxyServlet 3 com.ibm.ws.ajaxproxy.servlet.ProxyServlet service com.ibm.ws.ajaxproxy.servlet.ProxyServlet service(..) targetPath: http://www.ibm.com
[5/19/11 22:02:38:501 PDT] 00000087 Policy 3 com.ibm.ws.ajaxproxy.proxy.RequestBean com.ibm.ws.ajaxproxy.proxy.Policy RequestBean(URL, String, String):[Ljava.lang.Object;@6f726f72
[5/19/11 22:02:38:501 PDT] 00000087 Policy 3 com.ibm.ws.ajaxproxy.proxy.RequestBean com.ibm.ws.ajaxproxy.proxy.Policy RequestBean(URL, String, String):http://www.ibm.com/ibm/us/en/sandbox/ver1/
[5/19/11 22:02:38:532 PDT] 00000087 URINormalizer 3 com.ibm.ws.ajaxproxy.util.URINormalizer normalize com.ibm.ws.ajaxproxy.util.URINormalizer normalize(..) Orginal URI: http://www.ibm.com/ibm/us/en/sandbox/ver1/ Normalized URI: http://www.ibm.com/ibm/us/en/sandbox/ver1/
[5/19/11 22:02:38:532 PDT] 00000087 ProxyServlet 3 com.ibm.ws.ajaxproxy.servlet.ProxyServlet service com.ibm.ws.ajaxproxy.servlet.ProxyServlet service(..) Attempting to match Policy too: /http/www.ibm.com/ibm/us/en/sandbox/ver1/
[5/19/11 22:02:38:532 PDT] 00000087 ProxyServlet 3 com.ibm.ws.ajaxproxy.servlet.ProxyServlet service com.ibm.ws.ajaxproxy.servlet.ProxyServletPolicies for: *

ACF: none
Actions:
GET

Cookies:

Headers:
Users:

[5/19/11 22:02:38:532 PDT] 00000087 ProxyServlet 3 com.ibm.ws.ajaxproxy.servlet.ProxyServlet service com.ibm.ws.ajaxproxy.servlet.ProxyServlet service(..)1305867758532

[5/19/11 22:02:38:532 PDT] 00000087 ProxyServlet 3 com.ibm.ws.ajaxproxy.servlet.ProxyServlet service com.ibm.ws.ajaxproxy.servlet.ProxyServlet service(..)1305867758532
[5/19/11 22:02:38:532 PDT] 00000087 ProxyServlet 3 com.ibm.ws.ajaxproxy.servlet.ProxyServlet service com.ibm.ws.ajaxproxy.servlet.ProxyServlet service(..) HostConfiguration set too: HostConfiguration[host=http://wpconnections.atech.com]
[5/19/11 22:02:38:532 PDT] 00000087 ProxyServlet 3 com.ibm.ws.ajaxproxy.servlet.ProxyServlet service com.ibm.ws.ajaxproxy.servlet.ProxyServlet service(..) Assigning GetMethod as method
[5/19/11 22:02:38:532 PDT] 00000087 Policy 3 com.ibm.ws.ajaxproxy.proxy.Policy getValidHeaders com.ibm.ws.ajaxproxy.proxy.Policy getValidHeaders(..) entering
[5/19/11 22:02:38:532 PDT] 00000087 Policy 3 com.ibm.ws.ajaxproxy.proxy.Policy getValidHeaders com.ibm.ws.ajaxproxy.proxy.Policy getValidHeaders(..)Reusing pattern for User-Agent
[5/19/11 22:02:38:532 PDT] 00000087 Policy 3 com.ibm.ws.ajaxproxy.proxy.Policy getValidHeaders com.ibm.ws.ajaxproxy.proxy.Policy getValidHeaders(..)Reusing pattern for Accept.*
[5/19/11 22:02:38:532 PDT] 00000087 Policy 3 com.ibm.ws.ajaxproxy.proxy.Policy getValidHeaders com.ibm.ws.ajaxproxy.proxy.Policy getValidHeaders(..)Reusing pattern for Content.*
[5/19/11 22:02:38:532 PDT] 00000087 Policy 3 com.ibm.ws.ajaxproxy.proxy.Policy getValidHeaders com.ibm.ws.ajaxproxy.proxy.Policy getValidHeaders(..)Reusing pattern for Authorization.*
[5/19/11 22:02:38:532 PDT] 00000087 Policy 3 com.ibm.ws.ajaxproxy.proxy.Policy getValidHeaders com.ibm.ws.ajaxproxy.proxy.Policy getValidHeaders(..) exiting
[5/19/11 22:02:38:532 PDT] 00000087 ProxyServlet 3 com.ibm.ws.ajaxproxy.servlet.ProxyServlet service com.ibm.ws.ajaxproxy.servlet.ProxyServlet service(..) Adding Request Header [User-Agent : Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1]
[5/19/11 22:02:38:532 PDT] 00000087 ProxyServlet 3 com.ibm.ws.ajaxproxy.servlet.ProxyServlet service com.ibm.ws.ajaxproxy.servlet.ProxyServlet service(..) Adding Request Header [Accept-Charset : ISO-8859-1,utf-8;q=0.7,*;q=0.7]
[5/19/11 22:02:38:532 PDT] 00000087 ProxyServlet 3 com.ibm.ws.ajaxproxy.servlet.ProxyServlet service com.ibm.ws.ajaxproxy.servlet.ProxyServlet service(..) Adding Request Header [Accept-Encoding : gzip, deflate]
[5/19/11 22:02:38:532 PDT] 00000087 ProxyServlet 3 com.ibm.ws.ajaxproxy.servlet.ProxyServlet service com.ibm.ws.ajaxproxy.servlet.ProxyServlet service(..) Adding Request Header [Accept : text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8]
[5/19/11 22:02:38:532 PDT] 00000087 ProxyServlet 3 com.ibm.ws.ajaxproxy.servlet.ProxyServlet service com.ibm.ws.ajaxproxy.servlet.ProxyServlet service(..) Adding Request Header [Accept-Language : en-us,en;q=0.5]
[5/19/11 22:02:38:532 PDT] 00000087 ProxyServlet 3 com.ibm.ws.ajaxproxy.servlet.ProxyServlet service com.ibm.ws.ajaxproxy.servlet.ProxyServlet service(..) Adding Request Header [Content-Type : application/x-www-form-urlencoded]
[5/19/11 22:02:38:532 PDT] 00000087 Policy 3 com.ibm.ws.ajaxproxy.proxy.Policy getFilteredCookieString com.ibm.ws.ajaxproxy.proxy.Policy getFilteredCookieString(..) rawCookieString: editMode=false; __utma=249276503.1859418337.1303153383.1303153383.1303153383.1; __utmz=249276503.1303153383.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); JSESSIONID=0000UQ_tLLLGMSwyBt5z5_Xd10t:15td46icp; LtpaToken2=VQZuwqjVCdZQtnucuRYbpDNspppYkuTG5jaRDPRMbfI89rxcJBaJ0J4qUMgkw6M3OWJ8N2DpI5zm5f+4AIM3l5IDBdt8WiI04G6MTELExy0nTdt5pBlquum/DMA8OoTYZcpADrN/dHicV8hTiiExAjyZQrOe8WQ2QvA9hFnS1IK5wZM+C7a400lcOt3pzh1T3mr65/SPKXW2LLfFIAXIzFcrqj18Af7Ak6zcb1MmX9lFTRdgopVSxVOBq+5ReL0SJkwwuuaO/WiZptOmuP/1Js4QJ60ekfn8+AQ0znWdtV8khwgV/6Searq14egNeSswzoLdtla8XmxXvT9bN+Ht/5VtXKEXkGRwNglHlbZmg74mHIjnhgf4svqYzJM6kuWK/fnHtkFfAj5iaJQ46zYUzGwMHEMaYqfU217WliF4BD5W8wSrZPc+qYseNUb9zMQ3pF0k4sVi5fkYWWkZ9G6eWNvXCinjQPq5Xt3CtJh+sERJs3zuKepD/milfm29QCsTN0klc8bLCT+x+NFx2bwrAs2hpfywQWoFguwt3pdUw71GfaCzvSU3eIj7EmWDfzm9kQtWpDYSfzhSlbDkKb4N5joSNWlOVWk7BRqkWDqx4bSY3jLUFkg8YIAqSf07ZX4LXp0S5FGkCRs8iTZAmpWuddZoWjGkh4TZwUOKd8oGz1hCqVYsaXHK6ZOX6xjpP7dBC++1NGFeuB5dRIy2swj8MA==; LtpaToken=3FQ3Cxvhqjt0NgBdBtA7adO0uhc79AJbvbc1BgkpjWBRdRevNX0wEoT9Q66qd56aALSOlleaerpij2MXbCGkCSx2cHx/SSNjxdBqYWeXIYvG+542IsczVs3w97GxmBXURp0pc/AYTDd1+ZCmep2zGqyhnKbGNdbQJ5eHMON3KLzerrgtrX9H4rrfvLZUEH3Ejq4ota4XMwrqNz2cJCUOJxig+ygmf0oAUR34kUp+8LSebTY+P8uwEGS/d4d8uh3jWR1seXu4XHUDceqV308GH1ewCMraOEJPq2/k5Cw7vHjcd3V+xdKK3PrV+0Ao65DdyMph+XZuo8yroJ730pHqSFYkAy+TsU6DfSJZm+XTcRo=; currentUserName=was bind; currentUserHandle=546b0260-6273-497e-acfa-1e898f3037d4

Synchronizing between LDAP and the profiles database

When you install connection, you will

  • Go to the E:\IBM\LotusConnections directory, there you will see two TDISOL.zip expand the platform specific version in the same directory like this


  • Open the tdienv.bat file and change value of TDIPATH to point to the location where TDI is installed on your machine, By default it is set to C:\Program Files\IBM\TDI\V7.0


    @REM *****************************************************************
    @REM
    @REM IBM Confidential
    @REM
    @REM OCO Source Materials
    @REM
    @REM Copyright IBM Corp. 2010
    @REM
    @REM The source code for this program is not published or otherwise
    @REM divested of its trade secrets, irrespective of what has been
    @REM deposited with the U.S. Copyright Office.
    @REM
    @REM *****************************************************************

    @echo off
    IF "%TDIPATH%" == "" (
    SET TDIPATH=E:\IBM\TDI\V7.0
    )

    IF "%TDI_CS_HOST%" == "" (
    SET TDI_CS_HOST=localhost
    )

    IF "%TDI_CS_PORT%" == "" (
    SET TDI_CS_PORT=1527
    )

  • Then open the profiles_tdi.properties file which is in the E:\IBM\LotusConnections\TDISOL\TDI directory in text editor and set following values


    source_ldap_url=ldap://directory.atech.com:1389
    source_ldap_user_login=cn=root
    source_ldap_user_password=tdsadmin
    source_ldap_search_base=dc=webspherenotes,dc=com
    source_ldap_search_filter=(objectclass=inetOrgPerson)

    source_ldap_use_ssl=false
    source_ldap_authentication_method=Simple
    source_ldap_time_limit_seconds=0
    source_ldap_required_dn_regex=
    source_ldap_collect_dns_file=collect.dns
    source_ldap_map_functions_file=profiles_functions.js
    source_ldap_page_size=0
    source_ldap_logfile=logs/PopulateDBFromSource.log
    source_ldap_debug=true
    source_ldap_sort_attribute=
    source_ldap_sort_page_size=
    source_ldap_escape_dns=false
    source_ldap_compute_function_for_givenName=
    source_ldap_compute_function_for_sn=
    source_ldap_collect_updates_file=employee.updates
    source_ldap_binary_attributes=GUID
    source_ldap_manager_lookup_field=
    source_ldap_secretary_lookup_field=
    dbrepos_jdbc_url=jdbc:db2://localhost:50000/peopledb
    dbrepos_jdbc_driver=com.ibm.db2.jcc.DB2Driver
    dbrepos_username=LCUSER
    dbrepos_password=lcuser1

    dbrepos_mark_manager_if_referenced=true
    monitor_changes_debug=false


  • Execute following task sync_all_dns.bat file which is in the E:\IBM\LotusConnections\TDISOL\TDI directory and look at the logs in the E:\IBM\LotusConnections\TDISOL\TDI\logs directory


After the command is executed you should be able to see the changes right away you can keep executing the same task repeatedly

Adding a Google gadget to Connection

In the Adding Facebook iWidget on the Connections Home page i talked about how you can add Facebook iWidget to the Connection page, If you look inside the Facebook iWidget you will notice that is simply wrapping the Facebook Google gadget which is hosted at , i thought i will figure out the steps that are required to wrap Google gadget into Widget. I am using the Google Weather Forcast as sample Gadget that i want to wrap into iWidget and it to the Connections Home page.


  • First i went to Google Gadgets for your Web page, to find out all the Google Gadgets that are available to be added to my page
  • Then i went to the Weather Gadget page, and i did click on Add to your webpage button, on the next screen i did get screen that would let me configure the widget before adding it to page, Ex, i can select the city that i am interested in, i can change title of the gadget,...


  • After configuring the Gadget i did click on Get the code button, which gives me a HTML script tag like this

    <script src="http://www.gmodules.com/ig/ifr?url=http://hosting.gmodules.com/ig/gadgets/file/100080069921643878012/facebook.xml&synd=open&w=320&h=400&title=&border=%23ffffff%7C3px%2C1px+solid+%23999999&output=js"></script>


  • I copied this HTML into a HTML file called GoogleWeatherGadget.html file and i did copy it on my HTTP server at E:\IBM\HTTPServer\htdocs\weather

    <script src="http://www.gmodules.com/ig/ifr?url=http://hosting.gmodules.com/ig/gadgets/file/100080069921643878012/facebook.xml&synd=open&w=320&h=400&title=&border=%23ffffff%7C3px%2C1px+solid+%23999999&output=js"></script>


  • Next i had to create a GoogleWeatherGadget.xml file which points to the GoogleWeatherGadget.html file on my HTTP server in E:\IBM\HTTPServer\htdocs\weather directory like this

    <iw:iwidget name="googleFacebook" xmlns:iw="http://www.ibm.com/xmlns/prod/iWidget">
    <iw:content mode="view">
    <![CDATA[
    <iframe scrolling="auto" width="100%" height="330" frameborder="0" src="http://wpconnections.atech.com/weather/GoogleWeather.html"></iframe>
    ]]>
    </iw:content>
    </iw:iwidget>

    In this the value of iframe src is fully qualified URL of the html page that has the Google gadget

  • The last step was to use the steps defined in Deploying custom widget on Connections Home Page for adding Weather widget to HomePage, If you want to add the widget to the profiles page use Adding widget on profiles page for more information




This is how my HomePage looks like after adding it Weather widget to it

WAS 8 is out

I saw WebSphere Application Server 8 is out press release today. It looks like bunch of other software products are also released. The main focus is on Mobile, cloud,...

Adding facebook iwdiget on the Connections Home page

YOu can add iWidgets to either Home, Profile or Communities page in the Connections. If you go to the IBM Lotus and WebSphere Portal Business Solutions Catalog and search for Lotus Connections, you will find bunch of iWidgets that you can use, there is iWidget for LinkedIn, Facebook, Twitter, Flickr,...

I wanted to try the iWidget for Facebook and these are the steps that i followed

  • First i did download the Facebook iWidget from the Solution Catalog

  • When i looked inside the .zip file i could see that it contains a FacebookGoogleGadget.xml (iWidget .xml file) and GoogleFacebook.html (Google gadget html file), i want to host this on my HTTP server so i did unzip it into the document root of my HTTP server at E:\IBM\HTTPServer\htdocs

  • Then i verified that i can access the FacebookGoogleGadget.xml at


  • The last step was to use the steps defined in Deploying custom widget on Connections Home Page for adding Facebook widget to HomePage, If you want to add the widget to the profiles page use Adding widget on profiles page for more information

  • After the widget is added to home page i went to home page and it did prompt me for my facebook login and once i supply my login information i could see my facebook page






Using Facebook iWidget turned out to be really easy.

Adding widget on profiles page

I was trying to add LinkedIn iWidget to my Connection's Homepage and as part of that process i had to figure out how to add custom iWidget to the profiles page, during the process i learned that the process for adding custom iWidget on profiles page is very similar to adding iWidget on the communities page.

The configuration for the iWidget is stored in widgets-config.xml file, This file has two sections one for profiles and other for communities. While adding iWidget first i had to add the definition of the iWidget, by that i mean point to the URL, define preferences,... The widget-config.xml file also has page definition in xml format, so i had to add the widget definition to the suitable position on the page, i followed these steps to add LinkedIn iWidget


  • Connect to Deployment manager of your lotus connection server using wsadmin client

    wsadmin.bat -lang jython -username wasadmin -password wasadmin -port 8879


  • The profilesAdmin.py has some jython functions that make administration of the profiles functionality easier, you should execute that file to make the functions available in the wsadmin console by calling

    execfile("profilesAdmin.py")


  • Checkout the currents widgets-config.xml in c:/temp using following command

    ProfilesConfigService.checkOutWidgetConfig("c:/temp", "wpconnectionsCell01")



  • Open the widgets-config.xml file using a text editor, you will notice that it has two top level resource elements one for profiles and other for communities. Expand the profiles related related element. You will find bunch of widgetDef elements in that file add following widgetDef element at the end pointing to HelloWorld widget

    <widgetDef defId="HelloWorld" url="/hellowidget.xml">
    </widgetDef>

    In my case the hellowidget.xml is available on HTTP server at the root. Once widget is defined next step is to add it to one or more pages, under the layout element you will have multiple page elements, you can add the widget definition to one of the pages here, In my case i want to add HelloWorld widget to 3rd column on the profilesView page, so i did add it to the page like this

    <page pageId="profilesView">
    <widgetInstance uiLocation="col2" defIdRef="multiWidget"/>
    <widgetInstance uiLocation="multiWidget" defIdRef="board"/>
    <widgetInstance uiLocation="multiWidget" defIdRef="contactInfo"/>
    <widgetInstance uiLocation="multiWidget" defIdRef="backgroundInfo"/>
    <widgetInstance uiLocation="multiWidget" defIdRef="multiFeedReader"/>
    <widgetInstance uiLocation="col1" defIdRef="socialTags"/>
    <widgetInstance uiLocation="col1" defIdRef="sand_thingsInCommon"/>
    <widgetInstance uiLocation="col1" defIdRef="LinkedIn"/>
    <widgetInstance uiLocation="col3" defIdRef="sand_socialPath"/>
    <widgetInstance uiLocation="col3" defIdRef="reportStructure"/>
    <widgetInstance uiLocation="col3" defIdRef="friends"/>
    <widgetInstance uiLocation="col3" defIdRef="linkRoll"/>
    <widgetInstance uiLocation="col3" defIdRef="HelloWorld"/>
    </page>


  • Once your done making changes to the widgets-config.xml file you can check it in by executing following wsadmin command

    ProfilesConfigService.checkInWidgetConfig()



  • You can synchronize your changes across the nodes by executing following wsadmin function

    synchAllNodes()


  • Restart the Profiels application using the WAS Admin console for changes to take effect



Connections solution catalog

The IBM Lotus and WebSphere Portal Business Solutions Catalog provides some ready made components that you might be interested in. It has some of the useful iWidgets such as iWidget for LinkedIn, facebook,... It also has plugings for Microsoft Outlook, Lotus Notes,...

Using Apache Abdera for working with Lotus Connections API

In the Connections API entry i blogged about how to make a REST call to the Connection server using simple Java based API, But in order to work with Connections we need two parts first is ability to make HTTP call and other is ability to parse the feed and get information from it.

The Apache Abdera project library provides infrastructure to help in both these infrastructure pieces. Both the part to make HTTP call and the part required for parsing and working with feed. I wanted to try this so i did create this HelloProfileAPI.java which is standalone Java application which makes a call to profile service and returns the result in ATOM format, once the results are returned it prints the content of feed entries on System.Out


package com.webspherenotes.connections;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;

import org.apache.abdera.Abdera;
import org.apache.abdera.model.Document;
import org.apache.abdera.model.Entry;
import org.apache.abdera.model.Feed;
import org.apache.abdera.parser.Parser;

public class HelloProfileAPI {

public static void main(String[] args) {
try {
URL url = new URL(
"http://wpconnections.atech.com/profiles/atom/search.do?name=was");

// Open the URL: throws exception if not found
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.connect();
InputStream inputStream = conn.getInputStream();
Abdera abdera = new Abdera();
Parser parser = abdera.getParser();
Document feed_doc = parser.parse(inputStream);
Feed feed = feed_doc.getRoot();
List entryList = feed.getEntries();
for (Entry e : entryList) {
System.out.println(e.getContent());
}
} catch (MalformedURLException e) {
e.printStackTrace(System.out);
} catch (IOException e) {
e.printStackTrace(System.out);
}
}
}


In this case i am making call to "http://wpconnections.atech.com/profiles/atom/search.do?name=was" and then parsing the feed and then i am printing content of feed entry to System.out. In my case there is only one user with name equal to was so it returns big chunk of HTML for that user like this


<span xmlns="http://www.w3.org/1999/xhtml" class="vcard"><div><img src="http://wpconnections.atech.com/profiles/photo.do?key=c3219b57-5e65-4c50-b00a-3e722315d035&lastMod=1302886728960" class="photo"></img></div><div><a class="fn url" href="http://wpconnections.atech.com/profiles/atom/profile.do?key=c3219b57-5e65-4c50-b00a-3e722315d035">was bind</a></div><div><a class="sound url" href="http://wpconnections.atech.com/profiles/audio.do?key=c3219b57-5e65-4c50-b00a-3e722315d035&lastMod=1302886728960">Pronunciation</a></div><div class="x-groupwareMail" style="display:none"></div><div class="org"><span class="organization-unit"></span></div><div class="role"></div><div class="title"></div><div class="x-office"><span class="x-building"></span><span class="x-floor"></span><span class="x-office-number"></span></div><div class="tel"><abbr class="type" title="work">Work:</abbr><span class="value"></span></div><div class="x-manager-uid" style="display:none"></div><div class="x-is-manager" style="display:none">N</div><div class="x-profile-key">c3219b57-5e65-4c50-b00a-3e722315d035</div><div class="uid">546b0260-6273-497e-acfa-1e898f3037d4</div><div class="x-profile-uid">wasbind</div><div class="x-lconn-userid">546b0260-6273-497e-acfa-1e898f3037d4</div><div class="rev" style="display:none">2011-04-15T16:58:48.960Z</div><div class="x-profile-type" style="display:none">default</div></span></>




I tried pasting the HTML in a simple test.html to see what is getting returned and i can see the markup for person card like this

Using Connection API

There could be situations when you would want to use the Connections API for interacting with Connections from outside the Web interface provided by Connection. Ex, you want to create a custom web application or portlet that will let you search for user in the Connection profile repository or want to pull users blog entries and display them outside, if thats the case you should use the REST API provided by Connections, basic idea is you make a HTTP call from your application to Connection, and it will return ATOM based response, you can then parse the feed,.. If you want to post data to connections you will have to create ATOM feed and send it to Connection using HTTP using either HTTP POST or PUT methods or you can delete a record by calling HTTP DELETE method.

I wanted to try this feature so i did create a simple standalone Java application which makes use of plain java to make a HTTP call with name of the user as argument to profiles feature in Connections and the Connections server will return list of all the users with matching name and i will print the output to Standard output



package com.webspherenotes.ssl;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class HelloSSL {

public static void main(String[] args) {
try {
URL url =
new URL("http://wpconnections.atech.com/profiles/atom/search.do?name=was");

// Open the URL: throws exception if not found
HttpURLConnection conn =
(HttpURLConnection)url.openConnection();
conn.connect();
InputStream inputStream = conn.getInputStream();
BufferedReader reader =
new BufferedReader(new InputStreamReader(inputStream));
String line = null;
while((line = reader.readLine()) != null){
System.out.println(line);
}
} catch (MalformedURLException e) {
e.printStackTrace(System.out);
} catch (IOException e) {
e.printStackTrace(System.out);
}
}
}


In my case the profiles application is available at http://wpconnections.atech.com/profiles and i am calling the Search API with one parameter name=was, which is my search criteria. Similarly you can search for all the users from say Fremont city by passing city=fremont as argument.

Connection will return a search result in ATOM feed format, i am reading that response and printing it to System.out.Take a look at Lotus Connections API document for more information what API meets your requirement and what are the inputs and outputs of the API call.

Making HTTPS call from inside the WebSphere Application Server

In the Getting markup from HTTPS connection in Java program entry i talked about how to make a HTTPS call from standalone Java program, but what if you want to make a HTTPS call from a Servlet, or some code that is running inside J2EE container such as WebSphere Application server, In that case you will have to import the SSL certificate inside WAS.

I wanted to figure out the steps to make HTTPS call from inside WAS so i did create a DemoSSL servlet that takes URL that you want to access as input and makes request to that URL, once the request is successful it will write the response back to the output. Then i configured WAS so that i can make call to https://mail.atech.com, my employers email server but you can use some other https url if you want.

First i did create a DemoSSLServlet that looks like this



package com.webspherenotes.ssl;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* Servlet implementation class DemoSSLServlet
*/
public class DemoSSLServlet extends HttpServlet {

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
}

/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Entering DemoSSLServlet.doPost()");
String submitUrl = request.getParameter("url");
System.out.println("Value of URL " + submitUrl);
URL url = new URL(submitUrl);

// Open the URL: throws exception if not found
HttpURLConnection conn =
(HttpURLConnection)url.openConnection();
conn.connect();
InputStream inputStream = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line = null;
while((line = reader.readLine()) != null){
response.getWriter().println(line);
}
System.out.println("Exiting DemoSSLServlet.doPost()");
}

}


In the DemoSSLServlet servlet, i did override the doPost() method and in this method i am making call to the supplied URL and printing response to the output. also had to create index.jsp to take URL as input from user


<%@ page language="java" contentType="text/html; %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<form action="DemoSSLServlet" method="post">
<table>
<tr>
<td>URL</td>
<td><input type="text" name="url" /></td>
<td><input type="Submit" name="submit" /></td>
</tr>
</table>
</form>
</body>
</html>


Then i did install the DemoSSL.war file on my WAS server, i tried testing it with http://www.google.com and made sure that it works and it is able to return markup, But when i tried accessing the https://mail.atech.com URL i got following error



So next step was to configure the WAS trust store so that it would trust SSL certificate from https://mail.atech.com. Login into the WAS Admin console and go to Security -< SSL Certificate and Key Management page like this



Click on Key Stores and Certificates



Click on CellDefaultTrustStore




Click on Signer Certificates



Click on Retrieve from Port button to get a screen like this,



On this page enter the information related to the Host from which you want to import the SSL certificate



Click on Retrieve signer certificate button, at this point WAS will import the SSL certificate from host and display it like this





Now save the changes and when you try to hit the URL again it should work without throwing exception

Getting markup from HTTPS connection in Java program

One of the common requirements in J2EE is making a request to HTTP URL and getting response back. The JDK has support to make HTTP call and get response, you dont have to use any external library if you dont want to but (But most of us use Apache HTTP Client).

This sample code demonstrates how you can make a HTTP call to http://wpconnections1.atech.com/ and get and print response back to the console


package com.webspherenotes.ssl;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class HelloSSL {
public static void main(String[] args) {
try {
URL url = new URL("http://wpconnections1.atech.com/");

// Open the URL: throws exception if not found
HttpURLConnection conn =
(HttpURLConnection)url.openConnection();
conn.connect();
InputStream inputStream = conn.getInputStream();
BufferedReader reader =
new BufferedReader(new InputStreamReader(inputStream));
String line = null;
while((line = reader.readLine()) != null){
System.out.println(line);
}
} catch (MalformedURLException e) {
e.printStackTrace(System.out);
} catch (IOException e) {
e.printStackTrace(System.out);
}
}
}



This program works but you might want to access the same URL on HTTPs instead of HTTP, This program will work if you try to access say https://encrypted.google.com. But, In development environment we use self-signed SSL certificate and your JVM would not trust the self-signed SSL certificate. So when you try to connect to http://wpconnections1.atech.com/ it will throw this exception


javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException:
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(Unknown Source)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(Unknown Source)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(Unknown Source)
at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Unknown Source)
at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at sun.net.www.protocol.https.HttpsClient.afterConnect(Unknown Source)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(Unknown Source)
at com.webspherenotes.ssl.HelloSSL.main(HelloSSL.java:23)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid
certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(Unknown Source)
at sun.security.validator.PKIXValidator.engineValidate(Unknown Source)
at sun.security.validator.Validator.validate(Unknown Source)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(Unknown Source)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
... 12 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid
certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source)
at java.security.cert.CertPathBuilder.build(Unknown Source)
... 18 more


You will have to follow some additional steps to import the self signed SSL certificate in your JVM so that it trust it. There are multiple ways to do that but the one that i like is outlined here http://blogs.sun.com/andreas/entry/no_more_unable_to_find, first i had to download the InstallCert.java on my machine and package it into a .jar file then i executed


java -jar InstallCert.jar wpconnections1.atech.com


This will create a jssecacert file on your machine where you executed the InstallCert.jar. In my case i did execute the .jar in c:\temp so i did create a C:\Temp\jssecacert file in that directory. This file has the imported certificate for wpconnections1.atech.com

Then i went back to my program and i did execute it by adding following JVM argument -Djavax.net.ssl.trustStore="c:\temp\jssecacerts" like this


Now when i run the HelloSSL program i can see response being returned

Deploying custom widget on Connections Home Page

I wanted to learn how to add a custom iwidget to my HomePage in the Connections so i followed these steps


  • First i did create hellowidget.xml file that will display Hello lotus connections widget message in the view mode, this is how my hellowidget.xml file looks like


    <?xml version="1.0" encoding="UTF-8" ?>
    <iw:iwidget id="MyWidgets"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:iw="http://www.ibm.com/xmlns/prod/iWidget"
    supportedModes="view" mode="view" lang="en">
    <iw:content mode="view">
    <![CDATA[
    <div>Hello lotus connections widget</div>
    ]]>
    </iw:content>
    </iw:iwidget>

    You can find more information about the iwidget here

  • Before i could use the widget i need to make it available on http, there are few different options for doing that, you can copy it in web application and install that web application on your connections server or the simplest is to copy it to HTTP server, in my case i copied it to E:\IBM\HTTPServer\htdocs and then i did access it by going to http://wpconnections.atech.com/hellowidget.xml to make sure i can access the widget.xml like this


  • Then i logged into the Connections Home page as admin user and went to Administration tab



    On this page click on Add another widget button

  • On the next page enter values for the newly added widget such as title, URL of the widget.xml file,...etc


  • Save your changes and it will take you back to the Administration tab, by default the newly added widget appears in the Disabled Widgets column, so select the widget can click on Enable button


  • Once you enable the widget it gets displayed under the Enabled Widgets column like this


  • Now if you go to HomePage and click on the HomePage you can see the HelloConnections widget in the available widget list click on add button to add to the page




You will notice that the widget is added to page and the view mode markup is displayed like this

Assigning administrator role to HomePage application in connections

I want to add custom widgets to my home page but for that i need to assign Administrative rights for HomePage application to one of the users, i followed these steps to assign admin rights


  • Every application in the Lotus Connections make use of J2EE security for managing user roles. For example if i open the web.xml file for the HomePage.war i can see that it defines few roles, this is how the Admin role is defined

    <security-constraint>
    <web-resource-collection>
    <web-resource-name>
    Secure Catalog Administration
    </web-resource-name>
    <url-pattern>
    /admin/*
    </url-pattern>
    <http-method>
    GET
    </http-method>
    <http-method>
    POST
    </http-method>
    <http-method>
    PUT
    </http-method>
    <http-method>
    DELETE
    </http-method>
    </web-resource-collection>
    <auth-constraint>
    <role-name>
    admin
    </role-name>
    </auth-constraint>
    </security-constraint>

    If you look into the web.xml you will notice that it defines following roles

    1. person

    2. everyone

    3. reader

    4. metrics-reader

    5. admin



  • Login into WAS admin console to assign admin role to the user

  • In the WAS Admin console go to Applications -< WebSphere Enterprise applications and you will see list of applications deployed on the server



  • Click on the HomePage link to view the details of HomePage enterprise applications like this


  • CLick on Security roles to users/group mapping link to view the current mappings


    In my case all other roles have some assignment but Admin role is not assigned to any one, so i did select the admin role and click on map users (Its better to map admin role to a group)

  • Search for the user that you want to assign to the admin role, in my case wasbind and save your changes and restart the Lotus Connections server


  • After server is restarted login as the admin user and on the home page you should see Administration tab like this


  • When you switch to Administration tab you will notice that your allowed to enable disable widgets, publish new widgets on this tab



Changing connections configuration

The Connections configuration is stored in xml files. You can find all the configuration files in the config directory of your deployment manager



Even though you can change these files manually and start the server, its not recommended (I never tried changing the config files manually so not sure if the server would pick them up) because you can change the file to be invalid and which could prevent server from coming up.

But if you want to change anything in the connections configuration, you should first checkout the file to your local directory, modify the file and then check it in. I wanted to try this so i followed these steps


  • First i went to the DMGR machine and i did execute this command to connect to DMGR using wsadmin command


    wsadmin.bat -lang jython -username wasadmin -password wasadmin -port 8879


    Important note: Make sure that your actually connecting to DMGR and the value of port is the SOAP port for DMGR, When i was trying this i mistakenly used the SOAP port of App server and when i tried checking out the config file i got this error

    wsadmin>LCConfigService.checkOutConfig("c:/temp","wpconnectionsCell01")
    Exception - com.ibm.ws.scripting.ScriptingException com.ibm.ws.scripting.Scripti
    ngException: WASX7070E: The configuration service is not available.
    wsadmin>exit


  • After connecting to DMGR, next step is to initialize the connection to lotus connection server for that we have to execute the connectionsConfig.py file which is located in the E:\IBM\WebSphere\AppServer\profiles\Dmgr01\bin directory, You can execute that file by executing following command on the wsadmin console

    wsadmin>execfile("E:\IBM\WebSphere\AppServer\profiles\Dmgr01\bin\connectionsConfig.py")

    If you open this file on your machine you will notice that it defines set of admin functions to check out and check in config files,... The checkin function takes care of validation configuration,...

  • Next step is to checkout the configuration file to your local drive by executing following command

    wsadmin>LCConfigService.checkOutConfig("c:/temp","wpconnectionsCell01")

    This will checkout set of files into your c:/temp directory


  • Once the file is checked out you can open it in text editor and make changes and once your done making changes save the file

  • Next step is to check in the changes back into the server that you can do by executing following command

    LCConfigService.checkInConfig()


  • After checking in your changes in deployment manager you can push them to nodes by executing

    wsadmin>synchAllNodes()


  • Last step is to restart the server for changes to take effect