Christian Oestreich

   two guys in design - software.development.professional

jQuery Memory Leak Frustration

| Comments

Recently at work we ran into a hard to track down issue with Internet Explorer 6. Every page load we were seeing about 10mb of memory being retained in the browser. We had no idea where to start and since there were several hundreds of commits to the code and data structures we had a tough time finding the culprit.

We initially thought the problem was the Sencha ExtJS Grid we were using. Between the four of us working on it we finally reverted the jQuery 1.5.1 upgrade we had made and found the problem disappeared. Initially we reverted to 1.4.3, but found that 1.5 also didn’t cause the problem. All future version from 1.5.1 to 1.6.1 caused the same issue.

We found some information about how IE6 didn’t like the modal: true of the ui.dialog widget. We set that boolean to false and it seemed to clear up the issue with 1.5.1, but we were duped by IE6 for a short time as the problem only seemed to disappear until after we deployed to production. We reverted the modal setting and went back to jQuery 1.5 and all was cleared up.

I am a huge fan of jQuery and love how amazingly simple doing really cool stuff is, but I was a bit erked that something so non-trivial was introduced and remains through another major release of the framework.

I understand that testing all browsers, and especially IE6, might be difficult… but the “write less do more” only goes so far when your end users can’t use your application after 15 minutes.  When sites like Google stop supporting IE6, you know that your platform is old. Perhaps my frustration over jQuery is better vetted against the ridiculous corporate oppression of adopting technology newer than 10 years old.

I did write another widget today that extends the autocomplete box which supports invoking both urls with normal data params or in a restful style with very little effort… so I guess I am over it.

Testing Grails REST Services

| Comments

Recently I needed to run a simulated load test on some rest services to see what the memory footprint would be as I varied my cache size and strategy.  I looked for a good tool to do this and couldn’t really find anything that fit my need easily.  I liked the SOAP UI test runner, but I couldn’t quite get it working like I wanted.

I ended up writing a script that I can run from the grails shell to invoke the REST service using an xml data file to hold the IDs of the objects I wanted to load.   I played around with the idea of just grabbing the items from the DB, but I really wanted to minimize the impact on the application during the loading so I grabbed all the data from dbvis and exported it into an XML file that I slurp in through groovy.

I took the script one step farther and implemented gpars as well to try and speed up the load.  There were some issues with the server becoming inundated with too many requests so I added a 100ms sleep to every request.  I still need to tune that to get the best throughput without causing the server to throw exceptions from having unavailable listener threads.

Note: I changed some of the data in these scripts to use dummy names and paths as to not violate any corp policies.

I created a script and put it under scripts\performance\MyScript.groovy (obviously not called MyScript, but you get the point).  By wrapping the script in a closure with the .call() at the end, this will run the default method when we execute load.  There might be better ways to do this, but I found it to work quite nicely.  I would suggest moving the thread size to the config file as it would be easier to change on the fly that way without recompiling the script.  The grails shell isn’t as hot-swap friendly as the running grails tomcat/jetty instance.  I had some profiling around the code execution, but when I had to add the sleep, I took it out.  I can see cases for these scripts where adding it back would be useful.

The script contains the following code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import static groovyx.gpars.GParsPool.withPool
import org.codehaus.groovy.grails.commons.ConfigurationHolder as CH

def blahStressTest = {
    String path = CH.config.dataDir + File.separator + "mydata.xml"

    println "Loading ${path}.."

    def theData = new XmlSlurper().parseText(new File(path).text)
    def size = theData.ROWSET.ROW.size()

    withPool(3) {
            medData.ROWSET.ROW.eachWithIndexParallel { object, index ->
                    try{
                            def url = new URL("${CH.config.grails.serverURL}/resturl/${object}").text
                if (index % 100 == 0) println "${Thread.currentThread().name} - ${index} of ${size}"
                            Thread.sleep(100)
                    } catch(Exception e){
                            println "exception ${e.message}"
                    }
            }
    }

    println "completed"
}.call()

