WebSphere CE – Deploy WAR on its own port

I was tasked by the powers that be to figure out how to deploy a WAR in WebSphere Application Server Community Edition (WSASCE) under a separate port from other applications that were already running on our test server. After much googling and frustration, since I have never worked with WSASCE before, I finally figured it out.

This ended up being fairly simple, however the the example from the WebSphere docs did not work for me. I then was able to finally find some Geronimo docs that ended up working for me after a few minor adjustments since the example was for deploying an EAR.

Essentially all that needs to be done is to instruct WSASCE to create a new web container to deploy your WAR to. This can all be done via the deployment plan for your WAR.

Here is a sample deployment plan. Please note that there are a few things you will need to tweak for your envirnoment, which are noted after the sample.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://geronimo.apache.org/xml/ns/j2ee/web/tomcat-2.0.1">
  <environment>
    <moduleId>
      <groupId>GROUP_ID</groupId>
      <artifactId>ARTIFACT_ID</artifactId>
      <version>VERSION</version>
      <type>war</type>
    </moduleId>
    <dependencies />
    <hidden-classes />
    <non-overridable-classes />
  </environment>
  <context-root>/YOUR_CONTEXT</context-root>
  <web-container>
    <gbean-link>TomcatWebContainer1</gbean-link>
  </web-container>

  <gbean name="TomcatWebConnector1">
    <attribute name="name">HTTP</attribute>
    <attribute name="host">0.0.0.0</attribute>
    <attribute name="port">8090</attribute>
    <attribute name="maxHttpHeaderSize">8192</attribute>
    <attribute name="maxThreads">150</attribute>
    <attribute name="minSpareThreads">25</attribute>
    <attribute name="maxSpareThreads">75</attribute>
    <attribute name="enableLookups">false</attribute>
    <attribute name="redirectPort">8453</attribute>
    <attribute name="acceptCount">100</attribute>
    <attribute name="connectionTimeout">20000</attribute>
    <attribute name="disableUploadTimeout">true</attribute>
    <reference name="TomcatContainer">
      <name>TomcatWebContainer1</name>
    </reference>
    <reference name="ServerInfo">
      <name>ServerInfo</name>
    </reference>
  </gbean>

  <gbean name="TomcatWebContainer1">
    <attribute name="catalinaHome">var/catalina</attribute>
    <reference name="EngineGBean">
      <name>TomcatEngine1</name>
    </reference>
    <reference name="ServerInfo">
      <name>ServerInfo</name>
    </reference>
    <reference name="WebManager">
      <name>TomcatWebManager</name>
    </reference>
  </gbean>

  <gbean name="TomcatEngine1">
    <attribute name="className">org.apache.geronimo.tomcat.TomcatEngine</attribute>
    <attribute name="initParams">
      name=Geronimo1
    </attribute>
    <reference name="DefaultHost">
      <name>TomcatHost1</name>
    </reference>
    <references name="Hosts">
      <pattern>
        <name>TomcatHost1</name>
      </pattern>
    </references>
    <reference name="RealmGBean">
      <name>NoSecurityRealm</name>
    </reference>
  </gbean>

  <gbean name="TomcatHost1">
    <attribute name="className">org.apache.catalina.core.StandardHost</attribute>
    <attribute name="initParams">
      name=localhost1
      appBase=
      workDir=work1
    </attribute>
  </gbean>

  <gbean name="NoSecurityRealm">
    <attribute name="className">org.apache.geronimo.tomcat.realm.TomcatEJBWSGeronimoRealm</attribute>
  </gbean>
</web-app>

You will need to replace the following placeholders in the above XML with values for your own WAR.

  • GROUP_ID – Your group ID
  • ARTIFACT_ID – Your artifact ID
  • VERSION – The version of your WAR
  • YOUR_CONTEXT – The root context for your WAR

You may also modify the following for your own environment if desired

  • TomcatWebContainer1 is the name of the new web container.
  • TomcatEngine1 is the name of the new engine for the new web container.
  • TomcatHost1 is the name of the new virtual host in the new web container.
  • TomcatWebManager is the name of the web manager of the new web container.
  • TomcatWebManager is the name of the web manager defined in the initial server.
  • TomcatJAASRealm is the name of the Tomcat realm for authenticating and authorizing users.
  • TomcatJAASRealm is the name of the realm defined in the initial server configuration.
  • 0.0.0.0 is replaced with the host name or IP address of the web containers host. The value localhost will restrict access to requesters in the server’s host. Use the value 0.0.0.0 if you  don’t want to provide the specific IP Address or host name.
  • 8090 is replaced with the port number where the HTTP connector will listen for requests.

To deploy your WAR into the new web container, include the <web-container> element and specify a <gbean-link> to the new container where TomcatWebContainer1 is replaced with the name of the container GBean.

 ...
 <web-container>
   <gbean-link>TomcatWebContainer1</gbean-link>
 </web-container>
 ...

Now just deploy your WAR with your new handy-dandy deployment plan.

  • Share/Bookmark

