Wednesday, October 31, 2007

Disabling autocomplete on every single field in Curam

Like disabling the cache, disabling autocomplete is one of the other things that may be requested for Curam applications.  Theoretically this is simple if you control everything in a web application as you only need to put in autocomplete="off" in every form.  However, you can't really do that easily in Curam as you don't control the output JSPs nor do they provide you with a facility to do it.

Modifying gen-jsp.xsl

The first inclination would be to modify the generators just like I did when I disabled the cache. Unfortunately, Curam has its own form tag which does not accept the autocomplete attribute. So changing gen-jsp.xsl will not work.

Curam Implementation

Instead of fixing the output from the generators, we can use JavaScript that gets executed to update all the forms to disable autocomplete.  Of course to do that you need to know how to handle the differences between browsers for the onload and the setAttribute functionality.

However, we can get around it by using the standard Curam extension mechanism.  If you look at the generated code, then you will notice that there are a few JavaScript files that are always loaded, one of the relatively stable ones that have not really changed is omega3-util.js so we can theoretically use that.

First we make a copy of CuramCDEJ\lib\curam\web\jscript\omega3-util.js to components\custom\WebContent\CDEJ\jscript.  That will allow us to separate our customizations from the core files.

Now we write the following script at the end of the file.

function disableautocomplete() {
  var forms = document.getElementsByTagName('form')
  for (var i = 0; i < forms.length; ++i) {
    if (forms[i].setAttribute) {
      forms[i].setAttribute('autocomplete', 'off')
    } else {
      forms[i].autocomplete = 'off'
    }
  }
}
if (window.addEventListener) {
  window.addEventListener('load', disableautocomplete, true)
} else if (window.attachEvent) {
  window.attachEvent('onload', disableautocomplete)
} else {
  window.onload = disableautocomplete
}

Customizing gen-page-footer.xsl does not work

Although this change is be done using JavaScript and the scripts can be placed anywhere including gen-page-footer.xsl, this will not be sufficient.  The gen-page-footer.xsl file is only used by the standard Curam pages, but pop-ups do not use this file so if the pop-up has a form, the data would be saved.

Tuesday, October 30, 2007

Disabling cache in Curam

This is better than disabling the back button, even if it is one of the 10 ten web design mistakes.  However, for Curam applications and most web applications, this is something that would really need to be done in order to ensure that the user gets the most current data when they look at a page and to satisfy the requirement of nothing persisted on the client machine.

Technical overview

There are two things that need to be done.  The first is to add the following lines to the HTTP header:

Cache-Control: no-store
Pragma: no-cache
Expires: 0

And the following in the <head> block of your web page.

<meta http-equiv="Expires" content="0" />
<meta http-equiv="Cache-Control" content="no-cache" />
<meta http-equiv="Pragma" content="no-cache" />

This will make most web browsers not cache the page.

The second thing you need to do is to work around the Internet Explorer bug which disregards the no-cache header information if your page is larger than 64k.  To do that add

<head>
<meta http-equiv="Pragma" content="NO-CACHE" />
</head>

Near the end of your file.

Curam implementation

Unlike the process of disabling the back button, you cannot simply make the changes on gen-page-header.xsl or gen-page-footer.xsl.  The changes need to be directly done on CuramCDEJ\lib\curam\xml\xsl\gen-jsp.xsl

Note: If there are any upgrades to Curam, the changes need to be redone on the file.

In gen-page-header.xsl, add the following XML after the first instance of <head> in the jsp.xsl

<jsp:scriptlet>
response.setHeader("Cache-Control", "no-store");
response.setHeader("Pragma", "no-cache");
response.setHeader("Expires", "0");
</jsp:scriptlet>
<meta http-equiv="Expires" content="0" />
<meta http-equiv="Cache-Control" content="no-cache" />
<meta http-equiv="Pragma" content="no-cache" />

You also need to add the following XML after the scriptlet that outputs </body>:

<head>
<meta http-equiv="Expires" content="0" />
<meta http-equiv="Cache-Control" content="no-cache" />
<meta http-equiv="Pragma" content="no-cache" />
</head>

Browser compatibility

This has only been tested on Firefox and Internet Explorer 6, but it should work with most browsers.

Monday, October 29, 2007

Disabling the Back button in Curam

First let me say this. DISABLING THE BACK BUTTON IS A NOT A GOOD IDEA. Especially for web applications as this is a button that helps people navigate through your web site quickly. However, if you really have to remove the functionality. You can use the simple approach of adding the following JavaScript in every single page:

history.forward()
In current versions of Curam, they provide a facility for us to do this without actually modifying the CDEJ directly. First, make a copy of CuramCDEJ\lib\curam\xml\xsl\gen-page-header.xsl and put it somewhere in your components\custom folder. Then add:
<script type="text/javascript"><xsl:comment>
history.forward();
//</xsl:comment></script>

just before

<table class="page-header" summary="">

Then do a clean build.

But just let me iterate one more time... DISABLING THE BACK BUTTON IS A NOT A GOOD IDEA.

Thursday, October 25, 2007