I added the following to the config and put a different path under each environment section as the directory structure is a bit different in each. I really have no need to run this in prod for now, but I might in the future.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
environments {
  production {
    grails.serverURL = "https://url"
    logDirectory = "/unix/path/logs"
    dataDir = "/unix/projects/myproject/scripts/performance"
 }

  development {
    grails.serverURL = "http://localhost:8080/${appName}"
    logDirectory = "/windows/path/log"
    dataDir = "c:/projects/myproject/scripts/performance"
  }

  test {
    grails.serverURL = "http://testserver:8080/${appName}"
    logDirectory = "/unix/path/logs"
    dataDir = "/unix/projects/myproject/scripts/performance"
  }
}

My xml file is in the same directory as the scripts for now.  It just has some data that looks like this (as exported directly from dbvis).

1
2
3
4
5
6
7
8
9
10
11
<MyData>
<ROWDATA>
<ROW>
<ID>123</ID>
</ROW>
<ROW>
<ID>1234</ID>
</ROW>
...
</ROWDATA>
</MyData>

I really just wanted to load up all the objects in cache without any object misses.  I tried doing a 1..100000 type operation, but the IDs of the data I am using are not very sequential and skip around a bit.

I launched the grails shell using (use which ever env you want to run it for):

grails dev shell

Once launched I simply type

load scripts\performance\MyScript.groovy

This technique could come in handy for testing other types of rest/url actions as well.  I originally had created another controller that would use the rest plugin’s withHttp method to invoke the site, but found that to be a bit clunky and I didn’t like being forced to use up one of my browser tabs to run the script. :)

I used the grails melody plugin to see the cache sizes and memory foot print.  Definitely an awesome plugin!

FYI: To configure the spring cache plugin manually you need to add a file under the cong\spring folder called resources and add some code like the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import grails.plugin.springcache.web.key.WebContentKeyGenerator
import org.springframework.cache.ehcache.EhCacheFactoryBean

// Place your Spring DSL code here
beans = {
   MyCache(EhCacheFactoryBean) { bean ->
    cacheManager = ref("springcacheCacheManager")
    cacheName = "MyCache"
    eternal = true
    diskPersistent = false
    memoryStoreEvictionPolicy = "LRU"
    maxElementsInMemory = 100000
  }
}

Anywhere you use the @Cachable(“MyCache”) it will use this configuration instead of the default.

Good Luck.

Gmetrics & Codenarc Script for Grails

| Comments

I really wanted to get our project working with sonar and grails.  After some searching I found very few people that had this working.  Actually I only found one here.  I tried doing this by adding the pom.xml and invoking the mvn sonar:sonar target.  Maven got to the end and threw an exception.  I spent the better part of a day working on this and poking around at the pom file and finally gave up.

Setting Up The Environment

All the source code for this can be found at GitHub.  You can pull it down via the command:

git clone [email protected]:ctoestreich/jenkins-sandbox.git

I decided to go with using cobertura, gmetrics and codenarc for reporting on the code.  You need to first add the plugins to your project using the following commands

grails install-plugin coverage
grails install-plugin gmetrics
grails install-plugin codenarc

I ran the default targets for gmetrics and codenarc and they both produced html… ugly html.  I was hoping the default output would be a little more like the sonar dashboard; perhaps a little more web 2.0-ish.  Luckily I ran across a couple blog posts by Herbert Ikkink (mrhaki).  The posts had some nice xslt to html transformation and style sheets attached to them. I took the work mrhaki had done and went one step further and created a grails build target to generate the output for both gmetrics and codenarc and publish the results through Jenkins.

Creating The Script

I had first tried to consume the codenarc xml using the violations plugin for Jenkins, but that was puking all over itself, so I opted for making both reports simple HTML reports.

First I needed to create a script in grails by running

grails create-script CodeReports

Then I added the following code to the grails-app\scripts\CodeReports.groovy file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
includeTargets << grailsScript('Init')
includeTargets << new File("${codenarcPluginDir}/scripts/Codenarc.groovy")
includeTargets << new File("${gmetricsPluginDir}/scripts/Gmetrics.groovy")
configClassname = 'Config'
target(main: "Add some style to the gmetrics report") {
  depends(compile, codenarc, gmetrics)
  stylizeGmetrics()
  stylizeCodenarc()
   }

   private void stylizeGmetrics() {
  println "add some style to the gmetrics report"
  ant.mkdir(dir: 'target/gmetrics')
  ant.xslt style: "reports/gmetrics.xslt", out: "target/gmetrics/gmetrics.html", in: 'target/gmetrics.xml'
  ant.copy(todir: 'target/gmetrics') {
    fileset(dir: 'reports') {
      include name: 'default.css'
      include name: '*.png'
      include name: '*.gif'
    }
  }
}

