Feb 10 09

In a recent stackoverflow.com question I asked for input on how to implemented animated GIFs in SWT table/tree viewer cells. Below is some code of the final solution.

Animation thread

The animation thread can be asked for the “current frame index” (CFI) by interested parties. The CFI thusly denotes the frame of an animated GIF which should be rendered. LabelProviders are the interested parties in this context because they actually render the images.

The thread increments the CFI in its run method. Also, Display#asyncExec is triggered in the run method. During the async execution the table/tree is ask to redraw and update – see lines 69ff.

/**
 * This animation thread doesn't actually animate anything itself. However, it acts as a pace maker
 * for other components that are able to render animated images. In a loop this thread marks
 * designated columns of a tree viewer (see constructor) for redrawing and calls
 * update() on the tree itself. Label providers on the other hand ask this thread for
 * the current frame index whenever they're triggered provide an image for the designated column.
 *
 * The viewer's content provider is ideally suited to control the life cycle of this thread. It can
 * create/destroy a thread instance whenever the viewer's content is updated.
 *
 */
public class BuildColorAnimationThread extends Thread {

  private final TreeViewer viewer;
  private final int animationColumnIndex;
  private final ImageLoader sampleImage;
  private int currentFrameIndex;
  private boolean cancel;
  private static BuildColorAnimationThread runningInstance;

  /**
   * C'tor which sets this instance to be the one running instance.
   *
   * @param viewer the viewer whose content should be animated
   * @param animationColumnIndex the index of the column to be animated
   * @param sampleImage a sample image which resembles the animation images in the viewer, the
   *        thread uses it to extract repeat-counts and frame delays
   * @see BuildColorAnimationThread#getRunningInstance()
   */
  public BuildColorAnimationThread(final TreeViewer viewer, final int animationColumnIndex,
      final ImageLoader sampleImage) {
    this.viewer = viewer;
    this.animationColumnIndex = animationColumnIndex;
    this.sampleImage = sampleImage;
    this.currentFrameIndex = 0;
    BuildColorAnimationThread.setRunningInstance(this);
  }

  /**
   * Returns the running instance of the animation thread or null if there's none.
   *
   * @return the running instance of the animation thread or null if there's none
   */
  public static BuildColorAnimationThread getRunningInstance() {
    return runningInstance;
  }

  private static void setRunningInstance(BuildColorAnimationThread runningInstance) {
    BuildColorAnimationThread.runningInstance = runningInstance;
  }

  @Override
  public void run() {
    int repeatCount = this.sampleImage.repeatCount;
    /*
     * The repeat count takes into consideration that an animated image might not be animated
     * infinitely but only for a given number of loops.
     */
    while (!this.cancel && !this.viewer.getTree().isDisposed()
        && (this.sampleImage.repeatCount == 0 || repeatCount > 0)) {
      this.viewer.getTree().getDisplay().asyncExec(new Runnable() {

        public void run() {
          final BuildColorAnimationThread thread = BuildColorAnimationThread.this;
          final Tree tree = thread.viewer.getTree();
          if (!tree.isDisposed()) {
            final Rectangle clientArea = tree.getClientArea();
            // Marks ONLY the animation column to be redrawn and not the entire table!
            tree.redraw(clientArea.x, clientArea.y, tree.getColumn(thread.animationColumnIndex)
                .getWidth(), clientArea.height, false);
            tree.update();
            // During the first loop currentFrameIndex=0...then currentFrameIndex=1,etc.
            thread.currentFrameIndex =
                (thread.currentFrameIndex + 1) % thread.sampleImage.data.length;
          }
        }
      });

      try {
        int ms = this.sampleImage.data[this.currentFrameIndex].delayTime * 10;
        if (ms < 20) {
          ms += 30;
        }
        if (ms < 30) {
          ms += 10;
        }
        Thread.sleep(ms);
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      }
      /*
       * If the last frame was signaled as being the current frame, decrement the repeat count and
       * start again.
       */
      if (this.currentFrameIndex == this.sampleImage.data.length - 1) {
        repeatCount--;
      }
    }
  }

  /**
   * Returns the index of the current frame. Animation providers (i.e. renderers) call this method
   * in order to find out which frame of an animated image they should render at any given point in
   * time.
   *
   * @return frame index starting at 0
   */
  public int getCurrentFrameIndex() {
    return this.currentFrameIndex;
  }

  /**
   * Signals the thread to stop. It will finish the current execution in its run method and will
   * then stop.
   */
  public void cancel() {
    this.cancel = true;
  }
}

LabelProvider

