tag:blogger.com,1999:blog-80904752024-03-22T01:23:54.145-04:00TechnophiliaSri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.comBlogger35125tag:blogger.com,1999:blog-8090475.post-41193527489401402742014-08-29T11:29:00.000-04:002014-08-30T10:07:01.526-04:00Distrubuted JMeter testing using Docker<div dir="ltr" style="text-align: left;" trbidi="on">
Distributed performance testing using JMeter has been around for a while. The recipe goes something like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjX55FGfLh-Y2qFkJ09lUOIzVQ6v059XviWgPb9EBPgjWBm97u54pWLS-oEnop2ub_F5qzGMp669e-Dcf2Dq-ELFz7QdV4oJoX2VmED9xSMZ2kettccXof5nxUM7lYjP_bKdUItlQ/s1600/jmeter-details_graphml_-_yEd.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjX55FGfLh-Y2qFkJ09lUOIzVQ6v059XviWgPb9EBPgjWBm97u54pWLS-oEnop2ub_F5qzGMp669e-Dcf2Dq-ELFz7QdV4oJoX2VmED9xSMZ2kettccXof5nxUM7lYjP_bKdUItlQ/s1600/jmeter-details_graphml_-_yEd.png" height="440" width="640" /></a></div>
<span id="goog_772990974"></span><br />
A JMeter client (the green box referred to here as JMeter master) drives the process. It has the test script (the JMX file). The actual testing is done by n JMeter Server instances (blue boxes above). When the test is initiated, the JMX script and the necessary data files are made available to all the server instances. The servers run the test instructions and communicate the results back to the JMeter client.<br />
<br />
We had been implementing this recipe with the JMeter client running on a virtual machine (VM) and each of the JMeter server instances running on their own VM instances. Although this was an improvement on the bare-metal days it was fraught with problems:<br />
<br />
<ul style="text-align: left;">
<li>Provisioning of a VM with <i>just the right </i>pieces and parts -- a highly iterative process -- was tedious and time consuming</li>
<li>A change to the JMX script or the fixture data meant that the respective VM had to be reinstated. More tedium.</li>
<li>Constraints in our work environment made addition of new JMeter server instances laborious</li>
<li>Finally there was the cost of VM startup & shutdown. This was a nontrivial process and a time sink</li>
</ul>
<div>
I had been watching with interest the growing drumbeat of support for Docker. The more I looked into it the more I found it appealing.</div>
<div>
<ul style="text-align: left;">
<li>The relative ease of creating a Docker image</li>
<li>Ability to iterate, correct and improve on an existing image</li>
<li>Lastly the near non-existent startup time mean the feedback loop was really tight.</li>
</ul>
<div>
Noticing that the JMeter client (master) and the JMeter server were very similar from the point of view of provisioning I created a base JMeter image. Off of the base I created two other images -- JMeter (for the master) and JMeter-server. In summary:</div>
</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">jmeter-base</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> |</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> +-- jmeter</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> |</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> +-- jmeter-server</span></div>
<div>
<br /></div>
<div>
As you know the server process uses two ports; one to listen for instructions from the master and another to write responses back to the master. The server image exposed two ports for this purpose. In order to recreate the necessary interaction I did the following:</div>
<div>
<ul style="text-align: left;">
<li>Started n-instances of jmeter-server. Each of which was bound to two well known ports on the host</li>
<li>Using the container ID of the server instance I determined their IP addresses</li>
<li>Started the Jmeter client (master). The client image was crafted to receive the location of the remote server instances during invocation and write its log & test results back to the host</li>
</ul>
<div>
When the JMeter client started up it connected with every server instance. I monitored the master's log file on the host for all the action. When the tests completed I simply removed all the Docker containers. This left me with just the logs & test results! </div>
<div>
<br /></div>
<div>
Along the way I had eliminated all of my earlier annoyances.</div>
<div>
<br /></div>
<div>
<ul style="text-align: left;">
<li>Creating and modifying the Docker image to <i>just the right shape</i> was fast and easy</li>
<li>Since the JMX and data files were maintained on the host a change in these files simply meant I restarted my container</li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixpJhTI_ldIivLylzeyuvU8GHHNnfYpdVkmW-PUJapIMXM4L4oCXJNjRZ3PyHzasQ4w38LhIOOhu-FkEexWUxYzy8zX_EwD2quutv4GtoLfF2c0m769vMfDdQnWzfSh8KF4rYujw/s1600/jmeter-docker_graphml_-_yEd.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixpJhTI_ldIivLylzeyuvU8GHHNnfYpdVkmW-PUJapIMXM4L4oCXJNjRZ3PyHzasQ4w38LhIOOhu-FkEexWUxYzy8zX_EwD2quutv4GtoLfF2c0m769vMfDdQnWzfSh8KF4rYujw/s1600/jmeter-docker_graphml_-_yEd.png" height="489" width="640" /></a></div>
<div>
<br /></div>
<ul style="text-align: left;">
<li>Creating additional JMeter server instance was just a matter of another <span style="font-family: Courier New, Courier, monospace;">docker run</span> invocation</li>
<li>Finally startup time was almost imperceptible!</li>
</ul>
</div>
<div>
Perfect.....well, almost. I bothered me that the process of starting the containers and shutting them down was a manual process. So I started with a pie in the sky idea of what I would be an acceptable invocation and proceeded to implement one. I wanted to be able to do:</div>
</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">driver script datadir log-dir num-servers <script-file></script-file></span><span style="font-family: 'Courier New', Courier, monospace;"> <data-dir> </data-dir></span><span style="font-family: 'Courier New', Courier, monospace;"><log dir=""> </log></span><span style="font-family: 'Courier New', Courier, monospace;"><server count=""></server></span></div>
<div>
<br /></div>
<div>
Driver must:</div>
<div>
<ul style="text-align: left;">
<li>Create the specified number of JMeter server containers</li>
<li>Create the JMeter master container</li>
<li>Fire of the test</li>
<li>Wait for the test to complete</li>
<li>Remove all the containers</li>
</ul>
<div>
It took some scripting foo along with some Docker image revisions. I now have a setup that allows me to:</div>
</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">driver.sh -s jmxfile.jmx -d data-dir -n 8</span></div>
<div>
<br /></div>
<div>
This does everything I need except the container cleanup. I am in the process of implementing that.</div>
<div>
<br /></div>
<div>
Of course this only means I need to work on my next set of tweaks on my wish list. But that's for another post.</div>
<div>
<br /></div>
<div>
You can find the work referenced in this blog at:</div>
<div>
<br /></div>
<div>
<ul style="text-align: left;">
<li><a href="https://github.com/srivaths/jmeter-driver">https://github.com/srivaths/jmeter-driver</a></li>
<li><a href="https://github.com/srivaths/jmeter-base">https://github.com/srivaths/jmeter-base</a></li>
<li><a href="https://github.com/srivaths/jmeter">https://github.com/srivaths/jmeter</a></li>
<li><a href="https://github.com/srivaths/jmeter-server">https://github.com/srivaths/jmeter-server</a></li>
</ul>
</div>
<div>
I gave a lightning talk on this work. The slide deck I used for it is at <a href="http://www.slideshare.net/srivaths_sankaran/jmeter-docker-sitting-in-a-tree">http://www.slideshare.net/srivaths_sankaran/jmeter-docker-sitting-in-a-tree</a>.<br />
<br />
<hr />
A huge hat tip to my colleague <a href="https://twitter.com/vbatts" target="_blank">Vincent Batts</a> for helping me along the way and letting me use his machine. Vincent is a rare bird — brilliant technical chops, passionate, generous, patient and humble.</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
</div>
Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com30tag:blogger.com,1999:blog-8090475.post-87793719227322989512012-10-15T11:47:00.002-04:002012-10-15T11:47:38.523-04:00Women in IT<div dir="ltr" style="text-align: left;" trbidi="on">
<br />
I have come to appreciate the unique perspective each of us brings
to the job. By "perspective" I am going beyond technical skills and
programming acumen. It is that intangible quality that determines
our design decisions, the thought process that directs our problem-solving strategy,
that dictates how we communicate with our team. <br />
<br />
I feel that unique secret sauce that makes us who we are is
involuntarily seeded by our gender. To get a full breadth of
capabilities I think it best if projects have both men and women. The
most successful projects in which I have been involved included a mix of
men
and women. I don't think it is simply a coincidence. I know that
it is a <b>key reason </b>for the project's success. It is therefore regrettable that there aren't more women in our
line of work.<br />
<br />
I came across a
<a href="http://blogs.tedneward.com/2012/10/12/On+Equality.aspx" target="_blank">thought-provoking blog post</a> by Ted Neward on this very
subject. In case you aren't familiar with Ted, he is an outstanding
technologist. I got to know Ted back when I used to speak on the <a href="http://www.nofluffjustuff.com/">NoFluffJustStuff</a> tour.
The thing about Ted is that not only does he have impeccable
technical credentials (Java, .net, Android, etc etc) he is not shy
to share his opinion. His presentations are quite an experience -- just be prepared for a side-bar or two on unrelated topics :-)<br />
<br />
Here's hoping more women join the IT workforce.<br />
</div>
Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com1tag:blogger.com,1999:blog-8090475.post-24764080958508388532011-01-29T14:35:00.000-05:002011-01-29T14:38:30.670-05:00Can't Git enough<div>Couple of weeks ago I attended the January offering of <a href="https://github.com/training/online">GitHub's online Git class</a> taught by Matthew McCullough. I had been using Git for a little over a month and was starting to plateau off. I grokked the basics (at least so I thought) and stayed within the lines. The timing of the course was perfect for me. It helped solidify some concepts, explain the git idiom and presented several aha! moments. It was the kind of course that leaves you energized and exhausted at the same time. Energized because you learned something that you could apply immediately (like during the lunch break :-) and exhausted because you were facing a fire-hose of information for 7 hours!<br /><div><br /></div><div>The course material was well organized starting with just enough background information used to set the stage. Demonstrating Git's pedigree (been around for over 10 years) in the SCM space lent credence to those not impressed with its slavish following in the industry. The material was in resonance with what Matthew was saying -- amplifying his point where necessary, enumerating steps, screenshots where appropriate and links for additional information. Another sign of his thoroughness was demonstrating setup in all major platforms and not leaving the student high and dry having to connect the dots. Over the course of 7 hours (Six 50-min sessions with an hour for break) the class covered everything from setup to 3-stage thinking, branching, remote repos, with just the right amount of peek under-the-hood to explain how Git works.</div></div><div><br /></div><div>The format of the course was as hands-on as <i>you </i>wanted it to be. The attendees were provided instructions in advance of the class on setting up their local environment. During the course Matthew helped us connect to the GitHub repository he was referencing. This allowed as to follow along with the steps <i>he </i>was doing <i>as he was doing it</i>! If that wasn't your style you could just watch and listen. It was very stimulating and engaging -- especially when several of us were <i>simultaneously </i>pushing our changes to the same remote repository!!</div><div><br /></div><div><br /><div><a href="http://twitter.com/#!/matthewmccull">Matthew McCullough</a> did a first rate job of the online class. It was conducted over WebEx and went over without any major hitch. I was impressed with how Matthew kept up with several stimuli vying for his attention. He addressed the questions being posed over WebEx, emails sent to him and<i>even tweets </i>all the while staying on course! I wonder if he did a <tt>git clone matthew</tt> before the class! He answered questions and addressed issues seamlessly as if it were part of the course material.</div><div><br /></div><div>Matthew belongs to a rare breed that is at the locus of <b>knowledge of the subject</b>, <b>desire to teach</b> and <b>thoroughness of preparation</b>. He set the bar very high at other fora at which I have watched him teach. This Git class did not disappoint. He did his homework and came prepared to teach. From attending several software symposia I can tell you that this is a rare trait. Presenters tend to lean heavily on the content. Yes, content is King. However one should know how to deliver it. Think: Otherwise I could just read the book. Matthew's passion not just for Git but for teaching shone through even at hour-7 of the class!</div><div><br /></div><div>If you are new to Git you could not do any better than having Matthew as your teacher.</div><div></div></div>Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com4tag:blogger.com,1999:blog-8090475.post-61263410835152643952009-06-19T23:49:00.011-04:002009-06-22T22:55:06.510-04:00A rose by any other name...Martin Odersky, Lex Spoon, and Bill Venners have <a href="http://www.artima.com/lejava/articles/equality.html">collaborated</a> to venture where everyone has gone before. Yes, I mean the age old story about right way to override the <tt>equals</tt> method in Java. I see you rolling your eyes with the "I know, I know I must override <tt>hashcode</tt> method as well" bored refrain. <br /><br />Spare me a few more minutes and I will convince you that you must give the article a serious once over. I for one got a better appreciation of the complexity of the problem -- if you want it addressed fully.<br /><br />The article warms up around the 3<sup>rd</sup> of the four pitfalls they identify<ol><li>Defining <tt>equals</tt> with the wrong signature</li><li>Changing <code>equals</code> without also changing <tt>hashCode</tt></li><li>Defining <code>equals</code> in terms of mutable fields</li><li>Failing to define <code>equals</code> as an equivalence relation</li></ol>Pitfall #3 discusses what happens when the state of an object that has been placed in a collection is modified. Typical <code>equals</code>method implementation will result in it <b>disappearing from the collection</b>. <br /><pre><br /> public class Point {<br /> private int x, y;<br /> public Point(int i, int j) { x = i; y = j; }<br /> public int getX() { return x; }<br /> public void setX(int i) { x = i; }<br /> public int getY() { return y; }<br /> public void setY(int i) { y = i; }<br /> @Override public int hashCode() { return 41 * (41 + getX()) + getY(); }<br /> @Override public boolean equals(Object other) {<br /> boolean result = false;<br /> if (other instanceof Point) {<br /> Point that = (Point) other;<br /> result = (this.getX() == that.getX() && this.getY() == that.getY());<br /> }<br /> return result;<br /> }<br /><br />}<br />..<br />HashSet hashSet = new HashSet();<br />Point p = new Point(1, 2);<br />hashSet.add(p);<br />p.setX(12);<br />System.out.println(hashSet.contains(p)); // <-- Prints false!!<br /></pre><br />Yep! A forehead smacking moment alright. The article suggests a way around this problem. Which leads us to the 4<sup>th</sup> pitfall -- that of equivalence. This means that for non-null values, the following is true about the <tt>equals</tt> method:<br /><ul><li>Reflexivity: <tt>x.equals(x)</tt></li><li>Symmetry: <tt>x.equals(y)</tt> => <tt>y.equals(x)</tt></li><li>Transitivity:<tt>x.equals(y)</tt> and <tt>y.equals(z)</tt> => <tt>x.equals(z)</tt></li><li>Consistence: <tt>x.equals(y)</tt> <em>always</em> returns the same result</li><li> <tt>x.equals(null)</tt> is always <tt>false</tt></ul>With clear and concise examples, the article explains the complications of these contracts and how to implement them.Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com1tag:blogger.com,1999:blog-8090475.post-3836367196648371762009-04-07T23:11:00.003-04:002009-04-07T23:21:13.241-04:00Run a single JUnit test method in EclipseGoes to show you, that you <span style="font-style:italic;">can</span> teach an old dog new tricks. After years of using Eclipse I stumbled across a feature I am sure has existed since the glory days of Visual Age.<br /><br />I had added yet another test method to a large JUnit TestCase class. I wanted to just run and re-run the new method 'til I had it <span style="font-style:italic;">just right</span> without having to run all the other methods as well.<br /><br />Yes of course, I could comment out all the other methods or just remove the <tt>@Test</tt> annotation. Back in the JUnit 3 days we have all -- at one time or another -- disabled tests by changing the "<tt>test</tt>" prefix of the test methods.<br /><br />Somehow today, all these seemed abhorrent.<br /><br />I wondered what would happen if I simply highlighted the method I wanted to test, and selected <span style="font-weight:bold;">Run As > JUnit Test</span> from the context menu.<br /><br />Voila! That was the ticket!! It ran just the test I had wanted run.<br /><br />As a bonus it even added TestCase.method_name as a "Run Configuration" for ease of future use.<br /><br />FTW!Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com7tag:blogger.com,1999:blog-8090475.post-1883818062475294842009-02-27T14:22:00.007-05:002009-03-01T22:16:36.205-05:00Mockito: Most helpful stacktraceOne of the things I have admired about the <a href="http://www.springsource.org/">Spring framework</a> is its helpful error messages. Yeah, of all the things I could point to, I picked that....However, work with me here. What I mean is that when things go wrong when using Spring, it spits out error messages that are full sentences that make sense and point the developer in the proper direction. None of the <tt>NoClassDefFoundError</tt> but not telling who <i>which</i> freaking class.<br /><br />Now, I think <a href="http://mockito.org/">Mockito</a> -- the Java mocking framework -- has taken over the mantle of "King of error messages". Earlier today I had a new unit test I had just written fail. I didn't have to dig too much to find the error because Mockito pointed the way with the following stacktrace.<br /><pre><br />org.mockito.exceptions.misusing.UnfinishedStubbingException: <br />Unifinished stubbing detected!<br />E.g. toReturn() may be missing.<br />Examples of correct stubbing:<br /> when(mock.isOk()).thenReturn(true);<br /> when(mock.isOk()).thenThrow(exception);<br /> doThrow(exception).when(mock).someVoidMethod();<br />Also make sure the method is not final - you cannot stub final methods.<br /> at<br />etc. etc. <br /></pre><br />How cool is that! Hats off, Mockito-team. This developer appreciates the extra few minutes you took to put together that message. It helped me immensely.Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com2tag:blogger.com,1999:blog-8090475.post-13002996835306417932009-02-18T21:03:00.005-05:002009-02-18T21:36:04.740-05:00Tab navigating the Mac UIIt was driving me nuts when I got my MacBook that I could not use the tab button to navigate across a form. Let alone forms, on a browser page, I could only tab between the address bar and the search field. To go anywhere else, I <b>had to</b> reach for the mouse. Aaargh!!<br /><br />Surely the operating system that was God's gift to humanity could do better than that! It took a fair amount of Googling for me to find the obscure setting that controls it. <br /><br />Here's what you need to do in order to be able to tab away across the entire screen.<br /><br />Bring up the System Preferences window and select Keyboard & Mouse from the Hardware Category.<br /><br /><img src="http://img.skitch.com/20090219-gm445r79tbp4wfk23fkw8qb42b.jpg"/><br /><br />From the "Keyboard & Mouse" panel, select the "Keyboard Shortcuts" tab. The magical setting is at the bottom of that tab. Allow "Full keyboard access" by selecting the "All controls" radio button.<br /><br /><img src="http://img.skitch.com/20090219-8d3ifcn2w2khwgrh5hchrchs6b.jpg"/><br /><br />As this panel shows, you can impress your colleagues by toggling this setting using CTRL+Fn+F7.<br /><br />The question remains, who in their right mind thought that the default setting was to be "Text boxes and lists only"!!Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com0tag:blogger.com,1999:blog-8090475.post-65206246176513647442009-02-13T16:27:00.000-05:002009-02-13T16:28:24.944-05:00JUnit 3 & JUnit 4: Oil & waterVersion 4 of the popular <a href="http://junit.org/">JUnit test framework</a> has been available for use for quite some time (In fact, it is up to version 4.5). However, many projects have a wealth of JUnit 3-style tests and developers may choose to continue using it. If, on the other hand, you decide to dip your toes in JUnit 4 waters, make it a complete immersion. Don't try to mix and match.<br /><br />As you may be aware, couple of the touted "benefits" of JUnit 4 are the obviation of extending the <code>TestCase</code> class and the identification of test and lifecycle methods (<code>setUp</code> and <code>tearDown</code>) using Java 5 annotations. For example, the following is a valid JUnit 4-style test case (minus the necessary imports):<br /><tt><br /> public class BusinessLogicTests {<br /> @Before<br /> public void do_this_before_every_test() { <br /> // set up logic goes here<br /> }<br /><br /> @After<br /> public void clean_up_after_a_test() {<br /> // teardown logic goes here<br /> }<br /><br /> @Test<br /> public void addition() {<br /> assertEquals("Invalid addition", 2, 1+1);<br /> }<br />}<br /></tt><br />Observe how <br /><ul><br /> <li>The class does not extend <code>TestCase</code></li><br /> <li>The "setup" and "tearDown" methods are identified by annotations</li><br /> <li>The test method does not require a "test" prefix and is also annotated</li><br /></ul><br />I'll let <i>you</i> be the judge of whether this is an improvement on JUnit 3. What I want to alert you to are the perils of subconciously mixing JUnit-3 and JUnit-4 style programming. Consider the following test case:<br /><tt><br /> public class BusinessLogicTests extends TestCase {<br /> @Before<br /> public void do_this_before_every_test() { <br /> // set up logic goes here<br /> }<br /><br /> @After<br /> public void clean_up_after_a_test() {<br /> // teardown logic goes here<br /> }<br /><br /> @Test<br /> public void testAddition() {<br /> assertEquals("Invalid addition", 2, 1+1);<br /> }<br /><br /> @Test<br /> public void subtraction() {<br /> assertEquals("Invalid subtraction", 1, 1-1);<br /> }<br />}<br /></tt><br />This will result in a green-bar even though there is an error in the test of subtraction. Can you guess why?<br /><br />It was that pesky little <code>extends TestCase</code> that I added from force of habit. The flips the switch to JUnit-3, which knows nothing about annotations, ignores my setup and teardown methods and looks for methods prefixed by <code>test</code>. It <b>completely ignores the method named <tt>subtraction</tt></b>!<br /><br />And of course, conversely, the following is just a plain old Java class that won't be acknowledged by either JUnit-3 or JUnit-4.<br /><tt><br /> public class BusinessLogicTests {<br /> public void testAddition() {<br /> assertEquals("Invalid addition", 2, 1+1);<br /> }<br />}<br /></tt><br />Final word of advice, if you are using <a href="http://strutstestcase.sourceforge.net/">Struts Test Case</a>, you have no choice but to write JUnit-3 style tests since the parent class of the framework (<tt>MockStrutsTestCase</tt>) extends Junit-3's <tt>TestCase</tt>.Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com5tag:blogger.com,1999:blog-8090475.post-13165351068514392952009-01-09T08:56:00.002-05:002009-01-09T08:59:31.865-05:00Oh! Dash it...I spent half a day cursing and spewing bile at JavaScript when after all was said and done, it was doing the right thing. The ignominy of it all...My writing here is part penance for casting aspersion at JavaScript and part hoping that <i>you</i> don't have to go through this agony.<br /><br />I have a typical Java Server Page (JSP) with its share of HTML, scriptlets (gasp) and JavaScript. Much of the HTML is neatly organized in <code><div></code> tags. The JavaScript controlled the toggling the visibility of some of the <code>div</code>s.<br /><br />When I tested the page and invoked the script, I didn't see the expected behavior. <a href="http://www.getfirebug.com/">Firebug</a> even told me that there was an error when the JavaScript was invoked. It went so far as to tell me that<br /><br /> <code>document.getElementById(a_div_id) is null</code><br /><br />By setting breakpoints in the script I quickly determined that, not surprisingly, the variable <code>a_div_id</code> referred to the <code>id</code> of a <code>div</code> on the page. It was there in the code -- clear as daylight. Here is about where the JavaScript cursing began. When that didn't help -- for the record, it never has -- I pulled out my trusty <a href="http://www.squarefree.com/shell/">JS shell</a> tool. Still muttering under my breath "I'll show <i>you</i> how it's done", I pounded the command<br /><br /> <code>document.getElementById("line_items").style.display = 'block';</code><br /><br />fully expecting to see the <code>div</code> with <code>id=line_items</code> to pop up.<br /><br />Nope! Nothing but silence. The next port of call was the HTML source of the page in question. Sure enough, I was able to locate the <code>div</code> in question. However, the syntax highlighting showed that the <i>entire <code>div</code> was commented out</i>!!!! So, my script <i>was correct</i> in saying that there was no such element in the document.<br /><br />Turns out that culprit was the use of textual em-dashes<sup>1</sup> in my comments. Typically em-dashes are represented in text as two consecutive hyphens thusly<br /><br /> <code>--</code><br /><br />WYSIWYG editors (such as MS Word) are wont to convert that into a single em-dash<br /><br /> <code>—</code><br /><br />However, there is no such implicit conversion in a JSP or HTML page. So there it was smack in the middle of the comment.<br /><br /> <code><!-- This is bad news –– if you ask me. --></code><br /><br />It colluded with other such comments on the page to make a mess of what <code>div</code>s were <i>actually there</i>. I know HTML wonks out there are going to chime-in in unison with a "Well, D'uuuuh! Don't you know that that's improper HTML syntax". OK, for the rest of us, the Web Design group <a href="http://htmlhelp.com/reference/wilbur/misc/comment.html">provides </a>a pithy maxim to code by<br /><br /><blockquote>An HTML comment begins with "<!--", ends with "-->" and does not contain "--" or ">" anywhere in the comment. </blockquote><br />The <a href="http://www.w3.org/TR/html401/intro/sgmltut.html#h-3.2.4">formal definition of an HTML comment</a> can be found at W3C. <br /><br />My peeve now is that you would think that a self-respecting IDE like Eclipse<sup>2</sup> would point that out for you. Now that I think back, even a puny little editor like <a href="http://www.vim.org/">VIM </a>would flag this error. Heck, Eclipse doesn't even acknowledge <code><!></code> as a valid comment. If Eclipse had shown the code correctly as being in a comment, I would not have wasted time down the JavaScript rabbit hole in the first place.<br /><br />All this is coming at the end of what has been a long week of futzing with Eclipse's sloppy handling of JSPs in general. Yes, this is with an Eclipse that is souped up to use the <a href="http://www.eclipse.org/webtools/">Eclipse Web Tools Project (WTP)</a> "goodness".<br /><br /><hr /><br /><sup>1</sup> em-dashes (—) Denotes a pause in thought, a parenthetical statement, or — more casually — an afterthought. For more on this subject, see <a href="http://www.kajhaffenden.com/articles/em-dash-en-dash-hyphen/">What’s the Difference Between an Em Dash, En Dash & Hyphen?</a>. BTW, did you know that an em-dash is called that because it is the width of a an 'm' in the current font? Useless trivia.<br /><sup>2</sup> For the record, I <i>am</i> using the latest version 3.4.x (Ganymede).Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com0tag:blogger.com,1999:blog-8090475.post-48975217791391956172008-11-24T16:25:00.004-05:002009-01-09T09:03:12.444-05:00Getting Hudson going on a Windows serverAfter getting all giddy about the ease of running <a href="https://hudson.dev.java.net/">Hudson</a> (java -jar Hudson.war), I ran into rough weather pretty quickly. The main problems:<br /><ol><li>Running under <a href="http://tomcat.apache.org/">Tomcat</a></li><li>Accessing our CVS server<br /></li></ol>As the saga unfolded the two issues got inexorably intertwined. After the Winstone experiment worked swimmingly, I cranked up Tomcat and dropped the Hudson WAR into the Tomcat webapps directory and it dutifully deployed up the application. I then configured Tomcat to run as a service.<br /><br />Here was the first sign of trouble. When I ran a project build from Hudson, it reported a problem accessing CVS. It gave the vanilla CVS response to login first. Login as <span style="font-style: italic;">whom</span>? The trouble was that the Tomcat service was running as the Windows system account. However, the system account is not authorized on the CVS server. Hmm...<br /><br />Methinks I'll run Tomcat under the credentials of the account that <span style="font-style: italic;">does </span>have access to CVS. I restart the service after this change and try to start another build.....however....I can't access Hudson. The Tomcat admin console reported that Hudson <span style="font-style: italic;">was</span> running. Yet, when I accessed its URL, I didn't get a response. Uh oh!<br /><br />To recap,<br /><ol><li>If I ran Tomcat under the Windows system account, I cannot access CVS</li><li>If I ran Tomcat with an account that <span style="font-style: italic;">could </span>access CVS, Hudson's pages weren't being delivered....and let me throw in to the mix</li><li>To make matters a little more intractable, CVS passwords when using <a href="http://www.cvsnt.org/">CVSNT </a>(the common CVS client on Windows servers) is stored in the registry and not in a .cvspass file.<br /></li></ol>Given all these facts, we decide to hack the registry by copying the CVS-authorized user's password into the profile of the system account. We had to go to some Microsoft web site to find out the arcane SID for the System account. Once we located the account, we created a cvspass entry for it and copied the password from the authorized user's account. Then, we reverted the Tomcat service to use the system account account and triggered a project build. No dice :-(<br /><br />It became clear that the solution had to be found while running Tomcat as the CVS-authorized user. However, why wouldn't Tomcat serve the Hudson pages?<br /><br />It turned out the reason was that Tomcat had been installed by the administrator to a location that the CVS-authorized user did not have write-access. However Hudson required write-access to the logs directory! Once we gave this account the necessary permission, everything was hunky dory.<br /><br />Phew! That was quite a workout. I am not sure how typical/atypical is our user and environment set up. For sure it highlighted several of the moving parts and many of the things that <span style="font-style: italic;">can </span>go wrong.<br /><br />Here's hoping you aren't here because you ran into similar problems!Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com1tag:blogger.com,1999:blog-8090475.post-26921093778454158232007-04-24T23:34:00.000-04:002008-11-17T23:17:08.528-05:00DBUnit + Sequence numbers + Foreign keys = Nightmare!Setting up my unit tests with reliable fixtures is turning out to be quite a headache.<br /><br />Sparing you arcane application-speak let me use the classic simple EMPLOYEE-DEPARTMENT example to illustrate my problem.<br /><br />Facts:<br /><ul><li>We have the typical EMPLOYEE table and the DEPARTMENT table</li><li> Both use database-generated ids (sequence numbers) as primary keys.</li><li>An EMPLOYEE belongs to a DEPARMENT.</li><li>The DEPT_ID field in the EMPLOYEE table is a foreign key into the ID field of the DEPARTMENT table.</li></ul>So far, so good.<br /><br />Now, consider a method in the <span style="font-family: courier new;">EmploymentDao </span>class that returns all known employments. In SQL speak, it executes<br /><br /><pre>Select e.emp_name, d.dept_name<br />From employee e, department d<br />Where e.dept_id=d.id</pre><br />In order to test this method I need to set up the database with necessary data. I would like to be able to use something like DBUnit to set up my fixtures. However, I have to populate the DEPARTMENT table with at least one department record. Then, I need to get the ID field of <span style="font-style: italic;">that </span>record to insert a record into the EMPLOYEE table. How does one do that ? This is crucial for a test such as<br /><br /><pre>public void testGettingAllEmployments() {<br /> List employments = employmentDao.getAllEmployments();<br /> assertNotNull("Empty list of employments", employments);<br />}</pre><br />Even with a simplification of suppressing foreign key constraints, I will have to do something like:<br /><ul><li>Insert a record into the DEPARTMENT table with a DBUnit fixture. <br /></li><li>Insert a record in the EMPLOYEE table (with a bogus dept_id)</li><li>Then read the record, from the DEPARTMENT table to get at its id</li><li>Update the record in theEMPLOYEE table to set the dept_id</li></ul>Wow! That's a lot of preamble -- not to mention using of the very DAO methods I want to test!!Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com4tag:blogger.com,1999:blog-8090475.post-7514627058689512982007-02-21T22:58:00.001-05:002008-11-17T23:17:08.529-05:00Hello Google Reader. Goodbye Bloglines<div xmlns="http://www.w3.org/1999/xhtml">I have been a longtime user of Bloglines. Sometime last year when Google introduced the Google Reader (GR) I took a looksee and was severely underwhelmed and so I stayed with Bloglines. One of my primary reasons was that GR did not work with my reading style. You see I like the blog posts grouped by feed -- all Slashdot posts together, all Javablogs together -- separated from the Slashdot posts.<br /><br />Of course, the choice of Bloglines over over a locally installed reader was a no-brainer; I did not want to have to maintain a copy of my blogroll on each machine that I used during a typical day.<br /><br />I recently peeked over to Google-land and lo and behold, this one feature that had been the deal-breaker for me had been added. In addition, were some very useful features that won me over. Here are the things that clinched the deal for me:<br /><br /><ul><li>List view -- This allows me to glance at about 50-60 posts in a group at a time. If I am interested, I click on it to expand it and read a synopsis.</li><li>Ability to "star an item"</li><li>Keyboard shortcuts -- Boy! Do they ever have shortcuts. I haven't mastered them all yet</li><li>Ease of renaming feeds</li><li>Ability to selectively mark items as having been read -- Bloglines has a severe problem in that when you select a blog, every unread article in the blog is displayed and (this is the problem) they are all <i>immediately </i>marked as "read". Very often I start looking at feeds from a blog and before I can finish scanning all the unread posts, I am called away. With GR, I <i>know </i>where I left off<br /></li><li>Progressive loading -- The reader doesn't attempt to load every article in the selected grouping in one fell swoop</li><li>Configurable start page -- I like my starred items to be displayed when I bring up the reader since well, I starred the item for a reason -- so that I can read them later!<br /></li></ul>Of course there is always a wishlist. Here's mine:<br /><br /><ul><li>Ability to choose the order of display in the list view <br /></li><ul><li>[UPDATE] Anonymous commenter has shown me how! However I wish this wouldn't be a <span style="font-style: italic;">per-feed </span>setting. OK, have a <span style="font-style: italic;">per-feed </span>setting if you must but the one must be able to customize the default at a <span style="font-style: italic;">blogroll level</span>.<br /></li></ul><li>Ability to mark an item as unread (I wouldn't at all be surprised if this feature is already there and that I just haven't figured out the keyboard shortcut)</li><ul><li>[UPDATE] Thank you, Anonymous commenter for showing me the way!<br /></li></ul><li>Speed it up, please! Were it not for the features, GR's sluggishness would've be a deal-breaker!</li></ul><br /><br /><br /><br /><br /><p class="poweredbyperformancing">powered by <a href="http://performancing.com/firefox">performancing firefox</a></p></div>Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com2tag:blogger.com,1999:blog-8090475.post-1147707637463658982006-05-15T11:12:00.000-04:002008-11-17T23:17:08.529-05:00JBoss Rules...Well, not quite!I have been attempting to work with JBoss Rules (nee Drools) and have mostly met with frustration.<br /><br />With the acquisition by JBoss, the Drools site (www.drools.org) pointed me to JBoss for v3 of the API. In fact, today, I can't even get to drools.org any more. Now cleverly annointed "JBoss Rules", I downloaded the version 3.0 RC2 of both the binary and the IDE. <br /><br />No joy, however (several errors reading the DRL files). Tried various alternatives including <a href="http://article.gmane.org/gmane.comp.java.drools.user/3931/match=3+2m5">recommendations</a> on their mailing list by some of the project's leading lights that one use Eclipse 3.2 M6 and not 3.2 M5 as indicated by their docs. Still no joy.<br /><br />Finally, I decided to download the source and built v3.0. Pay dirt! Actually, you don't need Eclipse 3.2 M6 -- the docs were right it works just fine on M5.<br /><br />On the subject of docs, they are mere shells. The original Drools site used to have a plethora of documented examples, and usage documentation. Not any more. What is the syntax for the new DRL files? I see "duration" is a long, but what <span style="font-style: italic;">is</span> it? About the DSLs? Why get rid of perfectly good documentation of the concepts?<br /><br />OK, since the docs aren't there yet, I decided to join the mailing list. Again, I followed the <a href="http://labs.jboss.com/portal/jbossrules/lists.html">instructions </a>and tried to subscribe by sending email to user-subscribe@drools.codehaus.org. Again, no joy. I got an automated response that read<br /><br /><blockquote><span style="font-style: italic; color: rgb(102, 102, 204);">Hi. This is the qmail-send program at codehaus.org.</span><br /><span style="font-style: italic; color: rgb(102, 102, 204);">I'm afraid I wasn't able to deliver your message to the following addresses.</span><br /><span style="font-style: italic; color: rgb(102, 102, 204);">This is a permanent error; I've given up. Sorry it didn't work out.</span><br /><br /><span style="font-style: italic; color: rgb(102, 102, 204);"><user-subscribe@drools.codehaus.org>:</span><br /><span style="font-style: italic; color: rgb(102, 102, 204);">Sorry, no mailbox here by that name. </span><br /></blockquote><br />So, what next?<br /><br />JBoss Rules...I don't think so.Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com0tag:blogger.com,1999:blog-8090475.post-1138999462502122562006-02-03T15:43:00.000-05:002008-11-17T23:17:39.530-05:00RailsConf: If you announce it, they will come...The <a href="http://www.railsconf.org/">RailsConf</a> has been getting a lot of press. So methinks let's see what the hullabaloo is all about.<br /><br /><b>Q: What are the sessions going to be?<br /></b>Silly me, that's not been decided yet. Don't worry they have over 80 proposals.<br /><br /><b>Q:Where is it going to be held?</b><br />The web site says: Chicago, Venue TBA. TBA!!? What kind of amateurish prank is this? Oh, well, drill down a few pages further and you figure it's at a Wyndham hotel. Phew! At least I know where I'll be staying!<br /><br />Important details aren't fleshed but they want you to register.... <a href="http://railsconf.org/pages/registration">Do they ever</a>! Better get there soon if you want to catch the "super early bird" blue light special. What gall!<br /><br />That's not all, Mike Clark, Dave Thomas and cohorts (from the NFJS circuit) are drumming up the support on their blogs.<br /><br />I am sure -- given the track record of the names named -- it will be a great conference. However I did not expect such premature promotion from so far east of Redmond. The Ajax Conf (or Experience or whatever they call it) is another example of a conference that was announced, much hyped before any details were available.<br /><br />Is this to be the trend? Announce a conference, announce keynote speakers, build up clientele on the shoulders of the blogosphere an <em>then</em> work out the details?Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com1tag:blogger.com,1999:blog-8090475.post-1136122704221888352006-01-01T08:17:00.000-05:002008-11-17T23:19:49.927-05:00Wisdom of Amazon crowds?I often find myself relying on the <a href="http://www.amazon.com/gp/product/0385721706/qid=1136121413/sr=8-1/ref=pd_bbs_1/103-0068111-2712619?n=507846&s=books&v=glance">wisdom of crowds </a>to guide my purchasing decision. How so? I go check out the customer reviews at <a href="http://www.amazon.com">Amazon</a>. This has been a very a very good barometer for my purchase.<br /><br />Analyzing my habit a little further, I realized that I don't just pick out the majority opinion. BTW, is there a way to get a summary of the customer reviews for an item? Such as,<br /><br />Acme's New Super duper toaster ratings:<br />95% -- 4+ stars<br />2% -- 3 stars<br />2% -- other<br /><br />probably could using <a href="http://www.amazon.com/gp/browse.html/103-0068111-2712619?node=3435361">Amazon web services</a>. Anyway, what I seek out is the not-so-silent minority. I want to see why, while the page is filled with 5-star or 4-star reviews, there are a discordant few. I do read (some of) the majority opinions. However, you tend to get the drift after the first few. Have you noticed that the majority opinion cites similar reasons? However, the minority seem to have more variety -- or maybe its just a matter of numbers.<br /><br />It is the gripes I pay close attention to: is the review just a sore rant or do the complaints seem to have merit. Are these reasons something that I feel is important to me -- or do I not care that the toaster doesn't have a mirror-like sheen?<br /><br />Such an analysis has held me in good stead over the past few years of major and minor purchases. In retrospect, this seems to weight the <span style="font-weight: bold; font-style: italic;">few </span>heavier than the <span style="font-weight: bold; font-style: italic;">many </span>-- contrary to Surowiecki's contention.Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com1tag:blogger.com,1999:blog-8090475.post-1135631107876277022005-12-26T15:58:00.000-05:002008-11-17T23:17:08.530-05:00AJAX Experience...Yet another boondoggleA colleague of mine upon hearing of the Spring experience, said "what a boondoggle". That captured <span style="FONT-STYLE: italic">exactly </span>how I felt about it.<br /><br />Now, I don't object to one man's opinion of enterprise. I know Jay Zimmerman and <a href="http://srivaths.blogspot.com/2005/06/reasons-to-attend-nofluffjuststuff.html">I applaud him </a>for putting up a fabulous show time and time again with his <a href="http://www.nofluffjuststuff.com/">No Fluff Just Stuff series</a>. However a symposium just on Spring? C'mon. If you are new to Spring, a symposium isn't a way to learn to use it. If you already using Spring, you may get something out of 10-20% of the sessions. How do I figure? Due to many level 101 sessions, and others that you can't attend due to conflicts.<br /><br />Hard on its heels comes the <a href="http://www.theajaxexperience.com/coming_soon.jsp">AJAX experience</a>. A symposium essentially on <span style="FONT-WEIGHT: bold">one Javascript API call</span>! Do we <span style="FONT-STYLE: italic">really </span>need a <span style="FONT-STYLE: italic">conference </span>on <span style="FONT-STYLE: italic">AJAX</span>? Aren't there enough online articles, blogs, and books (don't get me started) on the subject?<br /><br />My prediction: Coming soon shall be a Ruby Experience -- which actually is a topic worthy of an "experience". I guess what I really envy is Jay's entrepreneurial spirit. You go Jay!Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com3tag:blogger.com,1999:blog-8090475.post-1121959943537030352005-07-21T10:16:00.000-04:002008-11-17T23:17:08.530-05:00Maven catch-22 with multiproject buildsMaven's multiproject plug-in is a very useful tool on a large project that's been organized into several sub-projects. Using the dependencies the reactor figures out the correct order in which to build the entire project. However Maven gets in its own way when doing such builds. Here's how:<br /><br />Consider a multiproject with sibling sub-projects that are interdependent. Having been bitten one too many times for not doing so, I have become fastidious about <span style="font-family:courier new;">maven clean</span>ing before every build. Just as with any other plugin, when you invoke <span style="font-family:courier new;">clean</span>, <span style="font-family:georgia;">Maven checks the dependencies. However, when this happens the first time the multiproject is being built, the dependencies cannot satisfied and so the build quits with a failed dependency.<br /><br />If you are wondering why get so anal about cleaning and that it isn't needed before the first-ever build, consider setting up your builds to be managed by <a href="http://cruisecontrol.sf.net">CruiseControl</a>. That's an automated process and needs to be given the goal(s) that must be executed at <span style="font-style: italic;">every build cycle</span>.<br /><br />So, the only solution I know is to perform a one-off build <span style="font-style: italic;">manually</span> without a <span style="font-family: courier new;">clean </span>and then let CruiseControl loose. Can you suggest a better alternative?<br /><br />Speaking of cleaning and multiprojects, why doesn't <span style="font-family: courier new;">multiproject:clean</span> clean the root directory? Why do I have to <span style="font-family: courier new;">maven clean multiproject:clean</span>?<br /></span>Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com1tag:blogger.com,1999:blog-8090475.post-1118680478135453932005-06-13T12:10:00.000-04:002008-11-17T23:17:08.531-05:00Reasons to attend a NoFluffJustStuff conferenceI just spent the last three days at my third <a href="http://www.nofluffjuststuff.com/index.jsp">NoFluffJustStuff </a>conference -- the <a href="http://www.nofluffjuststuff.com/show_view.jsp?showId=17">Research Triangle Software Symposium</a>. My brain is full and I am wiped and extremely invigorated!! It is my annual shot-in-the-arm booster that makes me look forward to going to work! Here are some of <span style="font-style:italic;">my</span> reasons why you shouldn't miss the next opportunity. (Of course you can also see <span style="font-style:italic;">their</span> <a href="http://www.nofluffjuststuff.com/top_ten.jsp">top-10 reasons</a>):<br /><br /><span style="font-weight:bold;"><u>The speakers:</u></span> The speakers are uniformly exceptional. They not only know what they are talking about, they <em>want</em> to be on the stage and are good at it. Further, they don't mind answering questions -- and not just during the session. Speakers are routinely found mingling with the attendees at lunch and breaks. <br /><br /><span style="font-weight:bold;"><u>Constant innovations:</u></span> In the three years that I've been attending these conferences, it has constantly been evolving, never leaving well enough alone. First it was end-of-day keynotes, then it was the introduction of .Net topics, then it was BOFs, <a href="http://www.nofluffjuststuff.com/blogs.jsp">web-site with speaker blogs</a>, etc etc.<br /><br /><span style="font-weight:bold;"><u>Timely topics:</u></span>At any given time there are invariably 5 active sessions. The topics being discussed at these session are not only of high quality, they are timely. Let's take the latest conference. There were sessions on AJAX, Tapestry, Spring, Hibernate, Ruby, Rails, the list goes on.<br /><br /><span style="font-weight:bold;"><u>Doesn't cut corners:</u> </span>The high quality doesn't stop with the speakers and the sessions, it includes the other amenities. You get all the session materials on CD, with binders, in a nice laptop carrying case (another innovation for this year). Meals (read: breakfasts, lunches, snacks and opening night dinner) during the conference are provided. Jay Zimmerman (the organizer) could very easily have just thrown in the "continental breakfast" fare. But meals here would put most restaurants to shame. There is of course the obligatory raffle during the conference. Even there this one outshines. There are <span style="font-style:italic;">two </span>raffles. At the recent conference, up for grabs were books, an iPod and the new Sony PSP.<br /><br />I think there are about 28 events over the course of the year at different locations across the US. So, if you are in the country, chances are, there is one in your neighborhood. Don't miss it.Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com4tag:blogger.com,1999:blog-8090475.post-1115872099014154712005-05-12T00:20:00.000-04:002008-11-17T23:20:30.665-05:00Freeware installer recommendation?I have been using <a href="http://nsis.sourceforge.net/">Null Soft's product</a> and frankly, I just don't get the NSI scripting language. It just seems too arcane and doesn't seem to flow right -- if you know what I mean. I spend more time digging through the help docs and Googling to figure out how to get something going. <br /><br />I would welcome any alternative tool recommendation -- freeware of, course :) My target environment is Windows and so the tool doesn't have to be cross-platform capable.Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com2tag:blogger.com,1999:blog-8090475.post-1115737661015095922005-05-10T10:57:00.000-04:002008-11-17T23:17:08.532-05:00Visualizing Web Logic's domain configuration<span style="font-size:85%;">Web Logic's config.xml is the center of WL universe. This is what WL uses to keep track of all servers, deployments, services etc and is one gnarly XML file. Yes it isn't for human consumption but it helps to be able to read it when diagnosing problems. Now you have <a href="http://dev2dev.bea.com/blog/euxx/archive/2005/04/visualize_weblo.html" title="Eugene Kuleshov's blog" target="_blank" _base_href="http://d10616.na.sas.com/roller">a handy-dandy XSL</a> stylesheet that'll present this file in HTML. Element names are hyperlinks. Following these links takes you to the appropriate reference documentation page. Now, why didn't I think of that? This works with IE and Firefox although with the latter it doesn't work for a <code>file://</code> URL. Any ideas why?<p/>Attributes: I can't place the actual blog where I came across this tip. So thank you, anonymous.</span>Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com0tag:blogger.com,1999:blog-8090475.post-1114030737179286972005-04-20T16:52:00.000-04:002008-11-17T23:17:08.533-05:00StrutsTestCase and TokensHow does one get around the usage of Struts tokens when unit testing using <a href="http://strutstestcase.sf.net">StrutsTestCase</a>? If the action class under tests for duplicate submissions using tokens,<br /><br /><blockquote style="font-family: courier new;">if(isTokenValid(request) == false) {<br /> // don't perform the action and route the user<br /> // to an appropriate location<br />}<br /></blockquote><br />it'll always fail. This is because, the token is set in the request by the <span style="font-family: courier new;"><html:form></span> tag and of course when running a StrutsTestCase, the JSP isn't involved.<br /><br />The application functions correctly, however, I cannot run my unit tests :(Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com1tag:blogger.com,1999:blog-8090475.post-1112908851560379712005-04-07T17:03:00.000-04:002008-11-17T23:17:08.533-05:00Clustering mutable objects<div> <div><span class="778303622-29032005"><span style=";font-family:Arial;font-size:85%;" ><span style=";font-family:georgia;font-size:100%;" >One of things a clusterable application server (for example, Web Logic) does for an application is to broadcast objects placed in the user's session. This happens (at least with Web Logic) whenever the application invokes the </span><span style=";font-family:Courier New;font-size:100%;" >setAttribute</span><span style=";font-family:georgia;font-size:100%;" ><span style="font-family:courier new;"> </span>method of the </span><span style=";font-family:Courier New;font-size:100%;" >HttpSession</span><span style=";font-family:georgia;font-size:100%;" ><span style="font-family:courier new;"> </span>interface. This has a subtle impact when the thing being placed in the session is a mutable object. To explain, first consider a simple (non-mutable) example</span><br /><br /></span></span></div> <div><span class="778303622-29032005"></span> </div> <div><span class="778303622-29032005" style="font-size:100%;"><span style="font-family:Courier New;"> 1: String name = "John";</span></span></div> <div><span class="778303622-29032005"><span style=";font-family:Courier New;font-size:85%;" ><span style="font-size:100%;"> 2: session.setAttribute("username", name);</span><br /><br /></span></span></div> <div><span class="778303622-29032005"></span> </div> <div><span class="778303622-29032005"><span style=";font-family:Arial;font-size:85%;" ><span style="font-family:georgia;">The application server will serialize the value associated with the key </span><span style=";font-family:Courier New;font-size:100%;" >username</span><span style="font-family:georgia;"><span style=";font-family:courier new;font-size:100%;" > </span>to all the nodes in the cluster. Now suppose that the node executing the above steps turns around and changes the value of </span><span style="font-family:Courier New;">name</span><span style="font-family:georgia;">. IOW</span><br /><span style="font-size:100%;"><br /> </span></span></span></div> <div style="font-family: courier new;"><span class="778303622-29032005" style="font-size:100%;"></span> </div> <div><span class="778303622-29032005"><span style=";font-family:Arial;font-size:85%;" > <div style="font-family: courier new;font-family:courier new;" ><span class="778303622-29032005" style="font-size:100%;"><span style=""> 1: String name = "John";</span></span></div> <div style="font-family: courier new;font-family:courier new;" ><span class="778303622-29032005" style="font-size:100%;"><span style=""> 2: session.setAttribute("username", name);</span></span></div> <div style="font-family: courier new;font-family:courier new;" ><span class="778303622-29032005" style="font-size:100%;"> ..</span></div> <div style="font-family: courier new;font-family:courier new;" ><span class="778303622-29032005" style="font-size:100%;">15: name = "Mary";</span></div> <div><span class="778303622-29032005"><span style="font-family:Courier New;"><span style=";font-family:courier new;font-size:100%;" >16: String sessionValue = (String)session.getAttribute("username");<br /></span><br /></span></span></div></span></span></div> <div><span class="778303622-29032005"></span> </div> <div><span class="778303622-29032005"><span style=";font-family:Arial;font-size:85%;" ><span style="font-family:georgia;">Since Java passes a copy of a reference to a method call, </span><span style=";font-family:Courier New;font-size:100%;" >sessionValue</span><span style="font-family:georgia;"><span style="font-size:100%;"><span style="font-family:courier new;"> </span></span>is guaranteed to be </span><span style=";font-family:Courier New;font-size:100%;" >John</span><span style="font-family:Arial;"><span style="font-size:100%;"><span style="font-family:courier new;"> </span></span>-- on all the nodes</span><span style="font-family:georgia;">. No harm. No foul.</span><br /><br /><span style="font-family:georgia;">Now consider placing a </span><em style="font-family: georgia;">mutable </em><span style="font-family:georgia;">object such as a<span style=";font-family:courier new;font-size:100%;" > </span></span><span style=";font-family:Courier New;font-size:100%;" >java.util.List</span><span style="font-family:georgia;"> in the session.</span><br /><br /></span></span></div> <div><span class="778303622-29032005"></span> </div> <div style="font-family:courier new;"><span class="778303622-29032005" style="font-size:100%;"><span style=""> 1: List cart = new ArrayList();</span></span></div> <div><span class="778303622-29032005"><span style=";font-family:Courier New;font-size:85%;" ><span style="font-size:100%;"><span style="font-family:courier new;"> 2: session.setAttribute("shoppingcart", cart);</span><br /></span><br /></span></span></div> <div><span class="778303622-29032005"></span> </div> <div><span class="778303622-29032005"><span style=";font-family:Arial;font-size:85%;" ><span style="font-family:georgia;">Just as with the </span><span style=";font-family:Courier New;font-size:100%;" >String</span><span style="font-family:georgia;"><span style="font-size:100%;"><span style="font-family:courier new;"> </span></span>before, this will get propagated to all the nodes in the cluster. Now suppose the cart is updated in one of the nodes:</span><br /><br /></span></span></div> <div><span class="778303622-29032005"></span> </div> <div><span class="778303622-29032005"><span style=";font-family:Arial;font-size:85%;" > <div style="font-family: courier new;font-family:courier new;" ><span class="778303622-29032005" style="font-size:100%;"><span style=""> 1: List cart = new ArrayList();</span></span></div> <div style="font-family: courier new;font-family:courier new;" ><span class="778303622-29032005" style="font-size:100%;"><span style=""> 2: session.setAttribute("shoppingcart", cart);</span></span></div> <div style="font-family: courier new;font-family:courier new;" ><span class="778303622-29032005" style="font-size:100%;"> ..</span></div> <div style="font-family: courier new;font-family:courier new;" ><span class="778303622-29032005" style="font-size:100%;">19: cart.add(aLineItem);</span></div> <div style="font-family: courier new;font-family:courier new;" ><span class="778303622-29032005" style="font-size:100%;"> ..</span></div> <div style="font-family: courier new;font-family:courier new;" ><span class="778303622-29032005" style="font-size:100%;"> // More business logic</span></div> <div style="font-family: courier new;font-family:courier new;" ><span class="778303622-29032005" style="font-size:100%;">25: List sessionValue = (List)session.getAttribute("shoppingcart");</span></div> <div><span class="778303622-29032005"><span style="font-family:Courier New;"><span style="font-family: courier new;font-size:100%;" >26: System.out.println("Cart size = " + sessionValue.size());<br /></span><br /></span></span></div></span></span></div> <div><span class="778303622-29032005"></span> </div> <div style="font-family:georgia;"><span class="778303622-29032005"><span style="font-size:85%;">Now, line 26 above will display different values depending on the node on which it was executed. The node which added the item to the cart will display <span style="font-size:100%;"><span style="font-family:courier new;">Cart size = 1 </span></span>while the others will display <span style="font-size:100%;"><span style="font-family:courier new;">Cart size = 0</span></span>.<br /><br /></span></span></div> <div style="font-family:georgia;"><span class="778303622-29032005"></span> </div> <div><span style=";font-family:arial;font-size:85%;" ><span style=";font-family:Arial;font-size:85%;" >This is bad! This can be avoided if we place an item in the session as the <strong>last step<em> </em></strong>of responding to a user request.</span><span style="font-family:georgia;"> Application development and application deployment are -- in large part -- independent activities. This is an instance where the developer needs to be aware that (s)he is developing an application that's going to be deployed to a clustered environment.</span><br /></span></div></div>Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com0tag:blogger.com,1999:blog-8090475.post-1110213393125114382005-03-07T11:33:00.000-05:002008-11-17T23:17:08.534-05:00Jira's service -- Legendary or just a legend<span style="color: rgb(0, 153, 0);">[Updated: See end of posting]</span><br />Recently, we came across some critical problems with our <a href="http://www.atlassian.com/software/jira">Jira</a> installation. When we couldn't figure out/fix the problem ourselves, I posted a support request to <a href="http://support.atlassian.com/">Jira's support site</a>. In response, I received a request for additional information. Since the request involved a fairly sizeable attachment I wrote back to clarify the request. Didn't get a response. I decided to send the attachment anyway and updated the support request with the required information and waited for a response. That was 4 days ago...The silence is deafening.<br /><br />When you stick your neck out so far and claim "<a href="http://atlassian.com/about/legendaryservice.jsp">Legendary services</a>"....well, you have to deliver. Atlassian's web-site quotes Jeff Bezos<br /><br /><span style="font-style: italic;">"If you have an unhappy customer on the Internet, he doesn't tell his six friends, he tells his 6,000 friends"</span><br /><br />The trouble is, there is no apparent way to escalate an issue. I couldn't find a phone number to call or any other email addresses -- other than for sales. I finally located <a href="http://atlassian.com/about/contactform.jsp">a contact page</a>. I filled out the form stressing the urgency of our problem and asking about an escalation process. Still not a peep.<br /><br />We were considering purchasing <a href="http://www.atlassian.com/software/confluence">Confluence</a>. The one reservation that I had thus far was the lack of coherent documentation. I am now wondering if going for Confluence would be wise given the trouble I'm having with Jira; which, btw has excellent documentation.<br /><br />I realize the irony of using <a href="http://www.javablogs.com/">javablogs</a> to publish my gripe...<br /><br /><span style="color: rgb(0, 153, 0);">[Update: Mar 9 2005 2300]</span><br />Since originally posting the above, I <span style="font-style: italic;">have</span> heard back from Atlassian. I hope the timing was just a coincidence.<br /><br />The problems <span style="font-style: italic;">are </span>being handled.Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com3tag:blogger.com,1999:blog-8090475.post-1106713883422516252005-01-25T23:26:00.000-05:002008-11-17T23:17:08.535-05:00Struts and checkboxesThe following is a reminder to those working on implementing checkboxes on their pages.
<br /><p/>
<br />It is <span style="font-style:italic;">essential</span> that you implement the <code>ActionForm</code>'s <code>reset()</code> method. In this method, you must set the fields that populate the checkboxes to false. The reason is that an HTTP request only includes values for selected checkboxes. Any de-selected box will not be a part of the request and so the server-side will be none the wiser that a user un-checked a box.
<br />
<br />An important side note (one that cost me quite a while): The <code>reset()</code> method is overloaded with the following variants
<br /><pre>
<br />void reset(ActionMapping mapping, ServletRequest request)
<br /></pre>
<br />and
<br /><pre>
<br />void reset(ActionMapping mapping, HttpServletRequest request)
<br /></pre>
<br />
<br />For a web UI, you need to implement the latter. If you use your IDE's intellisense, it is easy to accidentally pick the former since it shows up first in the list of choices.Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com0tag:blogger.com,1999:blog-8090475.post-1099069030063434072004-10-29T12:57:00.000-04:002008-11-17T23:21:03.299-05:00President Edwards? A mathematical possibilityStephen Marmon's <a href="http://www.nytimes.com/2004/10/29/opinion/29marmon.html?oref=login&ex=1256788800&#38;en=a55cf721a1a0a0f8&#38;ei=5088&#38;partner=rssnyt">article</a> paints the scenario in which we could have John Edwards in the Oval office come Jan 2005. Interesting, if not anything.Sri Sankaranhttp://www.blogger.com/profile/00178974793936817176noreply@blogger.com2