Mac – Eclipse Galileo – Installing GWT Plugin

Ok, So I was having some real issues when I tried to install the GWT plugin from Google into Eclipse Galileo on my Mac in order create a GWT 2.0 application. After installing the plugin (via the update site – http://dl.google.com/eclipse/plugin/3.5) you need to restart Eclipse. However when Eclipse restarted all I got was an error popup telling me to check the Eclipse log file.

The log file contained the following …

!SESSION Thu Jan 14 16:42:24 CST 2010
------------------------------------------
!ENTRY org.eclipse.equinox.launcher 4 0 2010-01-14 16:42:24.080
!MESSAGE Exception launching the Eclipse Platform:
!STACK
java.lang.ClassNotFoundException:
org.eclipse.core.runtime.adaptor.EclipseStarter
	at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:316)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:556)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:514)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1311)

Well, luckily I was able to come up with a workaround.

For development I also use the Subversive and Maven Integration plugins so here is what I did. (This assumes you know how to install plugins via update sites)

  1. Start with a fresh copy of Galileo
  2. Add all the plugins without restarting Eclipse in the following order.
  3. Add the GWT plugin from the update site
    1. http://dl.google.com/eclipse/plugin/3.5
  4. Add the Subversive plugin and required SVN connectors from Polarion
    1. http://community.polarion.com/projects/subversive/download/eclipse/2.0/galileo-site/
  5. Add the maven integration plugin
    1. http://m2eclipse.sonatype.org/update
  6. After all the plugins are installed restart Eclipse.

And there you have it. Eclipse starts up without any problems.

  • Share/Bookmark

Open Office: invisible page break

When working on an open office document, I periodically come across an invisible page break (there’s no blue bar above the new page and I can’t seem to get rid of it easily).

This always happens to me when I am using Open Office’s numbering to outline requirements. The page break is very annoying. When you try to remove it, it’ll pull one line of text up even with the line on the previous page. You can add a carriage return to separate the two lines, but you have to reformat the second line.

To get rid of this annoying space, you can start adding carriage returns where the space begins and keep adding them until you have started a new page. Then, you can highlight all the added carriage returns and hit the delete key and it gets rid of the invisible page break.

  • Share/Bookmark

PowerBuilder 11 “Invalid character value for cast specification”

I was given the task of migrating a set of powerbuilder applications from version 8 to 11.5.

The PowerBuilder applications required a database connection. I went through their connection utility and the only connection type that I could get to work with my Microsoft SQL Server 2000 was ODBC. I configured it and PowerBuilder told me that it was working properly. I configured all of the applications to use the ODBC.

All of the applications worked properly, except one: the only application that uses stored procedures. The stored procedures that were failing had input and output parameters. They kept giving me the error message of

     [Microsoft][ODBC SQL Server Driver] Invalid character value for cast specification

I figured the problem was just a configuration issue. After extensive research, I found one forum that had a solution: adding

      StripParmNames='Yes',CallEscape='No'

to the end of the DBParm. I tried this. It fixed the “Invalid character” problem, but then I started getting the following error message:

     [Microsoft][ODBC SQL Server Driver] Incorrect syntax near 'OUTPUT'

Since everything seems to change when you tweak parameters, I tried every combination of parameters that I could see as possibly making a difference (both on the ODBC connection configuration and the DBParm settings).

Finally, I looked back at the original configuration and noticed that the DBMS was “MSS (Msoft) SQL Server 2000″. I started researching PowerBuilder database connection options and found this blog:

http://sureshmoorthy.blogspot.com/2008/05/migrating-to-powerbuilder-11.html?showComment=1239682860000#c3657249529659890174

Mainly, that PowerBuilder’s native driver (MSS) had been removed from PowerBuilder version 10 and that PowerBuilder 11 replaced it with SNC to support SQL Server 2000 & 2005. Finally, I had found my solution. I switched my configuration to use the SNC driver and it solved all the problems I was having. Here’s the new configuration:

  DBMS=SNC
  Database=<database name>
  ServerName=<server IP address>
  LogId=<db user name>
  LogPassword=<db password>
  Lock=RU
  DbParm=staticbind=0,Async=1,AppName='<app name>'
  • Share/Bookmark

Struts2 Rest Webservice

I started out trying to create a rest webservice for a project I’m working on with Mersoft. After a little bit of research, I discovered Apache has a rest plugin that enables Struts 2 to perform as a rest webservice.

I followed the documentation at http://struts.apache.org/2.x/docs/rest-plugin.html and created my rest webservice. However, I came across a couple of issues that I could not find any documentation as to how to address. The first of these issues is that the documentation says that the rest plugin supports xml, json, and xhtml. I downloaded the rest example’s source jar and tore it apart, but there was nothing to address the xhtml. Next I installed the pre-compiled war and to my amazement, the xhtml worked. After I tore apart the war, I discovered what is required for the xhtml (I have that documented here)

The second issue I came across was that I didn’t like how the xml was being displayed (instead of using just the object name as the xml tag, it included the package name). How to customize the xml display is documented here.