private void stylizeCodenarc() {
  println "Add some style to the codenarc report"
  ant.mkdir(dir: 'target/codenarc')
  ant.xslt style: "reports/codenarc.xslt", out: "target/codenarc/codenarc.html", in: 'target/codenarc.xml'
  ant.copy(todir: 'target/codenarc') {
    fileset(dir: 'reports') {
      include name: 'default.css'
      include name: '*.png'
      include name: '*.gif'
    }
  }
}
setDefaultTarget(main)

This new script can be called at the command line by running

grails code-reports

Some interesting caveats to note.  The paths to the plugins won’t resolve until you run compile at least one time.  So make sure you invoke the compile target directly or through another script like test-app to get the paths all set up in grails before calling code-reports.  I also had some issues with out-of-date plugins and had to blow away the grails 1.3.6 working directory to get this to work on local machine and ubuntu (build server) for my demo project.  It worked no problem on another project… go figure.  Just go to C:\Documents and Settings[your user].grails[grails version]\projects[project name] and remove the [project name] dir (where [project name] if the name of your project, etc).  On my build box the dir is /usr/share/tomcat6/.grails/1.3.6/projects/[project name].

Additional Required Settings

Add the code below at the end of _grails-app\conf\Config.groovy. _ This will set up gmetrics and codenarc to produce xml instead of HTML and set the output for the xml files.  Also I didn’t want to scan my test code.  You might want scan your integration and unit test code to so you can remove those lines or set them to true.  I am also using a custom codenarc.properties file to exclude certain patterns.

1
2
3
4
5
6
7
8
9
gmetrics.reportType = 'org.gmetrics.report.XmlReportWriter'
gmetrics.outputFile = 'target/gmetrics.xml'
gmetrics.processTestUnit = false
gmetrics.processTestIntegration = false
codenarc.reportType='xml'
codenarc.reportName='target/codenarc.xml'
codenarc.processTestUnit = false
codenarc.processTestIntegration = false
codenarc.propertiesFile = 'codenarc.properties'

I added the codenarc.properties at the root of the project folder (same dir as application.properties).  It has one line.

1
GrailsStatelessService.ignoreFieldNames=dataSource,scope,sessionFactory,transactional,*Service,messageSource,s3Credential,applicationContext,expose,profiled

Add the code below to the end of grails-app\conf\BuildConfig.groovy. This sets the coverage report to xml for Jenkins to process and excludes the tests.

1
2
3
4
coverage {
    xml = true
    exclusions = ["**/*Tests*"]
}

I created a directory at the root of the application called reports to hold my report artifacts and xslt files.  These are the files that were provided by mrhaki.  You can change the logo to whatever you want or muddle with the css as it suits you.  There are few image files that you can grab from my github in addition to the following.

reports\codenarc.xslt reports\gmetrics.xslt reports\default.css

So with the script created I ran the following scripts locally to test the output

grails test-app -coverage
grails code-reports

You should get some output in your target dir like the following

Once all that is working we can get to setting up Jenkins.

Setting Up Jenkins

I am assuming you are at least a bit familiar with Jenkins and are already building your grails application with it.  You will need to add the following two plugins

Cobertura Plugin

HTML Publisher plugin

You will then need to go to the project admin screen and add code-reports to your target for your grails build.  Here is mine:

clean "test-app -coverage --non-interactive" code-reports

You will then need to add the following to the html report output section and coverage configuration to get the reports to show up on the home page of the build.

You should see the areas marked in the red boxes show up on your build

I really like the output format that mrhaki produced.  Until there is a better integrated solution with sonar, this is golden.

Here are the report samples

The actual source code that I wrote I neither claim to be useful or entertaining, use at your own enjoyment.

All the source code for this can be found at GitHub.  You can pull it down via the command:

git clone [email protected]:ctoestreich/jenkins-sandbox.git

Tuesday Night Worlds - 4/19/2011