I use my own implementation of an OwnerDrawLableProvider which paints a static image if the icon doesn’t have to be animated. If it needs to be animated (based on some attribute of the row’s input) it paints the frame of an animated GIF. The index of the frame to draw is determined by the animation thread shown above. Hint: the getImage() method is called by paint().

  /*
   * For each (animated) job color the provider keeps a map of frame_index-to-image entries. This
   * speeds up the process of delivering images for rendering because they don't have to be created
   * all the time from ImageData objects.
   */
  private final Map> imagesMap =
      new HashMap>();

  @Override
  protected Image getImage(Event event, Object element) {
    final IJob job = (IJob) element;
    Image image = BuildColorInfo.getIconImage(job.getColor());
    if (job.isRunning()) {
      final BuildColorAnimationThread animationThread =
          BuildColorAnimationThread.getRunningInstance();
      /*
       * Should no animation thread be running, the static image fetched a few lines above will be
       * returned.
       */
      if (animationThread != null) {
        final int currentFrameIndex = animationThread.getCurrentFrameIndex();
        Map images = this.imagesMap.get(job.getColor());
        if (images == null) {
          images = new HashMap();
          this.imagesMap.put(job.getColor(), images);
        }
        image = images.get(Integer.valueOf(currentFrameIndex));
        if (image == null) {
          image = createNewImage(event, job.getColor(), currentFrameIndex);
          images.put(Integer.valueOf(currentFrameIndex), image);
        }
      }
    }
    return image;
  }

  private Image createNewImage(Event event, JobColor color, int currentFrameIndex) {
    final ImageLoader imageLoader = BuildColorInfo.getImageLoader(color);
    return new Image(event.gc.getDevice(), imageLoader.data[currentFrameIndex]);
  }

“Controller”

The controller is responsible to start/stop the animation thread. Basically, whenever the viewer’s input changes the running animation thread must be canceled and a new thread must be started. The best hook for that is the viewer’s content provider – an implementation of I<Tree|Table>ContentProvider as it declares an inputChanged() method.

    @Override
    public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
      if (this.animationThread != null) {
        this.animationThread.cancel();
      }
      // Animates the first column.
      this.animationThread =
          new BuildColorAnimationThread((TreeViewer) viewer, 0, BuildColorInfo
              .getImageLoader(JobColor.BLUE_ANIME));
      this.animationThread.start();
    }

The code presented here was implemented for H2Eclipse, the Hudson plugin for Eclipse.

Jan 10 18

This is a follow-up for the Beware of WebSphere admins post just below.

My first immediate conclusion after the described deployment problems was to ban the use of the jsession cookie  in future applications. If the application always includes the jsessionid parameter in URLs there’s nothing that can go wrong during deployment in terms of cookie paths.

Contemplating a second longer made it obvious that maybe this wouldn’t be such a wise decision after all. There are number of developers who try to enforce the exact opposite because the jsessionid URL parameter can be considered harmful. I highly recommend reading the following two blog posts that support this thesis:

http://randomcoder.com/articles/jsessionid-considered-harmful
http://boncey.org/2007_1_8_purging_jsessionid

Jan 10 18

A lot can go wrong when you deploy a simple Java EE application in IBM WebSphere – even if the application needs nothing but a Servlet container.

We recently shipped a JEE application to the customer. Although it was packaged in an EAR file, because the customer required it, it needs nothing but what the Servlet specification mandates. The application was tested both at other customer sites and on our internal infrastructure. Some customers use JBoss, others Oracle Weblogic and we use Tomcat internally. This new customer uses IBM WebSphere *sigh* – not our dearest friend, but so what. So we set up a WebSphere test server in-house and successfully deployed the application.

The customer asked us to fill in a several page form to specify all the necessary configuration parameters our application required. For almost all fields we settled for the provided default values because all we basically needed was a JNDI name pointing to a datasource.

Needless to say the application didn’t run out-of-the-box at the customer site.

Given the fact that the application runs fine on all but this customer’s infrastructure neither the customer nor I thought it necessary for us to provide on-site support. The log files we got for analysis indicated that somehow they seemed to have a multi-threading or multi-session problem. For two weeks the customer’s network and WebSphere specialists tried to figure our what was going on. When they were at their wits end, my repeated offer to send one of our engineers over was accepted. When he, too, was stuck a day later I asked him to share his screen remotely and show me request/response headers in HttpFox.

It became apparent immediately why our application constantly created new sessions. The cookie path for the jsessionid cookie was neither ‘/’ nor was it equal to the context root of our application. Hence, with every response our application returned a new jsession cookie, but when the browser sent a new request to the application it didn’t include the session id – because it didn’t find a cookie with a path that matched the application’s context root. Usually you’d notice this immediately because you’d have to authenticate yourself (i.e. log in) constantly with every request. Since the customer uses SSO this was done transparently in the background.

So, what’s this got to do with the WebSphere admin? Well, in the form mentioned above we were asked for the application cookie path. The default value was ‘/’ and therefore we hadn’t changed it. The admin, however, acted on his own authority and changed this to something else.

Dec 09 12