The last struggle I had was that I wanted to use Spring to inject classes into my controllers. I searched online and found other developers asking how to do this, but could find nothing on how to actually do this. How to use Spring for the controllers is documented here.

I am writing this blog to document what I did to create a rest webservice and to share what I did to resolve the issues I had.

Creating a Rest Webservice

To create a rest webservice, Apache created a rest plugin that works with Struts 2. This plugin was written with the intent of being used with the struts 2 convention plugin.

These are the only dependencies needing to be added to a Struts2 project:

<dependency>
   <groupId>org.apache.struts</groupId>
   <artifactId>struts2-rest-plugin</artifactId>
   <version>${struts.version}</version>
</dependency>
<dependency>
   <groupId>org.apache.struts</groupId>
   <artifactId>struts2-convention-plugin</artifactId>
   <version>${struts.version}</version>
</dependency>

The web.xml must use the StrutsPrepareAndExecuteFilter for the struts 2 convention plugin (this Filter can also be used with other struts 2 applications as long as the application doesn’t need another filter that has to access the action’s context information). The web.xml should look like the following:

<web-app id="WebApp_9" version="2.4"
   xmlns="http://java.sun.com/xml/ns/j2ee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

   <filter>
      <filter-name>action2</filter-name>
      <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
   </filter>

   <filter-mapping>
      <filter-name>action2</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>

</web-app>

With the convention plugin, the only thing required in struts.xml is any changes in configuration needed for the convention plugin. If the web service is written without the convention plugin, you would just have to define each controller in the struts.xml (the same way an action is defined). The convention plugin just makes this defining process unnecessary.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

   <!-- The struts rest plugin allows you to define the paths used for the webservice.
   If you have groupings of controllers and you want users to access the controllers based on the groupings, you can do this in the following way:
      1. create a package for each grouping (i.e. com.mersoft.test.group1, com.mersoft.test.group2)
      2. set the struts.convention.package.locators as the last package name before the groupings (i.e. "test")
      3. access the rest webservice by {application base}/{group name}/{controller name} (i.e. {application base}/group1/controller1, {application base}/group2/controller2)
   If you want all controllers accessed from the application base:
      1. put all the controllers in the same package (i.e. com.mersoft.controller).
      2. set the struts.convention.package.locators as the last package name (i.e. "controller")
     3. access the rest webservice by {application base}/{controller name} (i.e.  {application base}/controller1)
   -->
   <constant name="struts.convention.package.locators" value="[PACKAGE TO LOOK FOR CONTROLLERS]"/>  <!-- The convention plugin defaults this to "action,actions,struts,struts2" -->
   <constant name="struts.convention.action.suffix" value="Controller"/>  <!-- The convention plugin defaults this to "Action" -->
   <constant name="struts.convention.action.mapAllMatches" value="true"/>   <!-- The convention plugin defaults this to "false" -->
   <constant name="struts.convention.default.parent.package" value="rest-default"/>  <!-- The convention plugin defaults this to "convention-default". The "rest-default" is defined by the struts2 rest plugin -->
</struts>

The objects being passed through the rest webservice need to be defined. These are just POJOs. Here’s an example:

package com.mersoft.web.model;

public class TestObject {

   private String id;
   private String someString;
   private long someLong;
   private String anotherString;

   public String getId() {
      return id;
   }
   public void setId(String id) {
      this.id = id;
   }
   public String getSomeString() {
      return someString;
   }
   public void setSomeString(String someString) {
      this.someString = someString;
   }
   public long getSomeLong() {
      return someLong;
   }
   public void setSomeLong(long someLong) {
      this.someLong = someLong;
   }
   public String getAnotherString() {
      return anotherString;
   }
   public void setAnotherString(String anotherString) {
      this.anotherString = anotherString;
   }
}

The rest webservice uses “controllers” for data manipulation. The controllers must implement ModelDriven<T>. In my example below I added the Validateable interface and the ValidationAwareSupport extension. This allows the controller to be validated prior to any executions.

package com.mersoft.web.controller;

import org.apache.struts2.rest.DefaultHttpHeaders;
import org.apache.struts2.rest.HttpHeaders;

import com.mersoft.web.model.TestObject;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Validateable;
import com.opensymphony.xwork2.ValidationAwareSupport;

public class TestController extends ValidationAwareSupport implements ModelDriven<TestObject>, Validateable{
   /**
   *
   */
   private static final long serialVersionUID = -1045170916847707617L;

   private TestObject testObject = new TestObject();

   private String id;

   public TestController() {
      super();
   }

   public TestObject getModel() {
      //TODO: populate test object
      return testObject;
   }

   // GET /test/1
   public HttpHeaders show() {
      return new DefaultHttpHeaders("show");
   }

   // GET /test
   public HttpHeaders index() {
      return new DefaultHttpHeaders("index");
   }

   // POST /test
   public HttpHeaders create() throws Exception {
      //TODO: creation action here

      return new DefaultHttpHeaders("success");
   }

