Pages

Thursday, October 20, 2011

Using Environment Entries with WebSphere

I just spent the last few days trying to find an easy way to configure my web services deployed as annotated POJOs and I think I've found it. The idea is to provide an easy mechanism to change values through the administration console and have the java code pick up those changes at run time without redeploying. I thought about using a property file but this is inconvenient because we have a clustered environment and editing files in a deployment directory isn't the best idea.

@Resource Saves the Day
Anybody who used JNDI before the days of annotations knows that old style lookups were a little bit of a pain, but maybe I’m just being difficult. Here’s how I got it working - first thing is to add the proper entries in my web.xml:
<web-app...> ... <env-entry> <env-entry-name>DsixE/SomeParameter</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <env-entry-value>This really rocks</env-entry-value> </env-entry> ... </web-app>
Next I add an annotation to my code:
    @Resource(name="DsixE/SomeParameter")
    private String someParamter;
I build a WAR, deploy it to WebSphere and notice that I have a new entry in the administration console:


…. which allows me to change the value at any time:


Now the container injects the value defined into member variable someParameter at run time. This is the first time I’ve really used an <env-entry> so I guess this old dog can still learn new tricks.


Tuesday, October 18, 2011

Using Browser Web Services with SSO


A while ago I discussed how web services can be invoked using javascript in a browser, but there was a nagging problem that prevented me from deploying it into a production environment - how do I prevent a hacker from manipulating the user identifier in a request? Suppose we have an application that displays an end user's payroll information. In this case we need web service operation will retrieve information specific to the user's ID. We can't just send the user's identifier in the request because that would allow spoofing the ID of another person resulting in a breach of privacy.

Retrieving the User's Identity from the Container
In a servlet, it is relatively easy to retrieve the user's identity from the request, but this presents a problem if we're using JAX-WS annotated POJOs which have no concept of HTTP or a request object. How the heck do I figure out what the user ID is? I spent many hours looking at WS-Security but I got lost in all the details about policy sets and bindings and I just couldn't get anything to work. Then I found this writeup on developerworks:
http://www.ibm.com/developerworks/websphere/library/techarticles/1001_chung/1001_chung.html?ca=drs-

It is very close to what I wanted to do, but it uses WS-Security and I was dead in the water. Instead, I fell back on servlet security configuration to get the container to use the SSO LTPA token originating from WebSeal. This is the same way I would handle the situation if I was coding a servlet, but again - I'm working with POJOs, so no request object available. The developerworks article revealed the missing piece:
@Resource WebServiceContext wsCtx;

Putting it all Together
I coded a POJO and added JAX-WS annotations to expose it as a web service, then I added an @Resource that tells the container to pass the context:
package com.dsixe;

import javax.annotation.Resource;
import javax.jws.WebService;
import javax.xml.ws.WebServiceContext;

@WebService
public class ContactsService {
    @Resource WebServiceContext wsCtx;