I just invested 30min to find tools/libraries which allow me to use PHP scripts instead of Java on the server when the front end is GWT. There are various potential channels through which GWT and PHP can talk to each other. GWT RPC, ideally native or over JSON/XML would certainly be the most obvious choice.

My Internet search didn’t turn up a whole lot of useful stuff…

http://code.google.com/p/gwtphp/

http://code.google.com/p/gwtamp/

http://code.google.com/p/lacertae/

http://download.boulder.ibm.com/ibmdl/pub/software/dw/xml/x-gwtphp/x-gwtphp-pdf.pdf

http://angel.hurtado.googlepages.com/tutorialgwt2

Sep 09 11

Yet another software configuration issue that I wasted a few hours at today.

Environment

Apache 2.2.13 connect to Tomcat 5.5 with mod_jk (ajp13). Apache requires basic-auth for “/” i.e. for all URLs it serves. Just to be 100% precise, Tomcat runs as a WTP server “inside” Eclipse. However, the fact that it’s not a standalone instance has no effect to either the problem or the solution.

Problem

I noticed that request.getUserPrincipal() returned null in my Servlet filter although basic-auth in Apache was successful. By raising the mod_jk log level to debug (JkLogLevel debug) and looking at the mod_jk.log I could confirm, however, that mod_jk at least passed the remote user along in the request.

Solution

Set tomcatAuthentication=”false” for the AJP/1.3 connector in server.xml. The parameter is explained in the Tomcat connector documentation: “If set to true, the authentication will be done in Tomcat. Otherwise, the authenticated principal will be propagated from the native webserver and used for authorization in Tomcat. The default value is true.”

A thread from the tomcat-users mailing list archive helped a lot: http://www.mail-archive.com/users@tomcat.apache.org/msg55080.html. I didn’t initially find that through a web search because I kept looking for something like “principal null Tomcat Apache mod_jk” instead of “REMOTE_USER null”.

Aug 09 16

On some of my Eclipse workspaces to Galileo update failed in the SVN department (Subversive). Activating anything remotely related to Team/SVN features triggered an error. The workspace log contained:

!MESSAGE An error occurred while automatically activating bundle org.eclipse.team.svn.core (512).
!STACK 0
org.osgi.framework.BundleException: Exception in org.eclipse.team.svn.core.SVNTeamPlugin.start() of bundle org.eclipse.team.svn.core.
 at org.eclipse.osgi.framework.internal.core.BundleContextImpl.startActivator(BundleContextImpl.java:805)
...
Caused by: java.io.StreamCorruptedException: invalid type code: 00
 at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1356)
 at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
 at org.eclipse.team.svn.core.svnstorage.AbstractSVNStorage.loadLocationsFromFile(AbstractSVNStorage.java:551)

I tried to start Eclipse with the -clean option – didn’t help. Then I updated the Subversive SVN plugin and all connectors – didn’t help.

What eventually did help was to remove all org.eclipse.team.* folders in the workspace’s .metadata\.plugins folder.

May 09 20

Reading a book on the Sun Certified Java Programmer (SCJP) test is interesting and worthwhile, but the test itself is bogus.
Says who? I do…

I took the SCJP for Java 2 many many years ago and subsequently also passed the SCJD and SCWCD tests. Recently, I took the SCJP 6 upgrade exam. So, I feel I experienced a fair share of the Sun certification universe.

SCJP is known as the “API test” and its successful completion is the basis for all other Sun Java certificates. You’re expected to be familiar with many of the basic Java/OO concepts and APIs – sometimes down to the method signature. As such, passing the test shouldn’t require at lot of studying for any serious Java engineer since this is our daily bread and butter.
So, going through a certification prep book should hopefully not teach you a lot of new stuff (i.e. things you weren’t previously familiar with) but rather fill your know-how gaps. And that’s the interesting part. You might uncover surprising aspects of the JLS (Java Language Specification) which can be valuable.

However, it’s the way questions and code samples are formulated in the test that hamper the significance of its results. The time limit for the full Java 6 test is 210min (yes, that’s 3.5h!) and it consists of 72 questions. While you may finish a lot quicker it certainly is a primary test for your ability to concentrate in a test center. Furthermore, the majority of the questions contain code samples that are outright silly. You’ll see code that no sensible programmer would ever craft like that – even if the JLS allowed it.
You job would then be to find the missing ‘;’, the missing ’static’ modifier, the illegal [] in an array declaration, the overridden instead of overloaded method, the illegal auto-boxing, the… you name it i.e. mainly stuff the compiler would report anyway. Finding the correct answer has become harder over the years because for most questions two of the possible answers nowadays are “Doesn’t compile” or “Fails at runtime”.

So, practicing for the test is not so much about learning Java as it is about getting accustomed to this type of questions and finding a (personal) strategy to tackle them.