   //PUT /test/1
   public String update() throws Exception {
      //TODO: update action here
      return "success";
   }

   // DELETE /test/1
   public String destroy() {
      //TODO: delete action here
      return "success";
   }

   public void validate() {
      // TODO add validation here

   }

   public String getId() {
      return id;
   }

   public void setId(String id) {
      this.id = id;
   }

}

To access the webservice, you just need to make the request. (Look above at the type of request and the parameters that need to be passed to access each method.)

For xml requests, add .xml to the end of the url (i.e. Making a GET request to {application base}/test.xml will access the index method and return an xml response).
For json requests, add .json to the end of the url (i.e. Making a GET request to {application base}/test.json will access the index method and return a json response).

Adding custom methods

The rest webservice plugin is designed to be flexible. If there’s actions you don’t want the user to be able to perform, you simply omit it from the controller. If there’s a special action that is not part of the standard actions, you just add the custom action to your controller:

   public String test() {
      return "test";
   }

To access the above action, make a GET request to {application base}/test/1/test.xml.

Accessing the webservice via xhtml

Out of the box, the rest webservice supports xhtml, however you must create the jsp pages the webservice must display to the user.

By default, the convention plugin expects these files in WEB-INF/content/ (this can be changed by adding <constant name=”struts.convention.result.path” value=”/WEB-INF/{jsp directory}”/> to struts.xml)   . The file names must be {controller name}-{request name}.jsp. For example, for the jsp corresponding to the show action in TestController, the file name should be test-show.jsp.

NOTE: To display objects returned by the getModel() method, you just need to reference the object name within the model. (i.e. to display someString from the TestObject, you can reference it as ${someString} in the jsp)

For requests needing to be sent as a PUT or a DELETE, you can attach “?_method={REQUEST TYPE} to the end of the action method in the form and send the request as a post.

Customizing xml response

To make the xml cleaner, I wanted to display just the object name without the package the object is in. After doing a little bit of research, I discovered that the struts-rest plugin uses XStream. The latest version of XStream allows annotations to customize the xml. Since the struts-rest plugin doesn’t use the latest XStream, I created my own ContentTypeHandler using the latest XStream.

Be sure to add the XStream dependency to the classpath:

<dependency>
   <groupId>com.thoughtworks.xstream</groupId>
   <artifactId>xstream</artifactId>
   <version>1.3.1</version>
</dependency>

The ContentTypeHandler I created is very similar to that of the rest plugin’s XML content handler. The main difference is that I told XStream to process annotations.

NOTE: This must implement org.apache.struts2.rest.handler.ContentTypeHandler to work with the struts rest plugin.

package com.mersoft.rest.handler;

import java.io.IOException;
import java.io.Reader;
import java.io.Writer;

import org.apache.struts2.rest.handler.ContentTypeHandler;

import com.thoughtworks.xstream.XStream;

/**
* Handles XML content
*/
public class XStreamHandler implements ContentTypeHandler {

   public String fromObject(Object obj, String resultCode, Writer out) throws IOException {
      if (obj != null) {
         XStream xstream = new XStream();
         xstream.processAnnotations(obj.getClass());
         xstream.toXML(obj, out);
      }
      return null;
   }

   public void toObject(Reader in, Object target) {
      XStream xstream = new XStream();
      xstream.processAnnotations(target.getClass());
      xstream.fromXML(in, target);
   }

   public String getContentType() {
      return "application/xml";
   }

   public String getExtension() {
      return "xml";
   }
}

To use the new content handler, I had to add the following line to my struts.xml:

   <bean type="org.apache.struts2.rest.handler.ContentTypeHandler" name="myXml" />

To tell the rest plugin to use my new content handler, I simply have to add the bean name to a struts.properties file:

struts.rest.handlerOverride.xml=myXml

Now I can use the XStream annotations. Here’s an example of how to use the annotations with the TestObject:

package com.mersoft.web.model;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamOmitField;

@XStreamAlias("test")
public class TestObject {

   private String id;
   private String someString;

   @XStreamOmitField
   private long someLong;

   private String anotherString;
   ...

For more on XStream’s annotations see http://xstream.codehaus.org/annotations-tutorial.html

Using Spring Dependency Injection on the Controllers

With the rest webservice I was creating, I wanted to spring inject my managers into my controllers. After searching on the web, I couldn’t find any answers on how to do this. Analyzing what I have accomplished so far, I realized that the controllers were no more than specialized actions. My biggest obstacle was discovering how to use the struts2 spring plugin with the struts2 convention plugin. The struts2 spring plugin pulls the “class” name from the struts.xml file. If the “class” name matches a bean definition, then the spring plugin pulls the bean. If it does not match a bean definition, then it pulls the class. After discovering this, it was pretty clear as to how to get this to work with the struts convention plugin.

First, I added the struts2 spring plugin dependency:

<dependency>
   <groupId>org.apache.struts</groupId>
   <artifactId>struts2-spring-plugin</artifactId>
   <version>${struts.version}</version>
</dependency>

As the standard struts2 spring plugin requires, we must configure it in web.xml by setting up the context path and including the context listener:

   <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>
         classpath:/applicationContext-resources.xml
         classpath:/applicationContext-dao.xml
         classpath*:/applicationContext.xml
      </param-value>
   </context-param>

   <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
   </listener>

The only thing left was to set up the applicationContext.xml. In order for the spring plugin to pull the beans based on this configuration, the bean id must match the bean class.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
   xmlns:tx="http://www.springframework.org/schema/tx"
   xsi:schemaLocation="
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

   <bean id="com.mersoft.web.controller.TestController" >
      <constructor-arg ref="testManager" />
   </bean>
</beans>

NOTE: This will call the TestController constructor with the constructor containing the “testManager” class (i.e.: public TestController(TestManager testManager))

For more information on the struts2 spring plugin, you can see http://struts.apache.org/2.0.8/docs/spring-plugin.html

With this last issue resolved, I was able to do everything I wanted to do with my rest webservice. I hope this is helpful.

  • Share/Bookmark

GWT – Hide Tabs in TabPanel

I was continuing my work on a sample GWT app and needed the ability to use a Menu with CheckItems to show/hide tabs in a tab panel. Here is how I did it.

Note: This post does not cover the creation of the TabPanel, Toolbar and ToolbarButton with a Menu.

Step 1: Override the Ext.TabPanel. (Note: This was just added to a JavaScript file I added to my application. I did not modify the ext-all.js file)

Ext.override(Ext.TabPanel, {

  hideTabStripItem : function(item){
    //Don't hide if its the last tab
    var cnt = this.items.length;
    var visibleTabs = 0;
    for(var i = 0; i < cnt; i++ ){
      if(!this.isTabStripItemHidden(i)){
        visibleTabs++;
      }
    }
    if(visibleTabs > 1){
      item = this.getComponent(item);
      var isactive = (item == this.activeTab);	    

      var el = this.getTabEl(item);
      if(el){
        el.style.display = 'none';
          this.delegateUpdates();
      }

      if(isactive){
        //Set the next visible tab active
        var curIndx = 0;
        var indx = 0;
        for(var i = 0; i < cnt; i++ ){
          var tab = this.getComponent(i);
          if(tab.id == item.id){
            curIndx = i;
            break;
          }
        }

        //Check for the next available tab
        for(var i = (curIndx+1); i < cnt; i++ ){
          if(!this.isTabStripItemHidden(i)){
            indx = i;
            break;
          }
        }

        if(indx == 0){
          //Check for a tab before the current one
          for(var i = 0; i < curIndx; i++ ){
            if(!this.isTabStripItemHidden(i)){
              indx = i;
              break;
            }
          }
        }

        this.setActiveTab(indx);
      }
    }
    if(visibleTabs == 1){
      throw "Could not hide the tab";
    }
  },

  unhideTabStripItem : function(item){
    item = this.getComponent(item);
    var el = this.getTabEl(item);
    if(el){
      el.style.display = '';
      this.delegateUpdates();
    }

    this.setActiveTab(item);
  },

  isTabStripItemHidden : function(item){
    item = this.getComponent(item);
    var el = this.getTabEl(item);
    if(el){
      return el.style.display == 'none';
    }
    //return true since it does not exist.
    return true;
  }
});

Step 2: Create a CheckItem with a listener to show/hide the tab (add it to a ToolbarButton’s Menu).

public CheckItem createTabCheckItem(String menuTitle, final String tabId, final TabPanel tabPanel){
  CheckItem ci = new CheckItem(menuTitle);
  ci.addListener(new CheckItemListenerAdapter() {
    public void onCheckChange(CheckItem item, boolean checked) {
      if (checked) {
        tabPanel.unhideTabStripItem(tabId);
      } else {
        try {
          tabPanel.hideTabStripItem(tabId);
        } catch (Exception e) {
          item.setChecked(true);
        }
      }
    }
  });
  ci.setHideOnClick(false);
  return ci;
}
  • Share/Bookmark

How to Configure Axis 1.4 WS Client (Java) to accept GZIP compressed SOAP messages.

In this blog I will explain how to write an Axis 1.4 Client that can accept GZIP compressed SOAP messages. The GZIP compression feature of Axis 1.x is not well documented on the Apache website, so you have to search for it in blogs like this one. You can also use Axis 1.x to write server side code to compress SOAP messages, but I will not cover that in this blog.

Steps to write Axis 1.4 Client code:

  1. Create the Axis Client code: Run wsdl2java on your wsdl to generate the client side stubs. Write code to connect to the correct web service endpoint. You can find examples on the Axis website and numerous blogs.
  2. Use a TCP/IP monitoring software so that you can see the request and response messages. If you’re using Eclipse, there’s already a built-in “TCP/IP Monitor”. The monitor will tunnel a TCP connection from a local port to the web service endpoint. Set the Monitor Type to TCP/IP and not HTTP.
  3. Test that you can get to the web service using the TCP/IP monitor. (Compression in not enabled at this point)
  4. Add an HTTP Header to the request to inform the server that the client accepts GZIP compression. (HTTP Header is not to be confused with the SOAP Header).
  5. URL url = new URL(”www.acme.corp/webservice/MyService”);
    AcmeServiceLocator service = new AcmeServiceLocator();
    AcmeServicePortType port = service.getacmeServiceHttpPort(url);
    ((Stub) port)._setProperty(HTTPConstants.MC_ACCEPT_GZIP,Boolean.TRUE);
    QueryResult result = port.queryMyStore(param);
  6. Change the Axis Client configuration file to use “CommonsHTTPSender” instead of the default “HTTPSender” as the http transport. This transport knows how to deal with GZIP compression, the default one does not. The Axis configuration file can be found in the Axis.jar (client-config.wsdd). This file needs to be modified to use “CommonsHTTPSender”, see below. (I tested this configuration with Axis 1.4; older versions might have a slightly different configuration) The updated client-config.wsdd file needs to be put on the Classpath.
  7. <?xml version=”1.0″ encoding=”UTF-8″?>
    <deployment name=”defaultClientConfig”
    xmlns=”http://xml.apache.org/axis/wsdd/”
    xmlns:java=”http://xml.apache.org/axis/wsdd/providers/java”>
    <globalConfiguration>
    <parameter name=”disablePrettyXML” value=”true”/>
    <parameter name=”enableNamespacePrefixOptimization” value=”false”/>
    </globalConfiguration>
    <transport name=”http” pivot=”java:org.apache.axis.transport.http.CommonsHTTPSender”/>
    <transport name=”http” pivot=”java:org.apache.axis.transport.http.HTTPSender”/>
    <transport name=”local” pivot=”java:org.apache.axis.transport.local.LocalSender”/>
    <transport name=”java” pivot=”java:org.apache.axis.transport.java.JavaSender”/>
    </deployment>
  8. Now run your code! In the TCP/IP monitor you should see the HTTP Header “Accept-Encoding: gzip” in the request. In the response you should see the HTTP Header “Content-encoding: gzip” and some binary data on the bottom. This is the server telling the client that it is sending back GZIP-ed data.
  9. *** Request Message ***
    POST /webservice/MyService HTTP/1.1
    Content-Type: text/xml; charset=utf-8
    SOAPAction: “”
    User-Agent: Axis/1.4
    Accept-Encoding: gzip
    Host: localhost:9001
    Transfer-Encoding: chunked
    28a
    <?xml version=”1.0″ encoding=”UTF-8″?><soapenv:Envelope …

    *** Response Message ***
    Server: Sun-ONE-Web-Server/6.1
    Date: Thu, 27 Aug 2009 15:42:25 GMT
    Content-type: text/xml;charset=UTF-8
    X-powered-by: Servlet 2.4; JBoss-4.0.5.GA Tomcat-5.5
    Content-encoding: gzip
    Vary: Accept-Encoding
    Transfer-encoding: chunked
    479