| Comments

Another week… another race.  The crowd was a little bit thinner for this race as the weather was a warm 36F.  With the absence of Patrick and his rockets, the group stayed a little more tightly coupled.  Colin was also absent leaving just Mitch, Chris and myself to represent team OptumHealth Performance.

On the first sprint lap I decided to take the outside line and Chris grabbed onto my rear wheel.  I death pulled him for a half lap.  I quickly petered out up the hill but Chris had enough gas to go hard to the finish line and earn the teams first 5 points with a first place sprint lap victory.  After the pull I was struggling to regain the group and didn’t reconnect for two laps. I really tried to use the downhills to my advantage to cover distance.

None of us really had any juice to go for the second sprint lap.  It was just enough to hang onto the group until the last lap.  We tried the best we could to get into position for the last lap.

At the bell Chris and I were somewhere in the 6-9 range.  The group quickly became a single file pace line and we just grabbed a wheel and rode the line until the uphill portion.  Everyone got up and started sprinting.  I couldn’t make up any ground, but I didn’t lose any either.  Chris managed a 3rd place finish and earned another 2 points.  Overall it was definitely a good night for the team and especially Chris.

I finished 7th overall and that felt pretty good for the 2nd criterium of my racing career.  I will respect Mitch’s feelings and not talk about where he finished the event.  I think his wheel fell off this time or something?!

[caption id=“attachment_99” align=“aligncenter” width=“702” caption=“OptumHealth Performance Team (Mitch Talbot, Chris Frykman, Patrick Parish, Colin Jaworski, Christian Oestreich) ”][/caption]

content/uploads/2011/05/optumhealth_performance_team.jpg (OptumHealth Performance Team)

content/uploads/2011/05/optumhealth_performance_team.jpg

Mixing Grails, Groovy, Scala & Java

| Comments

As I have mentioned earlier, I am a huge fan of grails and would love to ditch our existing java/spring/custom code framework and move to grails holistically.  A few of our senior people on the team really want to move to scala to get rid of a lot of the clutter code we write in our java stacks.  To further the grails cause, I decided to see if I could get the scala plugin for grails working and see how deeply I could integrate the scala code into the application.  While I was at it I decided that I might as well throw in java as well for giggles.  I really like both groovy and scala.  Each language has its advantages, but there is a general consensus on the team at work that the statically typed scala would be better for our inline conversion of existing java code.

Setting Up The Environment

All the source code for this can be found at GitHub.  You can pull it down via the command:

git clone [email protected]:ctoestreich/grails-scala.git

I am assuming you will be using IntelliJ (as it is my favorite IDE).  If you are using Eclipse you will be on your own for a lot of the set up pieces.  I am also assuming you have grails already setup on your machine correctly.  The first thing to do is install the scala plugin for grails via the following grails command:

grails install-plugin scala

If you are using IntelliJ, you should also install the scala plugin for IDEA

Install Scala Plugin

Starting The Coding

I started the coding and realized that I needed to be conscious about what type of objects inherited from what type of interfaces/traits.  I initially tried having everything inherit from a Java Interface.  This solution compiled and worked okay.  I then moved to trying to get everything to extend a groovy interface.  The code exploded.  It was immediately obvious that the scalac command was run first and compiling the scala and java sources before groovy. Having the java and scala extend a groovy interface wasn’t going to work.  In the end I decided to go with a scala trait due to its enhanced functionality and support in scala and standard java interface usage.  I spent the majority of my time fuddling with this compilation order problem and getting the three languages to compile nicely together.

First I defined a scala trait.

1
2
3
4
5
6
package com.far.scape.scala
trait Cast {
  def race():String
  def actor():String
  def save()
}

Then I created the following objects.

Java Object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.far.scape;
import com.far.scape.scala.Cast;

//public class Kadargo implements JavaCast {
public class Kadargo implements Cast {
    private String name;

    public String getName() {
        return name;
    }

    public void save(){
        System.out.println("in java save");
    }

    public void setName(String name) {
        this.name = name;
    }

    public String race() {
        return "I am Luxan";
    }

    public String actor() {
        return "Anthony Simcoe";
    }
}

Scala Object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.far.scape.scala
//import com.far.scape.JavaCast
//class Crichton extends JavaCast  {
class Crichton extends Cast  {
  var name = ""