All in all, I don’t think a Java test which a good Java developer might still flunk has a lot of significance. And passing this test doesn’t necessarily mean that you’re a good developer, either. Pity.

May 09 02

It was only recently after many years of Java programming that I found out that there’s no such thing as a “static inner class”. Such classes are called static nested classes.

-> http://www.coderanch.com/t/442837/Programmer-Certification-SCJP/certification/Members-inner-classes

Jan 09 25

After many hours searching the Internet for GWT widget libraries, analyzing them, and taking notes I thought I might as well publish my findings here. Feel free to comment and point out inconsistencies.

At the moment the market seems to be split into two segements:
- a few small or medium-size, low-key libraries with little activity
- the whole “Ext” gang

However things change very quickly, in a matter of months, and the end of 2008 saw the advent of a new-kid-on-the-block: SmartGWT.

Small, or medium-sized libraries

  • GWT-SL/WL, a collection of server- and client-side tools. No real show case that demonstrates the power of the library is available. Last minor release in December 2008. Little SVN activity over the entire course of 2008.
  • GWT Tatami, based on the DOJO JS framework. Show case isn’t impressive, but project is active. Continuous SVN activity. Road map for first half of 2009 available.
  • GWTLib, continuous but little SVN activity. Offers hardly anything else but but table-centric widgets.
  • GWT Tk, tbd
  • Rocket GWT, tbd
  • “vanilla” GWT, Google’s on-board GWT widgets. Nothing fancy, but clean and slick.

The “Ext” corner
A long time ago (think Internet time ;-) ) a guy called Jack Slocum built an extension to Yahoo’s YUI widget library named YUI-Ext. It evolved and became independent, Ext JS was born and with it a new company: http://extjs.com. The library quickly became very popular.
Once GWT was released people started writing widget libraries for it. Boston-based Sanjiv Jivan started GWT-Ext and based in on Ext JS. Darrell Meyer wrote MyGWT, a pure GWT widget library which shared the Ext JS l&f. It was voted #1 widget library one year ago.
In April 2008 things became very nasty an confusing when Jack Slocum changed the Ext JS license from LGPL to GPL thereby forcing GWT-Ext to stick with Ext JS 2.0.2 which was the last version available under LGPL. Furthermore, he hired Darrell Meyer who brought his MyGWT as a dower into the relationship. As a result, Ext JS published its own GWT widget library called Ext GWT. Now developers had the choice between GWT-Ext (full OSS with LGPL) and Ext GWT with a dual-licensing model. The GWT widget “war” (see references 1-5 below) turned many away from Jack Slocum’s Ext JS because they disliked the way the company treated the GWT community.
On November 26th, 2008, the game changed again when the GWT-Ext crew announced that they would no longer build major new features but instead be migrating to SmartGWT, a library created by GWT-Ext developer Sanjiv Jivan . SmartGWT 1.0 had been announced only two weeks earlier.

[1]http://www.jroller.com/sjivan/entry/my_response_to_jack_slocum
[2]http://pablotron.org/?cid=1556
[3]http://www.gwtsite.com/ext-gwt-gwt-ext-what-now/
[4]http://ajaxian.com/archives/to-gwt-ext-or-to-ext-gwt
[5]http://gwt-ext.com/license/

Sep 08 06

I struggled to update the content of a LONG column in an Oracle database with Spring JDBC. However, (in retrospective) this is more of a general JDBC problem than a Spring problem.

The problem first occured when I tried to use a regular SQL update statement to save a few thousand characters in that LONG field.

simpleJdbcTemplate.update("update mytable set content = ? where id = ?",
new Object[] { o.toString(), o.getId() });

Hence, I tried to execute the same statement in a SQL editor, Oracle’s SQL Developer in my case, and got “ORA-1704: string literal too long” as a feedback from the database. Further analysis revealed that the LONG datatype has been deprecated in Oracle ever since 9i. It can hold 2GB max. but you’re encouraged to use CLOB instead. Some poking around the JDBC pages at oracle.com revealed sample code that describes how to manipulate LONG fields: http://www.oracle.com/technology/sample_code/tech/java/sqlj_jdbc/files/basic/LongSample/Readme.html.

Now, whether the below code is optimized or not is yet to be seen. It solves my problem, though.

final LobHandler lobHandler = new DefaultLobHandler();
final byte[] contentBytes = o.toString().getBytes();
final InputStreamReader clobReader = new InputStreamReader(new ByteArrayInputStream(contentBytes));
simpleJdbcTemplate.getJdbcOperations().execute("update mytable set content = ? where id = ?",
    new AbstractLobCreatingPreparedStatementCallback(lobHandler) {
    protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException {
        lobCreator.setClobAsCharacterStream(ps, 1, clobReader, contentBytes.length);
        ps.setString(2, o.getId());
    }
});