    public String getName() {
        String userID = wsCtx.getUserPrincipal().toString();
        
        if (userID.equals("cdomingue"))
            return "Carl";
        if (userID.equals("csmith"))
            return "Christine";
        
        return "unknown: " + userID;
    }
    
}
Until this point I had never even looked at the web.xml bundled with the annotated POJOs into the WAR that I deployed to Axis2 since there are no servlets in it. I added the following security constraints to the web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>Jax-wsPolicyTest</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
  
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>ContactsService</web-resource-name>
      <url-pattern>
        /*
      </url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <description>
        Roles allowed to execute ContactsService
      </description>
      <role-name>AllAuthenticatedUsers</role-name>
    </auth-constraint>
  </security-constraint>
  
  <security-role>
    <description>All Authenticated Users Role</description>
    <role-name>AllAuthenticatedUsers</role-name>
  </security-role>  
</web-app>
I then deployed to the container and mapped the AllAuthenticatedUsers security role just like I would for any servlet. Protecting the resource caused the container to populate the WebServiceContext object and then I could retrieve the user ID.

Done!

EDIT: I was researching attachments with JAX-WS and found this very comprehensive writeup which I though would be good to read in this context.

EDIT: Here's another interesting read on this topic.

Monday, September 12, 2011

Can't Start Sametime

My current engagement uses the Lotus Sametime chat client and I use it on a daily (hourly) basis. Today, for no reason that I can discern, I was unable to start up the application after a computer reboot. The install creates a shortcut to C:\Program Files (x86)\IBM\Lotus\Sametime Connect\rcp\rcplauncher.exe and running that executable would do absolutely nothing.

After several attempts to figure out what was going on - including a failed attempt to reinstall the standard client - I found a lock file:
C:\Users\dsixe\AppData\Roaming\Lotus\Sametime\.rcp.lock
Delete the file, problem solved.

Wednesday, August 17, 2011

Using soapUI with WebSEAL

In previous posts I explained how it is possible to invoke web services through a browser using javascript, and sometimes the service needs to be debugged using soapUI through WebSEAL.

WebSEAL and Cookies
My current infrastructure uses WebSEAL as a reverse proxy, which means that a session must be established before any of the back end servers can be accessed, including the server hosting my web service. This poses a
problem with soapUI which doesn't provide a mechanism to log into WebSEAL directly, instead I can establish the session using a browser and then copy the cookies into the tool.

Log into the application server and authenticate as usual, then copy the cookies using firebug:












Then paste the
cookies into soapUI:


Now all requests will forward the cookie information which WebSEAL should recognize, allowing it to pass through as authenticated.

Thursday, August 4, 2011

Certification for Web Experience Factory

After 20 years of working in the IT industry, I thought it would be nice to get a certification in something, so I finally took the time to study the new features in WEF 7 and attempt the test.

Hard to Know Everything

Rarely does one have the opportunity to learn all the features of a product, usually a client will focus on a subset of functionality because of the way they do things. One can become an expert with those features, but this produces a false sense that the expertise extends to everything. My case was no exception. Fortunately I worked with WEF on several client engagements which gave me a broader experience.

Why is this relevant? I strongly suggest that anyone who wants to take a certification test attempt a sample test first, which is what I did. It allowed me to evaluate the level of difficulty (it was not easy) and also pointed out the gaps that I needed to focus on.

So there it is, my first certification and a new footnote on my email signature.

Monday, July 25, 2011

Ajax Busy Indicators in WEF

This is my first posting for websphere portlet factory re-branded as web experience factory. I guess I'll have to get used to WEF, at least it sounds like a word now although not very meaningful.

Looking Under the Hood

I often seem to find myself snooping in code, usually when trying to fix a bug or extend some OOTB functionality to my liking. In this case I had a need to display some busy indicators during an unusually long ajax call, which led me to the javascript that enables ajax in WEF.

The file of interest is WebContent\factory\clientjavascript\ajax\ppr.js and inside it, the following code can be found:
// Can overload these to, e.g., show a progress indicator for long operations.
    startLoad: function() { },
    endLoad: function() {  },

Since directly altering this file is not a good idea, we can instead take the advice in the comment and overload the functions in our own script:
dojo.require("dojox.widget.Standby");
   var standby = new dojox.widget.Standby({
         target: "basic2"
   });

wpf_ppr.startLoad = function(){standby.show()};
wpf_ppr.endLoad = function(){standby.hide()};

I've used the dojo standby widget which does a great job communicating to the user that something is taking place while disabling part of the UI, preventing unnecessary repeated requests.

 Done!

Tuesday, June 21, 2011

Building a WPF Project with Ant

Generally speaking, I never endorse deploying an application to a production environment unless it has been built from scratch in a clean environment. This means that building a portlet factory WAR from the eclipse IDE is not the way to do a production build, even though it is really convenient.

Building with Ant

Apache ant has been around for a long while now, is an excellent framework for building WAR files and is the tool that WPF uses to build its own WAR files. In older versions of WPF the build process was somewhat lacking, but the team at IBM has really done a great job with the latest release of the product (I think this improved build may have been available in 6.1.5, but we skipped over that and went straight to 7.0).
Below is an example of how I've taken the sample build.xml provided with the product and modified it to pull code from CVS and allow for project specific settings.

The build.properties is mostly unchanged:
CVSTag=HEAD

# Build source root directory !NOTE! This value must be an absolute path.
buildsrc.location=C:/wsad/GCPS_V7/PF_Build/WPF7/${project.name}

# Build temp location. This is where the temporary project will be created for building purposes
buildtmp.location=${buildsrc.location}/buildtmp

# The root directory for the Portlet Factory files.
wpf.artifacts.dir=C:/wsad/GCPS_V7/PF_Build/WPF7

# The output location for the generated war files.
builddist.location=${buildsrc.location}/dist

# The location of the pbportlet.jar file. This ships with portal
c2a.lib.dir=${wpf.artifacts.dir}/WebSphere Portal

# The name of the project (used for identifiying war files)
project.name=SET PROJECT NAME IN XML BUILD FILE

#-- The following properties should not need to be changed under normal circumstances. --

# The root directory of the temp project. This is used by projectDeploy.xml
project.location=${buildtmp.location}

# The WebContent directory of the project that will be created. This is also used by projectDeploy.xml
webcontent.location=${project.location}/WebContent

# The output directory for standalone deployment wars.
build.deployment.war.builddir=${builddist.location}

# The output directory for the portlet wars
wpf.portletwar.location=${builddist.location}

# The shipping version of Portlet Factory
version=7.0.0

Note that I've added a new property CVSTag which is used to identify which label or branch to use with CVS. I've also changed the project.name property to a self descriptive value that will stand out during a build if it is not changed. The intent is that this build.properties file does not contain any project specific values - those will be set elsewhere.

Next, I've taken the sample build.xml and made a few changes to it as well:
<project name="build" default="build4GCPS" basedir=".">

  <!-- define build script properties. -->
  <target name="properties" depends="propertiesOverride">  
    <property file="build.properties" />
  </target>

  <target name="build4GCPS" depends="properties, clean">    

    <!-- create temp directory structure for building -->
    <mkdir dir="${builddist.location}" />
    <mkdir dir="${buildtmp.location}" />  

    <cvs package="${project.name}" cvsroot=":pserver:blduser:XXXX@cvserver1:/usr/cvs/projects" tag="${CVSTag}"/>

    <!-- create temp dir to build on top of -->
    <copy todir="${buildtmp.location}/.deployment">
      <fileset dir="${buildsrc.location}/.deployment"/>
    </copy>    

    <copy todir="${webcontent.location}">
      <fileset dir="${buildsrc.location}/WebContent"/>
    </copy>    

    <!-- optional task to generate the logs folder in the temp location. 
         Specific war generation tasks will log their data to this directory. -->
    <mkdir dir="${buildsrc.location}/WebContent/WEB-INF/logs"/>
    
    <!-- expand factory image into the webcontent.location -->
    <ant antfile="${webcontent.location}/projectDeploy.xml" target="expandFactoryImage" >
      <property name="runtime.image" value="${wpf.artifacts.dir}/Images/factory${version}.zip" />
    </ant>
    
    <antcall target="addPackages"/>
    
    <!-- build source files -->
    <ant antfile="${webcontent.location}/projectDeploy.xml" target="compile" />     
         
    <!-- build 286 portlet war, and give it a new name. -->
    <ant antfile="${webcontent.location}/projectDeploy.xml" target="buildPortletWar" >
      <property name="wpf.portletapi.target" value="build286StandardPortletWar"/>
      <property name="project.name" value="${project.name}"/>
    </ant>               
     
  </target>

  <target name="addPackages">    
      <fail>Your project ant script is missing a target named "addPackages". This is used to add WPF featuresets.</fail>
  </target> 

  <target name="propertiesOverride">    
      <fail>Your project ant script is missing a target named "propertiesOverride".</fail>
  </target> 
 
  <!-- clean the project -->
  <target name="clean" depends="properties">   
    <delete dir="${buildsrc.location}" />
  </target>  
</project>

Noteworthy, from top to bottom:
  • added depends="propertiesOverride" to the properties target.
  • added a call to the ant cvs task. This will retrieve any code that was added to source control.
  • removed the sample antcall to addFeatureSet and replaced it with a call to my own addPackages.
  • finally at the bottom I've added default implementations for the new targets that will cause the build to fail if they're not defined elsewhere.

Now that I've done some prep work, I can build any WPF project with a short bit of xml saved as MyAwesomeWPFProject.xml:
<project name="mainDeployment" default="build4GCPS" basedir=".">
 
    <description>Template script for building a portlet factory WAR for deployment</description>

    <import file="build.xml"/>
     
    <target name="propertiesOverride">  
        <property name="project.name" value="MyAwesomeWPFProject"/>
    </target>

    <target name="addPackages">    
        <ant antfile="${webcontent.location}/projectDeploy.xml" target="addFeatureSet" >
          <property name="pkg" value="${wpf.artifacts.dir}/Packages/Tutorials.pkg" />
        </ant>
    </target>   
    
</project>

The heavy lifting here is done with the import of build.xml, which is just like adding everything in that file to my script, but allowing me to override any targets within it. In this case I've added my own propertiesOverride where I can change values that were defined in build.properties and addPackages where I can add any featuresets that my project requires.

Building my project is now as simple as
ant -f MyAwesomeWPFProject.xml 
and if I need to build a specific version that has been labeled in CVS, I can provide that label like this
ant -f MyAwesomeWPFProject.xml -DCVSTag=MyLabel
which will override the value in build.properties.

Piece of Cake
Prior to this, I had my own homebrew build process that I am now glad to throw away. It served me well for the last three years, but this is more robust and I really like the way I can add featuresets.