  def save() {
    println("in scala save")
  }

  def race():String = {
    "Frelling Human!"
  }

   def actor():String = {
    "Ben Browder"
  }
}

Groovy Object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.far.scape

import com.far.scape.scala.Cast
//class Chiana implements JavaCast {
class Chiana implements Cast {
  String name

  String race() {
    "I am Nebari"
  }

  String actor() {
    "Gigi Egdley"
  }

  void save() {
    println "in groovy save"
  }
}

One thing to note is that the directory structure matters.  There is a bug with the scala grails plugin at the time I wrote this post with grails 1.3.5+ and scala plugin 0.5.  I had to go into the scala plugin Events.groovy code and comment out the following line:

//addScalaToCompileSrcPaths(compileBinding)

I found the following note on the now-depreciated plugin page (not sure why this is gone now since the version didn’t change).

NOTES: current 0.5 version seems incompatible with Grails 1.3 As quick and dirty fix we do the following to the installed script scala-0.5/scripts/Events.groovy:

* comment out //addScalaToCompileSrcPaths(compileBinding)
* then we have to put scala sources under src/java (src/scala is not usable)

I have all my scala code under src/java/com/far/scape/scala.  I decided to put it in a dir under the package named scala so it was easier to differentiate.

I created a regular grails service under grails-app/services/[package- name]/GrailsService.groovy.  I mocked up a save method on the interface and put an implementation in each class.  Ideally I will be able to actually persist the objects, but for now I just used save on the interface to see how well the trait worked across java, scala and groovy.

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.far.scape

import com.far.scape.scala.Cast

class GrailsService {
    static transactional = true

    def saveObject(Cast cast) {
      println "Grails Service saving object ${cast.class.name}"
      cast.save()
      true
    }
}

Testing The Code

I wrote a really simply integration test to pass all the object types to the grails service saveObject method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.far.scape

import grails.test.GrailsUnitTestCase
import com.far.scape.scala.Crichton

class GrailsServiceTests extends GrailsUnitTestCase {
  def grailsService

  protected void setUp() {
    super.setUp()
  }

  protected void tearDown() {
    super.tearDown()
  }

  void testSaveGroovyObject() {
    def chiana = new Chiana(name: "Chiana")
    assertTrue grailsService.saveObject(chiana)
  }

  void testSaveJavaObject() {
    def kadargo = new Kadargo(name: "Ka'Dargo")
    assertTrue grailsService.saveObject(kadargo)
  }

  void testSaveScalaObject() {
    def crichton = new Crichton(name: "Crichton")
    assertTrue grailsService.saveObject(crichton)
  }
}

To run the integration tests execute the command below.  The -echoOut is to show the output from any print statements you have in your tests.

grails test-app -echoOut

Here is the result

Testing Mock Save

I played around a bit with creating a scala service.  I was able to get it working as a standard manually created bean, but didn’t get the auto- wiring/injection of the scala service working in the integration tests. Granted, I didn’t try that hard as I wanted to make that a topic of another post.

Client Side Code

I also added a simple controller and view to test adding different objects to gsp page.

1
2
3
4
5
6
7
8
9
10
11
12
package grails.scala

import com.far.scape.Chiana
import com.far.scape.scala.Crichton
import com.far.scape.Kadargo

class MoyaController {
    def index = {
      def characters = [new Chiana(), new Kadargo(), new Crichton()].asList()
      [view:"index","characters":characters]
    }
}

Then I created an index.gsp under grails-app/views/moya.

1
2
3
4
5
6
7
8
9
10
<html>
<body>
<g:each in="${characters}" var="character">
${character.class.name}<br>
${character.race()}<br>
${character.actor()}<br>
<p></p>
</g:each>
</body>
</html>

This should render the following:

Output View

All the source code for this can be found at GitHub.

Happy Coding.

Tuesday Night Worlds - 4/12/2010

| Comments

As a member of the OptumHealth Performance road racing team, we did our first criterium Tuesday night.  It was the Tuesday Night Worlds in the Opus complex.  You can view the course at Map My Ride course map.