  • Share/Bookmark

GWT Disable ComboBox options

I have been working on an application that required the ability to disable ComboBox options. I googled to see if someone else had already done this and I found the following post. I used the code from the post as a starting point and added additional code to flush out a few more features.
The main features I added dealt with the option selecting itself, such as using the up and down keys to select options. In order to tie everything together I have posted all the critical code in its entirety to avoid confusion.

To start off with I added the following to a JavaScript file that I included in my application. The main difference from this code and the code from the original post above is the addition of the selectNext and selectPrev overrides. I also had to modify the onViewClick override since I was using Strings instead of booleans for the selectable value (see the SimpleStore created later).

Ext.override(Ext.form.ComboBox, {

  tpl: '<tpl for=".">' +
    '<div class="x-combo-list-item <tpl if="selectable == \'false\'">x-combo-list-item-unsel</tpl> ">' +
    '<img src="{image}"> ' +
    '{text}<div class="x-clear"></div></div>' +
    '</tpl>',

    // private

    onViewClick : function(doFocus){

        var index = this.view.getSelectedIndexes()[0];
        var r = this.store.getAt(index);
        var sel = r.get('selectable');
        if(sel && sel == 'false'){
            return;
        }

        if(r){
            this.onSelect(r, index);
        }

        if(doFocus !== false){
            this.el.focus();
        }
    },

    selectNext : function(){
        var ct = this.store.getCount();
        if(ct > 0){
        	var hasSelectable = this.store.getAt(0).get('selectable');

        	if(hasSelectable){
        		//check for selectable records
        		//Get the next selectable record
        		var currentIndex = this.selectedIndex;
        		var nextIndex = currentIndex;

        		while(true){
        			if(nextIndex < ct-1){
        				var r = this.store.getAt(++nextIndex);
        				if(r.get('selectable') == 'true'){
        					break;
        				}
        			}else{
        				break;
        			}
        		}

        		this.select(nextIndex);
        	}else{
        		//select the original way
        		if(this.selectedIndex == -1){
                    this.select(0);
                }else if(this.selectedIndex < ct-1){                     this.select(this.selectedIndex+1);                 }         	}         }     },     selectPrev : function(){         var ct = this.store.getCount();         if(ct > 0){
        	var hasSelectable = this.store.getAt(0).get('selectable');

        	if(hasSelectable){
	        	//Get the next selectable record
	        	var currentIndex = this.selectedIndex;
	        	var nextIndex = currentIndex;

				while(true){
					if(nextIndex != -1){
						var r = this.store.getAt(--nextIndex);
						if(r.get('selectable') == 'true'){
							break;
						}
					}else{
						break;
					}
				}

				this.select(nextIndex);
        	}else{
        		//select the original way
        		if(this.selectedIndex == -1){
                    this.select(0);
                }else if(this.selectedIndex != 0){
                    this.select(this.selectedIndex-1);
                }
        	}
        }
    },
}    

);

I then added the following CSS class to my application’s CSS file…

.x-combo-list-item-unsel{
  -moz-opacity:0.5;
  opacity: .50;
  filter:alpha(opacity=50);
}

And now for some java code.

I created the Store for my ComboBox with the following code. If you read the JavaScript code above you will notice that it depends on the Records in the Store having the “selectable” attribute.

Note: The getOptions() method is coming up next.

final Store store = new SimpleStore(new String[]{"image", "text", "value", "selectable"}, getOptions(false));
store.load();

In my application I needed to be able to reverse the disabled options so I created the following method to generate the Object array used for my Store.

private Object[][] getOptions(boolean isDisabled){
  Object[][] results = new Object[][]{
    new Object[]{"images/image1.gif", "Option 1", "VALUE1", !isDisabled},
    new Object[]{"images/image2.gif", "Option 2", "VALUE2", !isDisabled},
    new Object[]{"images/image3.gif", "Option 3", "VALUE3", isDisabled},
    new Object[]{"images/image4.gif", "Option 4", "VALUE4", !isDisabled},
    new Object[]{"images/image5.gif", "Option 5", "VALUE5", !isDisabled}
  };

  return results;
}

Then in a listener on a TextField I added the following code to switch the disabled options…

Store store = combobox.getStore();
store.removeAll();
store.add(getOptionRecords(true));  //Note: send true or false depending on which options you want disabled.

Here is the getOptionRecords() method used in the code above…

private static Record[] getOptionRecords(boolean isDisabled){
  Store store = new SimpleStore(new String[]{"image", "text", "value", "selectable"}, getPhoneLabels(isDisabled));
  store.load();
  return store.getRecords();
}

I hope that this post helps someone out.

  • Share/Bookmark

Palm Pre / PreDev Camp

The buzz around the Palm Pre has been fun to be a part of. When I heard about this phone’s development, I thought I could finally have something at least comparable to an iPhone. I have been torn between a love for all things Apple and supporting my favorite telecom company, Sprint. Finally, something to ease my pain.

I’ve made the Palm Pre my summer project. When it was released in June, I waited in line for it. This kind of excitement is the spice of life for me. I was happy with phone, but still was wanting more.

Later that month, the phone got rooted. A new sense of joy to find out the device is Linux-based, so the code is easily readable. But more interestingly, a little community started to form. IRC channels went up, we all shared what we saw. The possibilities now became endless with what we could do with this phone.

Last weekend (August 8), we had PreDev Camp. This event was held in various locations all over the country and the world. I am amazed by this grass-roots effort. In Kansas City, Derek Gathright stepped up and lead the effort. He readily admits he had help, but he did an awesome job. There were about 70 people in attendance.

We met in a hall-like room at The Screenland in North Kansas City. We could not have asked for a better space. We had an Internet connection, a few of us brought WiFi routers, and we even had some MiFis from Sprint.

We set up a session area with a projector and screen to do presentations and a coding area which had round tables to work at. The round tables helped up huddle together and talk more.

Palm donated Mitch Allen’s book which was a great guide and reference to all of us. People like Mike Gaffney, Rick Boatright, and Kevin Williams from Handmark gave presentations and generally helped all of us with our questions. Ram Koya from Sprint gave a presentation on the Sprint Sandbox.
Barbara Ballard from Little Springs Design gave a talk on mobile design.

All in all, it was great experience. The most valuable part was the networking with other Palm/webOS developers. I’m sure it jump-started interest in developing on this platform for most of the attendees.

I plan to blog about my Palm/webOS development experience in upcoming weeks.