util-2.0.ear released

One of my other open source projects that I am not diligently working on called twiff contains small project called util.  Unfortunately, I haven't been able to update the documentation, but I have made some a number of changes to it structurally.  Some of the major changes are

  • Switched to Maven 2
  • Added a getfile.jsp to retrieve any file and browse through the file system
  • Added multi-line text field for db.jsp

This app is primarily for application developers who may require a backdoor to their application during development and testing.  Aside from databases and files, the users of this can check JMS, JNDI and the environment/session data.  Most of the code is written in single file JSPs (normally a bad coding practice) so it can be extracted and placed in any web application to get more data (especially for env.jsp which shows session information).

You can go to the File Release site to get the files.

Wednesday, October 24, 2007

JUnit Testing in Curam

Curam out of the box comes with some rudimentary JUnit testing capability within their EJBServer project since around version 3.  I've been using it instead of something I wrote up back in 2004 for my unit testing because at least it comes with Curam.  However, my colleagues have told me that there have been no instructions on setting it up till now.  So, this is my attempt at documenting what you need to do in order to get your Curam unit tests working.

Note: for those I have talked with on this subject before, this is a simpler way of doing things rather than what I have been doing in my previous projects.

Setting up a place for your unit tests

The first thing you would need to do is find a place to put your tests.

  1. Make a new source folder EJBServer\components\custom\tests.  The last path element has to be tests.
  2. When I initially set things up, I always create a test package called curam.yourproject.test.sanity.  In this folder I create a simple unit tests to prove that things work correctly.  In which case I put in the following test and verify that it runs within Eclipse.
public SanityTest extends TestCase {
  public void testSanity() throws Exception {
    assertTrue(true);
  }
}

Verifying that your unit test works in Curam

The next thing you need to deal with is running the actual test within Curam's build scripts.  To do this you just have to type the following in the command prompt:

build supplement -Dsupplement=tests
build test

The supplement target creates a supplementary jar and will pull any sources that have the value of supplement in their source path.  In this case it would be tests.

The test target runs the test cases and generates an HTML report.  The report is located in EJBServer\build\testresults\html\index.html.

Creating a Curam Server Test case

Before you create a Curam server test case, you need to ensure that curam.test.framework.CuramServerTest is in your build path.  To do this, just set EJBServer\components\core\tests as a source folder and set it to include only curam.test.framework.CuramServerTest.  The other files are used by the build scripts and won't compile cleanly in Eclipse without making additional changes to the build path that you don't really need.

I would the create a simple test case that looks like:

public CuramSanityTest extends CuramServerTest {
  /**
   * This is needed because Curam is using an
   * older version of JUnit which requires the
   * one argument constructor.
   */
  public CuramSanityTest(final String name) {
    super(name);
  }
 
  public void testSanity() throws Exception {
    assertTrue(true);
  }
 
  public void testOrganizationListUserForOrganization() throws Exception {
    OrganizationFactory.newInstance().listUserForOrganisation(); 
  }
}

Then I rerun the test to see if it works out on Eclipse and on the command line.

Special directories

Curam's command line tester will test every single file in the tests.jar with the exception of a few folders unless you modify app_test.xml.  However, if you want to avoid having to make changes to the build scripts, use the server.curam.test.utility package to contain your utility classes.

Setup and Teardown

Curam's test framework extends the setUp and tearDown methods with setUpCuramServerTest and tearDownCuramServerTest respectively. Unfortunately, they did not make them throw Exception.  So unless you make your own copy of the CuramServerTest, which I normally do in my projects to eliminate the need for the constructor, you would have to wrap your setUp and tearDown routines with a try-catch block that throws a RunTimeException for example:

protected void setUpCuramServerTest() {
  try {
    ...
  } catch (Exception e) {
    throw new RuntimeException(e);
  }
}

Thanks

Kudos to Robert S. for encouraging me to write this.  Good luck on your project Rob and sorry if this is simpler than the way you tried to copy from us!

Thursday, October 18, 2007

SpinRite on VMWare

I know that the SpinRite FAQ specifies that as long as you can see the drive in DOS you should be able to use SpinRite on it.  However, some of my external USB drives do not seem to work with the DOS drivers I find on the Internet.  Anyway, I had a thought that maybe I could run SpinRite in VMWare, and I found that someone had done it on a Mac.

I have a good news for mister Gibson: SpinRite would actually work on the Mac with VMWare

So I took the idea for a test drive.  I guess I wanted to make sure that those external drives would last a bit longer especially hearing from my friend that one of his drives died recently (good thing it was RAIDed).  I created a small virtual machine and made it use the physical drive which points to my USB drive.  Then I created the ISO image from the SpinRite executable and made the VM boot to the ISO image.

Before I start anything I run chkdsk /f on the drive to make sure that there are no open handles on the drive itself.  Once that completes I started the VM.

It seems to work quite well.  There is no SMART support though, but I guess that is fine according to the screen it says that maintenance and recovery operations remain functional.

It is quite slow.  An estimate of over 6 hours for a 80Gig hard drive on level 4.