Our strategy was to stay in the top 25% of the group and ride strong.  Things went as planned for the first two laps.  After that, Patrick was on his own and rode the remainder of the race out in the front group.  It was an amazing effort by the whole team for our first criterium showing.

Mitch dropped a chain on the first lap and was left on his own in no-mans-land for 5 or so laps until the group caught him again.  He grabbed back onto the group and hung strong through the rest of the race.

After the race we thought Colin and Patrick both both received points for sprints, but it turns out that all their efforts will in vein as they sprinted a lap early every time.  Now that we understand WHEN the sprints are we hope to get points for some of the riders next race.

The biggest surprise was Chris Frykman who, before Tuesday, I had only met on Facebook and heard about through the teammates.  He pulled like a locomotive up the hills in what I think was his 53x12 gear.  He was a power climbing machine.

My race went off without a hitch.  I led out the first lap and dropped back after the second and faded off the lead group into the second group.  I think I spent too much too early and eventually was playing yo-yo off the back of that group.  I finished just off the second group.  I sat up more than I should have, but after Patrick and the lead group were out of sight I was just trying to find some relief from the pain in my legs and lungs.  I am still dislodging phlegm from my lungs that apparently accumulated over the winter. I haven’t ever wheezed like this for so long after a race.  I think that is a testament on how hard we were riding.

I did a quick graph for the speed of the riders per lap. Mitch’s data for lap 5 is missing due to being caught by the group on that lap so I skipped him even with the group.

Speed By Lap

x400&cht=lxy&chco=3072F3,FF0000,FF9900,66AA00,FF9999&chds=1,9,15,25,1,9,15,25, 1,9,15,25,1,9,15,25,1,9,15,25&chd=t:1,2,3,4,5,6,7,8,9|21.9,22.9,21.9,20.4,20.6 ,20,19.8,19.1,19.2|1,2,3,4,5,6,7,8,9|22,23.5,21.9,21,21.8,20.8,20.4,21.8,20.5| 1,2,3,4,5,6,7,8,9|20.7,18.1,17.2,17.2,0,17.1,19,19.5,20.4|1,2,3,4,5,6,7,8,9|22 ,23.1,22,20.4,20.3,20.6,19.7,19.4,20.8|1,2,3,4,5,6,7,8,9|22,24,20.9,20.6,20.7, 19.9,19.7,20,20.7&chdl=Christian|Patrick|Mitch|Chris|Colin&chdlp=b&chg=0,0,0,0 &chls=1|1|1|1|1&chma=5,5,5,25&chtt=Speed+By+Lap

Jenkins Upgrade Script

| Comments

I got really tired of manually upgrading Jenkins on my build server so I wrote a little bash script to do it for me.

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
 echo "stopping tomcat"
 sh /etc/init.d/tomcat6 stop
 cd /var/lib/tomcat6/webapps
 echo "removing jenkins"
 rm -rf jenkins
 rm -rf jenkins.war
 echo "downloading latest jenkins"
 wget http://mirrors.jenkins-ci.org/war/latest/jenkins.war
 echo "starting tomcat"
 sh /etc/init.d/tomcat6 start
 cd ~
 echo "done"

I call mine upgrade.sh.  Make sure to set the permissions on the file to something executable; I set mine to chmod 775.  Then you can run this by typing the following line.  It should be run with elevated permissions so it can interact with processes like starting and stopping the server.

$  sudo ./upgrade.sh

You may have to tweak the locations to suit your needs.  Another thing you may want to do is move your jenkins folder instead of removing it, but since config is stored else where you should be okay to just remove the web app and put the new war in it’s place.

This script assumes you are running Jenkins under Tomcat and not standalone.

jQuery Timeout Dialog Widget

| Comments

The code for the plugin is available over on Github – jQuery Timeout Dialog Widget.

I was looking into what was available for making a browser idle timer that would redirect the user to a logoff page.  I really liked the work that was done by Paul Irish on his idleTimer plugin as well as Eric Hynd on his Idle Timeout Plugin.  The first thing I did was put their code onto our site.   It was pretty easy to hook up and get working.

Page Masking With jQuery

| Comments

As I mentioned in my last post we are trying to move to jQuery as a scripting solution over ExtJS or straight JavaScript.  When we wrote our application we were using ExtJS to do a mask on the page during a form submission so the user could have some useful feedback as to what was happening.