  • Share/Bookmark

GWT-EXT: Retain Checkbox selections in page-able GridPanel

When working on a sample project for Mersoft using GWT-EXT I came across a use case that required a data table that was page-able, had a checkbox selection per row, and needed to maintain the checkbox selections when paging.

Here is what I ended up doing. It may not be pretty, but hey, its a sample.

I made a few updates to the CheckboxSelectionModel.js in order to properly update the checkbox in the Header row when a user manually (un)selects all the rows. This required that I comment out the CheckboxSelectionModel definition in the ext-all.js and include the CheckboxSelectionModel.js in my HTML. I updated the onMouseDown function as follows…

onMouseDown : function(e, t){
        if(e.button === 0 && t.className == ‘x-grid3-row-checker’){ // Only fire if left-click
            e.stopEvent();
            var row = e.getTarget(‘.x-grid3-row’);
            if(row){
                var index = row.rowIndex;
                if(this.isSelected(index)){
                    this.deselectRow(index);
                }else{
                    this.selectRow(index, true);
                }

                //Get the CheckboxHeader so it can be updated.
               //Loop through the header cells since the user may have re-ordered them…
               var cbHeader = null;
               var i = 0;
               var view = this.grid.getView();
               var headerCellEl = view.getHeaderCell(i);

               while(cbHeader == null && headerCellEl != null){
                  if(headerCellEl.className.indexOf(‘x-grid3-td-checker’) > -1){
                     cbHeader = headerCellEl.firstChild;
                     cbHeader = Ext.fly(cbHeader);
                  }
                  headerCellEl = view.getHeaderCell(++i);
               }

                if (cbHeader) {
                if (this.grid.getStore().getCount() == this.selections.length) {
                   //all the rows are selected, so check the header…
                   cbHeader.addClass(‘x-grid3-hd-checker-on’);
                } else {
                   //not all the rows are selected, so uncheck the header…
                   cbHeader.removeClass(‘x-grid3-hd-checker-on’);
                }
             }
            }
        }
    },

I also used the code from the URL http://extjs.com/forum/showthread.php?t=45723&highlight=getselectionmodel, however it too needed to be modified to update the Header checkbox.

I created a CheckBoxMemory.js with the following code and included it in my HTML…

Ext.namespace(‘Ext.ux.plugins’);

Ext.ux.plugins.CheckBoxMemory = Ext.extend(Object, {
   constructor : function(config) {
      if (!config)
         config = {};

      this.prefix = ‘id_’;
      this.items = {};
      this.idProperty = config.idProperty || ‘id’;
   },

   init : function(grid) {
      this.grid = grid;
      this.view = grid.getView()
      this.store = null;
      this.sm = grid.getSelectionModel();
      this.sm.on(‘rowselect’, this.onSelect, this);
      this.sm.on(‘rowdeselect’, this.onDeselect, this);
      this.view.on(‘refresh’, this.reConfigure, this);
   },

   reConfigure : function() {
      this.store = this.grid.getStore();
      this.store.on(‘clear’, this.onClear, this);
      this.store.on(‘datachanged’, this.restoreState, this);
   },

   onSelect : function(sm, idx, rec) {
      this.items[this.getId(rec)] = true;
   },

   onDeselect : function(sm, idx, rec) {
      delete this.items[this.getId(rec)];
   },

   restoreState : function() {
      if (this.store != null) {
         var i = 0;
         var sel = [];
         this.store.each( function(rec) {
            var id = this.getId(rec);
            if (this.items[id] === true)
               sel.push(i);

            ++i;
         }, this);
         if (sel.length > 0){

            this.sm.selectRows(sel);
         }

         var cbHeader = null;
         var j = 0;
         var view = this.grid.getView();
         var headerCellEl = view.getHeaderCell(j);

         while (cbHeader == null && headerCellEl != null) {

            if (headerCellEl.className.indexOf(‘x-grid3-td-checker’) > -1) {
               cbHeader = headerCellEl.firstChild;
               cbHeader = Ext.fly(cbHeader);
            }
            headerCellEl = view.getHeaderCell(++j);
         }

         if (cbHeader) {
            if (this.store.getCount() == this.sm.selections.length) {
               cbHeader.addClass(‘x-grid3-hd-checker-on’);
            } else {
               cbHeader.removeClass(‘x-grid3-hd-checker-on’);
            }
         }
      }
   },

   onClear : function() {
      var sel = [];
      this.items = {};
   },

   getId : function(rec) {
      return rec.get(this.idProperty);
   }
});

I have not had a chance to really dive in ,but you will need to make sure that your RecordDef has a FieldDef with a name of “id”, example… new StringFieldDef(”id”, 0)

I then created the corresponding Java class as follows.

package com.mersoft.gwtsample.client.widgets.grid;

import com.google.gwt.core.client.JavaScriptObject;
import com.gwtext.client.widgets.Component;
import com.gwtext.client.widgets.ComponentPlugin;

public class CheckBoxMemoryPlugin extends ComponentPlugin {

    public CheckBoxMemoryPlugin() {
        jsObj = create();
    }

    protected native JavaScriptObject create() /*-{
        return new $wnd.Ext.ux.plugins.CheckBoxMemory();
    }-*/
;

    @Override
        public void init(Component component) {
    }
}

Then you just need to add the CheckBoxMemoryPlugin to the GridPanel. (This assumes that you already have set up a Local Paging Checkbox Grid Panel)

grid.addPlugin(new CheckBoxMemoryPlugin());

Anyway, I hope this helps someone else who may be looking to solve this.

I still need to update the code in order to be able to get the selected row IDs, but that is for another day.

  • Share/Bookmark