Here is the code that we wrote to do page masking.  In the form tag we simply put an onsubmit=“doLoadingMask()” and the mask is rendered to the page into the div with id=“loading-mask” that wraps all of the page content.  This stops the user from trying to click around and lets them know that something really happened when they clicked the button as not all users look to the browser icons spinning or status bar for details.

1
2
3
4
5
6
7
function doLoadingMask() {
    doLoadingMaskText('Loading...');
}

function doLoadingMaskText(loadingText) {
    Ext.get("loading-mask").mask(loadingText, 'x-mask-loading');
}

We ran into a problem that occurred very infrequent that would cause the form submit to hang for a long time… or indefinitely.  With the mask remaining on the screen the entire time and having the user blocked from interacting with the page, this was a problem.  So I rewrote the code to be “error/timeout” friendly in that it disappears after 30 seconds and the user can either reattempt the action of leave the page and come back.  The 30 second timeout does not stop the form submission from attempting to continue so given they leave it alone long enough it still might complete after the mask disappears.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function doLoadingMask() {
    doLoadingMaskText('Loading...');
}

var maskInterval;
var maskTimeout = 30;
var maskLoop = 1;

function doLoadingMaskText(loadingText) {
    Ext.get("loading-mask").mask(loadingText, 'x-mask-loading');
    maskLoop = 1;
    maskInterval = window.setInterval(maskUpdater, 1000);
}

function undoLoadingMask() {
    Ext.get("loading-mask").unmask();
    clearInterval(maskInterval);
}

function maskUpdater() {
    if(maskLoop >= maskTimeout) {
        undoLoadingMask();
    } else {
        if(maskLoop % 5 === 0) {
            Ext.get("loading-mask").mask("Waiting... " + (maskTimeout - maskLoop), 'x-mask-loading');
        }
    }
    maskLoop++;
}

This simply adds a 1 second interval call to invoke the maskUpdater and when the timeout is reached it just unloads the mask.  It also updates the mask with a “Waiting… [time]” every 5 seconds.

This solved the “infinite” mask problem and was, IMHO, a pretty simple solution.  After looking at the overall size of our ExtJS vs jQuery base + extensions, it was immediately clear that we wanted to get rid of as much of ExtJS as possible.  The 175k min file ext-all isn’t even the whole ExtJS library.  It is only the components we are using while the jQuery is the full caboodle.  The partial archaic file names are due to us using a JavaScript file compressor.

Every time I come across code written in ExtJS I rewrite it in jQuery.  Here is the same code as above using jQuery and jQuery UI to accomplish the same thing. We use a slightly different setup for the div as it doesn’t have to encompass the while page content. I just put this after the body tag

1
<div id="loadingMask" style="display:none"><p align="center"><img src="/icue/images/loading.gif">&nbsp;<span id="message">submitting data...</span></p></div>

with this JavaScript

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
        var maskInterval;
        var maskTimeout = 30;
        var maskLoop = 1;
        function maskUpdater() {
            if(maskLoop >= maskTimeout) {
                undoLoadingMask();
            } else {
                if(maskLoop % 5 === 0) {
                    $("#loadingMask").find("#message").html("waiting... " + (maskTimeout - maskLoop));
                }
            }

            maskLoop++;
        }

        function doLoadingMask() {
            $("#loadingMask").dialog({
                modal: true,
                width: 200,
                height: 110,
                position: [(window.width / 2),100],
                closeOnEscape: false,
                resizable: false,
                open: function(event, ui) {
                    $(".ui-dialog-titlebar-close").hide();
                    $(".ui-dialog-titlebar").hide();
                    maskLoop = 1;
                    maskInterval = setInterval(maskUpdater, 1000);
                }
            });
        }

        function undoLoadingMask() {
            clearInterval(maskInterval);
            $("#loadingMask").dialog("close");
        }

The setup for the dialog is a little more lengthy than the ExtJS version, but we have more control around the look and feel of the message box.  The open event override is setting the timer and also hiding the title and close bars.

For this jQuery to work you will need to include both jQuery base and the UI javascript with at least the dialog and base ui in it.

The old mask (content blacked/grayed out for security reasons)

The new mask both off and on