<meta name='google-adsense-platform-account' content='ca-host-pub-1556223355139109'/> <meta name='google-adsense-platform-domain' content='blogspot.com'/> <!-- data-ad-client=ca-pub-4320963827702032 --> <!-- --><style type="text/css">@import url(https://www.blogger.com/static/v1/v-css/navbar/3334278262-classic.css); div.b-mobile {display:none;} </style> </head><body><script type="text/javascript"> function setAttributeOnload(object, attribute, val) { if(window.addEventListener) { window.addEventListener('load', function(){ object[attribute] = val; }, false); } else { window.attachEvent('onload', function(){ object[attribute] = val; }); } } </script> <div id="navbar-iframe-container"></div> <script type="text/javascript" src="https://apis.google.com/js/platform.js"></script> <script type="text/javascript"> gapi.load("gapi.iframes:gapi.iframes.style.bubble", function() { if (gapi.iframes && gapi.iframes.getContext) { gapi.iframes.getContext().openChild({ url: 'https://www.blogger.com/navbar.g?targetBlogID\x3d7256432\x26blogName\x3dThe+Frustrated+Programmer\x26publishMode\x3dPUBLISH_MODE_BLOGSPOT\x26navbarType\x3dBLACK\x26layoutType\x3dCLASSIC\x26searchRoot\x3dhttps://frustratedprogrammer.blogspot.com/search\x26blogLocale\x3den_US\x26v\x3d2\x26homepageUrl\x3dhttp://frustratedprogrammer.blogspot.com/\x26vt\x3d4213664491834773269', where: document.getElementById("navbar-iframe-container"), id: "navbar-iframe", messageHandlersFilter: gapi.iframes.CROSS_ORIGIN_IFRAMES_FILTER, messageHandlers: { 'blogger-ping': function() {} } }); } }); </script>
| Friday, July 30, 2004

So picture it, as a last resort you've taken a job for your local spam house. Your task is to harvest email addresses. They need a programmer to come up with some new ways to populate their database of known addreses. What is the first thing you do?

I'd go after:

Does anyone have any proof any of these techniques cut down on spam? The JavaScript solution is particularly bad since 10% of users *still* have JavaScript turned off.

So what would I do, I think the only real solution is something that can't be automated yet is still decipherable for a human. In addition anyone using a screen reader should also have no problem figuring out the address.

Using the same techniques developed by major consumer registration sites, an image containing your email address with the appropriate level of bluriness and scratchiness. Additionally, when you click on the email address, a sound file describing your email address should be played. An alt tag should be used on the image to instruct visually impared of this behavior. It should look something like this:

<a href="email.mp3"><img src="email.png"
alt="Please click to hear my email address." /></a>

Is anyone doing this? Are there any other improvements? As for me, I still like the partial anonymity of this site. My email address is out there somewhere associated with this site but its not its not easy to find.

| Tuesday, July 27, 2004

In a recent post by Jason Bell, he asks for some help with a regex for validating email addresses. A commenter suggested this:


^[a-zA-Z0-9][_\\-\\.\\w]*@[\\w\\.\\-]+\\.[a-zA-Z]{2,4}$


If it makes sense I'm going to break it down into its pieces:
1) [a-zA-Z0-9] - Match 1 letter or number
2) [_\\-\\.\\w]* - Match 0 or more of: _ - . or a letter or number
3) @ - The @ character
4) [\\w\\.\\-]+ - Match 1 or more of: - . or a letter or number
5) \\. - The . character
6) [a-zA-Z]{2,4} Match between 2 and 4 of any letter

#2 matches valid username characters and #1 ensures they only start with letters or numbers. This is ok, except you will match usernames like 'a----.....___'

In the hostname, #4 suffers from the same problem as #2 (the begining of your hostname can begin with '....-----'). #5 requires that a hostname contains a '.' character. Didn't your orginal question ask about not requiring this? And finally #6 ensures your hostname ends in 2 to 4 letters? I think #5 and #6 were supposed to have ()'s around them to group them together. It would be nice to standardize on either [a-zA-Z0-9] or \w too.

To fix things up I'd trim #1 to just \w since its equivelant. Change #4 to [\w-]* and combine #5 and #6 into (\.[\w-]*){0,3}.

If you've read this far, you've come to the point. Although I love regular expressions, I been feeling more and more lately that maybe they are getting over used. Unless you have a solid understanding of finate state automata, your just programming them by trial and error. Tools and web pages like this can help, but I'm still left concerned about the maintenance of regex related code by non-regex loving peers. Its similar to my thoughts on AOP. The average team has average programmers on it. If Java programmers that maintain their own blogs are having trouble with regex, where does that leave average and below average programmers?

| Friday, July 23, 2004

It not pretty, but in large web applications, you need to pass around data in the URL from time to time. Usually it has to do with passing something about the state of the application. Its either this or put it in the session. I don't like the session because:
  • Everything you put in the session can bog down performance if you need to replicate your session for failover.
  • Its too hard to keep track of whats in the session. Users can jump around your application and easily jumble things up.
Putting things in the URL solves both these problems. But as of today in Struts, your still stuck without an easy way to do this. Struts wants you to define static URLs in your forwards via struts-config.xml. But if you want to modify them dynamically in your Action, your left writing the same code everyone on the planet writes. Here's an example, it was written by Jan Sorensen, I found it on the struts mailing list.


/*
* ParameterizedActionForward.java
*
* Created on August 3, 2001, 11:45 AM
*/

package org.apache.struts.action;

import java.net.URLEncoder;

/**
*
* @author Jan Soresen
* @version
*/
public class ParameterizedActionForward extends ActionForward {

/** Create a new ParameterizedActionForward,
* populated from an ActionForward
* @param forward Populate the receiver from this ActionForward
*/
public ParameterizedActionForward(ActionForward forward) {
setPath(forward.getPath());
setRedirect(forward.getRedirect());
firstParameter = getPath().indexOf('?') == -1;
}

/** Create a new ParameterizedActionForward,
* based on an ActionForward found in an ActionMapping
* @param mapping The ActionMapping to get the base forward from
* @param forwardName The ActionMapping to get the base forward from
*/
public ParameterizedActionForward(ActionMapping mapping, String forwardName) {
this(mapping.findForward(forwardName));
}

/** Create a new ParameterizedActionForward
* @param forward The base ActionForward
* @param key The key of the parameter
* @param value The value of the parameter
*/
public ParameterizedActionForward(ActionForward forward, String key, String value)
{
this(forward);
addParameter(key, value);
}

/** Create a new ParameterizedActionForward
* @param mapping The ActionMapping to get the base forward from
* @param forwardName The ActionMapping to get the base forward from
* @param key The key of the parameter
* @param value The value of the parameter
*/
public ParameterizedActionForward(ActionMapping mapping, String forwardName,
String key, String value) {
this(mapping, forwardName);
addParameter(key, value);
}

/** Add a new parameter
* @param key The key of the parameter
* @param value The value of the parameter
*/
public void addParameter(String key, String value) {
StringBuffer buffer = new StringBuffer(getPath());
buffer.append(firstParameter ? '?' : '&apm;');
firstParameter = false;
buffer.append(URLEncoder.encode(key));
buffer.append('=');
buffer.append(URLEncoder.encode(value));
setPath(buffer.toString());
}

private boolean firstParameter;


}

With this class you can write code like this:

ParameterizedActionForward forward =
new ParameterizedActionForward( mapping.findForward("updateSuccess"), "foo", "bar" );
return forward;


Its a shame, because there has been a bug submitted for this for over 3 years. It even has a good patch similar to the file above, complete with testcases. Why hasn't it been added to CVS? I'm not sure, but I know how you can help. Exercise your right to vote on this bug!

| Thursday, July 22, 2004

For some reason I'm always tempted to cram as much as I can into a single action definition in the struts-config.xml. Its mostly due to laziness. But I also like grouping related features together like CRUD (create, read, update, delete).

The thing about CRUD is, three of the four pieces (create, update and delete) can produce errors. This conflicts with my goal in two ways:

1 - You can only define validations for the form or the action.

Well, I tried to get around this. You may already know about the builtin 'page' attribute that Struts supports. If you pass 'page' as the name of a hidden parameter with an integer value, it will run all validations for that action/form with the page attribute less than or equal to the value of the 'page' parameter. This doesn't work because multiple validations could get run, when I only want one to get run.

Next I tried to configure validations on a per dispatch-method basis. I overrode my ActionForm's validate method:

public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request) {

ServletContext application = getServlet().getServletContext();
ActionErrors errors = new ActionErrors();

String validationKey = getValidationKey(mapping, request);

String suffix = (String) getMap().get("pageSuffix");
if (!StringUtils.isEmpty(suffix)) {
validationKey += '_' + suffix;

// Remove the suffix from the form in case this is a session scoped object.
getMap().remove("pageSuffix");
}

Validator validator = Resources.initValidator(validationKey,
this, application, request, errors, page);

try {
validatorResults = validator.validate();
} catch (ValidatorException e) {
log.error(e.getMessage(), e);
}

return errors;
}

This ran a validation matching the name of the form with the value of the 'pageSuffix' parameter appended to it. This ran the right validation but didn't work in the end because of reason #2.

2 - If the validation fails, the action can only have one input parameter to tell struts where to go.

This isn't good since delete() and add() will each want to go to a different page if an error occurs during validation. I think the next step is to configure the validator to allow for multiple forwards for the same form or action. Something like this would work, but I never implmented it because it still doesn't feel right.

<form name="fooForm_add" forward="addFailure">
<field property="name" depends="required" >
<arg0 key="label_name"/>
</field>
</form>


Well, if you can improve on it or tell me your preferred solution, I'll praise you in a comment. For now I will use a MappingDispatchAction (see my review) or a DispatchAction with at least two action definitions.

| Monday, July 19, 2004

To be honest, all the web applications I've written so far have always disabled page caching. But even though this application has multiple users that access the same data concurrently, they business owners wanted to turn on page caching. I agree that the back button warning from IE is really annoying, but I let them know some of the issues that go along with caching, and we agreed to give it a try.

After playing around a bit, we figured the behavior we wanted was that any page forward movement (form submit or link click) should get a new page from the server, while the back and forward buttons should use the browser's cache (to avoid the annoying message from the brwoser). There was one catch, they wanted the cache to expire after a set amount of time. I couldn't figure it out. Is there some setting I'm missing? The only way to make the back button cache, and link clicking not cache is to use the browser's detault behavoir. But then the duration of the cache is control usually be disk space, set on the browser.

I constructed the table below to document the behavoir of IE vs. Mozilla with the different caching methods. The three methods I tried were:
-Default Caching (DC)- This is what ever the browser does by default. No special headers were sent.
-Caching Disabled (CD)- The following headers were sent:
  response.setHeader( "Cache-Control", "no-cache" );

response.setDateHeader( "Expires", -1 );
response.setHeader( "Pragma", "No-cache" );

-Some Caching (SC)- Pages are only active for a set amount of time, in this case one minute. The following headers were sent:
  response.setHeader( "Cache-Control", "max-age=" + (1*60) +", must-revalidate" );


Browser/CachingForm PostForm GetLinkBackForwardReload
IE - DC---XXX
IE - CD------
IE - SC--(?)XXXX
Moz - DC---XXX
Moz - CD------
Moz - SC-XXXXX

(X indicated page was retrieved from cache, not the server)
(? Strange but true. IE treats a form GET different than a link GET)

This is the page I used to test things:

<%@ page import="java.util.Date"%>
<%
/*
response.setHeader( "Cache-Control", "no-cache" );
response.setDateHeader( "Expires", -1 );
response.setHeader( "Pragma", "No-cache" );
*/
//response.setHeader( "Cache-Control", "max-age=" + (1*60) +", must-revalidate" );
%>

Time: <%= System.currentTimeMillis() %> <%= new Date()%> <br />
<form taget="/commsales/cache-test.jsp" method="post">
Foo: <input type="text" name="foo" /><br />
<input type="submit" value="Submit Post" />
</form>

<form taget="/commsales/cache-test.jsp" method="get">
Bar: <input type="text" name="bar" /><br />
<input type="submit" value="Submit Get" />
</form>

<a href="/commsales/cache-test.jsp">Link</a>

Ever have a form with multiple submit buttons? Well, if you hit enter in one of the text inputs, browsers will act like the first button on the form was pressed. Unless, of course, the form only has one text input. Then it doesn't matter if you have several other types of inputs or none at all, IE will not send any button related information to the server. Its very consistent about this across all versions.

If you are using struts your error will look something like this.

javax.servlet.ServletException: Request[{0}] does not contain handler parameter named {1}


Or in Struts 1.2.1 you can overcome this by implementing the method unspecified(...) in your action.

But to avoid it all and make one input act just like multiple inputs, just add a 'hidden' text input:

<input type="text" style="display: none" />

This input will not show in any modern browser, and will force IE to act like the first button on the page was clicked when enter is pressed on this form.

Recently I found myself writing for loops that use Iterators a little differently. I used to write loops like this:

Iterator fooIter = bar.getFoos();
Foo foo;
while( fooIter.hasNext() ) {
foo = (Foo)fooIter.next();
[...]
}


And now I've moved to this:

for( Iterator fooIter = bar.getFoos().iterator(); fooIter.hasNext(); ) {
Foo foo = (Foo)fooIter.next();
[...]
}


Its nice and tight, but still readable. But I've always wondered about instantiating a temporary reference inside a loop. I wrote this code to try to tell what effect it has on performance.

I wrote this code:

public class Foo {
private static int SIZE = 999999;

public static void main( String args[] ) {
String strArray[] = new String[SIZE];
for( int i = 0; i < SIZE; i++ ) {
strArray[i] = Long.toString( System.currentTimeMillis() );
}

int count = 0;
long timer = System.currentTimeMillis();
for( int i = 0; i < SIZE; i++ ) {
String str = strArray[i];
count += str.length(); // make sure the loop does something;
}
System.out.println("timer: " + (System.currentTimeMillis() - timer) );

timer = System.currentTimeMillis();
String str;
for( int i = 0; i < SIZE; i++ ) {
str = strArray[i];
count += str.length(); // make sure the loop does something;
}
System.out.println("timer: " + (System.currentTimeMillis() - timer) );
System.out.println("count: " + count );
}
}


Well, the output wasn't too suprising, exactly the same times. I hope my test aren't completely bogus. I'd feel better if I had a definite confirmation of performance either way.

Until I can use Java 1.5 on a project I'll probably stick with this...

| Sunday, July 18, 2004

As noted in my last blog entry, Struts's DispatchAction never really integrated with other pieces of the framework. That entry talked about canceling validation, this entry covers kicking off validation. We'll take a look at the MappingDispatchAction introduced in Struts 1.2.1, but lets first look at the problem it was introduced to fix.

Say we have the following forms:

=========buddyList.jsp========
Buddy List:

[ ] Sally
[ ] Billy
[ ] Pat

[Delete] [Add]

=========buddyAdd.jsp========
Buddy Add:

Name: ___________

[Submit]


Now lets say we want to code the CRUD (w/o update) actions in a DispatchAction:

public class BuddyAction extends DispatchAction {
public ActionForward read( ... ) {
request.setAttribute( "buddies", businessLogic.getBuddies( ... ) );
}

public ActionForward delete( ... ) {
businessLogic.deleteBuddy( ... );
}

public ActionForward create( ... ) {
businessLogic.createBuddy( ... );
}
}


Now we get to the crux of the problem. How do you define the action in your struts-config.xml for this puppy? Most people try to define one action for the whole DispatchAction. The problem is, you can only have one ActionForm defined, one validation definition and one 'input' attribute to tell struts where to go if validation fails. (Note you can have multiple validations defined if you use the 'page' attribute on your validation, but that only applies to "wizards".) So in reality, a Struts action needs to be defined for each HTML form you have. So your struts-config.xml might look like this:

<action path="/buddyList"
type="BuddyAction"
name="BuddyListForm"
scope="request"
input="/buddyList.jsp"
parameter="method">
<forward name="readSuccess" path="/buddyList.jsp"/>
<forward name="deleteSuccess" path="/buddyList.jsp"/>
</action>

<action path="/buddyCreate"
type="BuddyAction"
name="BuddyForm"
scope="request"
input="/buddyCreate.jsp"
parameter="method">
<forward name="createSuccess" path="/buddyList.jsp"/>
</action>


So, this works. aside from the problems I described in this blog entry and your wondering 'Where does MappingDispatchAction come in'? Well, look at the URL you need to use to create a buddy:

<a href="/buddyCreate.do?method=create">Create</a>


If you find it too annoying to repeat yourself like this, you can change your Action to extend MappingDispatchAction and change the struts-config.xml to the following:

<action path="/buddyRead"
type="BuddyAction"
parameter="read">
<forward name="success" path="/buddyList.jsp"/>
</action>

<action path="/buddyDelete"
type="BuddyAction"
name="BuddyListForm"
scope="request"
input="/buddyList.jsp"
parameter="delete">
<forward name="success" path="/buddyList.jsp"/>
</action>

<action path="/buddyCreate"
type="BuddyAction"
name="BuddyForm"
scope="request"
input="/buddyCreate.jsp"
parameter="create">
<forward name="success" path="/buddyList.jsp"/>
</action>


Your new URL is just:

<a href="/buddyCreate.do">Create</a>

This also has the advantage that the action definitions are more specific and only define what is needed for each one. For example, no ActionForm is created for the /buddyRead action. These are all good things, but they come at the price of a larger struts-config.xml. If your using a tool like XDoclet to build your config file, I suppose this wouldn't be an issue, but for the rest of the hand-coding Struts world, they will need to make a choice about which method they prefer.

| Saturday, July 17, 2004

When Struts introduced the DispatchAction, people initially thought it was a great idea, until it started to break down in the real world. One of those points of break down is canceling a form (and bypassing validation). See, Struts normally only looks for the parameter org.apache.struts.actoin.CANCEL to indicate cancellation of a form. But if a form has multiple buttons and uses DispatchAction to manage them. Its not easy to pass a method name AND this special Struts parameter. It can be done with JavaScript, but that needs to be cut and pasted to each file, so its not really an option.


In the past, I used to subclass my ActionForm class and add override the validate() method and skip validation if the cancel button was pressed:


// The struts dispatch action and the validator framework don't work well together. To cancel validation of
// a form, struts noramlly looks for the parameter "org.apache.struts.action.CANCEL", but with a dispatch
// action and a form with multiple buttons, we can't really do this. So I'm just going to look for
// the method of "cancel".
private static final String METHOD_CANCEL = "cancel";

public ActionErrors validate(ActionMapping mapping, javax.servlet.http.HttpServletRequest request)
{
if( request.getParameter( mapping.getParameter() ).equals( METHOD_CANCEL ) ) {
return new ActionErrors();
}
return super.validate( mapping, request );
}


This requires my method to be named cancel, but it seems to work fine.

With Struts 1.2.1 came with this release note:


Cancel handlers - DispatchAction,
LookupDispatchAction, and MappingDispatchAction - Now provide default
cancel handler that can be overridden. It also also possible to specify
the default handler name.



So looking into the JavaDoc they explain it a little more with:


NOTE - If the value of the request parameter is empty,
a method named unspecified is called. The default action is
to throw an exception. If the request was cancelled (a html:cancel
button was pressed), the custom handler cancelled will be used instead.
You can also override the getMethodName method to override the action's
default handler selection.
 

What does this mean? Well, if Struts sees the parameter 'org.apache.struts.action.CANCEL' in DispatchAction or one of its children, it will ignore the value of your parameter and call a method named cancelled(). This is a good thing, since you can use the tag html:cancel with a DispatchAction now.

| Friday, July 16, 2004

Like most build servers, Anthill's current working directory is the directory you kicked off your build server in, not nessesarily the directory your build.xml is located in. So don't use relative paths in your build.xml, always prepend ${basedir} to the begining.

<property name="web.home" value="web/WEB_INF"/>

should be

<property name="web.home" value="${basedir}/web/WEB_INF"/>

Note: Some tasks seem to prepend basedir automatically on to the front of your paths(?), but others don't, which makes this hard to track down.

JSP precompilation is a good thing for a couple reasons:
-Better error checking - Errors can be caught at compile-time rather than run-time.
-Faster - Compiling pages is slow. Compiling pages during your build process means a user will never have to wait for a page to compile.

For our project, speed was one of the main drivers for implmenting precompiling. At our current stage we give a lot of demos and the project owners are forced to go through the entire application before the demo so it looks as fast as possible. This is kinda silly with the tools available, so we tried to fix it.

Tomcat's official documentation includes a detailed explanation of how to setup page precompiling, but it is missing a couple things.
-"Insert the ${webapp.path}/WEB-INF/generated_web.xml at the right place inside the ${webapp.path}/WEB-INF/web.xml file." - This is a little sparse for someone who doesn't know Ant well. This blog entry was a nice help.
-Precompiling has a disadvantage, if you change the JSP it won't automatically recompile the page. This isn't ideal for development so we need to make it conditional.


<target name="jspc">
<taskdef classname="org.apache.jasper.JspC" name="jasper2">
<classpath refid="compile.classpath">
</classpath>

<mkdir dir="${gen.path}/jspc">
<jasper2>
validateXml="false"
uriroot="${webapp.path}"
webXmlFragment="${webapp.path}/WEB-INF/generated_web.xml"
outputDir="${gen.path}/jspc" />
<loadfile propery="file" srcfile="${webapp.path}/WEB-INF/generated_web.xml">
<copy file="${webapp.path}/WEB-INF/web_tmpl.xml"
tofile="${webapp.path}/WEB-INF/web.xml" filtering="true" overwrite="true"/>
</target>

<target name="compile">
<mkdir dir="${webapp.path}/WEB-INF/classes">
<javac destdir="${src.path}:${gen.path}/jspc">
optimize="off"
debug="on" failonerror="false"
srcdir="${webapp.path}/WEB-INF/src"
classpathrefid="compile.classpath"
excludes="**/*.smap">
</javac>

<!-- Since we're not doing a page recompile at this time, lets just copy the web_templ.xml to web.xml. -->
<filter token="genarated_web.xml" value=""/>
<copy file="${webapp.path}/WEB-INF/web_tmpl.xml"
tofile="${webapp.path}/WEB-INF/web.xml" filtering="true" overwrite="true"/>
</target>

<target name="dist" depends="compile,jspc"/>


Things to note:
-You first need to copy web.xml to web_templ.xml and insert @generated_web.xml@ where you want the servlet definitions to go. You also need to add web_templ.xml to CVS and remove web.xml from CVS.
-During a normal development compile, no JSP compilation will occur and the web_templ.xml will just be copied over to web.xml.
-During a 'dist' build, page compiling will occur and the generated_web.xml from Jasper will be included in your web.xml.
-I copied the jasper related jars from CATALINA_HOME/common/lib to lib/build. This directory is included at compile time ONLY. It is not copied into my war for deployment. (It also contains things like the servlet api jar.)
-In the begining I still had the servlet 2.3 api jars in my lib/build directory. This caused the following error:
java.lang.NoSuchMethodError:

javax.servlet.jsp.tagext.TagAttributeInfo.(Ljava/lang/String;ZLjava/lang/String;ZZ)V
This email helped me track it down.

Next Step: Can you use these pages in weblogic by including the jasper-runtime.jar in WEB-INF/lib?

"The app is slow in QA, can you take a look?"

"Sure..."

-It turns out it takes 10 seconds to load one key page in the system.
-Telnet to the box, and look at the load average, vmstat and io stat - Everything is ok.
-Telnet to the database, do the same - Everything is ok.
-Turn on P6Spy in the hibernate.proeprties
-Look at the log files - 10 queries are taking over .5 seconds. Most are related to one table.
-It turns out the index for for the primary key of table is missing (huh?). Add it and reload the page, its down to 5 seconds.
-Look at the spy.log again and 4 queries are taking over .5 seconds. Notice some foriegn key relations don't have indexes. (Explain plan could of pinpointed things better, but most of this was easy to see w/o it).
-To see how wide spread this is, I found this excellent script:

select c.OWNER,
c.TABLE_NAME,
c.CONSTRAINT_NAME,
cc.COLUMN_NAME,
r.TABLE_NAME,
rc.COLUMN_NAME,
cc.POSITION
from dba_constraints c,
dba_constraints r,
dba_cons_columns cc,
dba_cons_columns rc
where c.CONSTRAINT_TYPE = 'R'
and c.OWNER not in ('SYS','SYSTEM')
and c.R_OWNER = r.OWNER
and c.R_CONSTRAINT_NAME = r.CONSTRAINT_NAME
and c.CONSTRAINT_NAME = cc.CONSTRAINT_NAME
and c.OWNER = cc.OWNER
and r.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
and r.OWNER = rc.OWNER
and cc.POSITION = rc.POSITION
order by c.OWNER, c.TABLE_NAME, c.CONSTRAINT_NAME, cc.POSITION


Adding more missing constraints and the app is blazingly fast again. Babu, I am in your debt.

This is a rehash of ideas from the original extreme programming book, but the question came up again today at work.

A new engineer at the company sent out a mail asking people which tools they prefer and why. Most people responded with Visio since its easy. Personally, my preferences is GML. For small designs (which is 90% of the time) I just want enough to get feedback from my peers, nothing more. Once I start coding, I consider a design document dead (although I might revive it if there is a complicated section of code that needs reviewing, but this rarely happens.) Sure, larger architecture-type documents should be done using more advanced tools since their life span will be longer (6 months to a year?). But even for these larger documents, Visio is plenty for me. This is because mostly because I'm not designing classes, just abstract concepts.

Well, the new engineer is persistent. He now says that he needs one of the mid-range (~$1000) tools to work on this new project and asks for feedback. He explains how he needs to use the tool to do round-trip engineering for the whole life-cycle of the project. Maybe the tools will be there one day, but I haven't seen it work on a medium to large sized project.

| Wednesday, July 14, 2004

I've been using FastBuzz for a few months now. It had almost everything I wanted, but now its now longer giving me new content. I'm pretty bummed, so I'm searching for a new tool.
Update: Fastbuzz is working again! It still has its short comings, see below, but its still better than the competition.
Update: I mailed FastBuzz about this matrix and got a good response including "Thank you for the competition matrix -- we definitely mean to deal with ATOM feeds, so no worries there."

I got my list of sites from google searching, DMOZ and Yahoo.

Aggregator requirements:
-I need to read news from home (2 different computers) and work. So aggregator most likely needs to be web based.
-Track read news at the article level. Only show me my unread news.
-Atom support
-OMPL import/export
-Clean interface. I read a lot of sites, and I need to see a very concise view of things. This usually involves at least 2 panes. Some sites try to provide a 'news paper' type feel. This sort of interface may work for a small number of feeds, but it doesn't scale for a large amount.
-Interface with technorati to see who else is commenting on this news item.
-Sounds obvious, but I need to be able to add a feed from *any* site.
Site MC Track Sites Track Articles Atom OMPL Clean Technorati Any Feed
FastBuzz X X X X X X
LiveJournal X ? ? X
Kinja X X X
Bloglines X X X X X X
BlogsReader (Didn't work in Mozilla) X X X
Findory Blogory X X
fyuze X X X
Journster X X
fyuze X X X
My Yahoo! X X
NewsIsFree X ? X ?
Sage X X ? X X X

| Tuesday, July 13, 2004

-Untyped - In all the Aspect languages I've seen, when you ccode and compile your aspect, there is no type checking to ensure the generated code will run. Strong type checking is one of my favorite pieces of Java, I don't like the idea of loosing this.
-Hard to test - How can you unit test an aspect in isolation? If I can't test it in isolation, I have to system test it. This opens it up for more regressions.
-"Complicated" - The average developer just doesn't get it. Most teams have average and below team members on them. This will be code they will not be able to touch. Every project has these portions, but obviously keeping them to a minimum is going to make less work for you in the long run.
-AOP is also an acronym for Attribute Oriented Programming -- Ala XDoclet, JSR 175 - Annotations

Well, I do think they still have a place on a project, but should be used sparingly. Not just because it makes things easier. Good reasons for using AOP that I've seen:
-Declarative transactions in Spring
-I heard of an aspect that could exceptions at the Facade level of a J2EE application. After it caught the exception it generated a new JUnit test case that could reproduce the exception by storing the arguments passed to the Facade.

What's your favorite aspect?

| Thursday, July 08, 2004

If you have a form with multiple buttons on it, when your action is invoked you need to find out which button was pressed. Usually this is done by each button passing the same parameter with a different value.

Something like this:

<html:form action="/foo.do">
[...]
<html:submit name="method" value="Add">
<html:submit name="method" value="Delete">
</html:form>


You could use a series of if/else statements to perform the correct logic based on the 'method' parameter, or you could extend DispatchAction and set the parameter attribtue to 'method'. Using a DispatchAction will use reflection to call a method with the name as the value of the 'method' parameter.

Using this solution still has a drawbacks:
-The value attribue of the html:submit tag is the method, exactly. If you need to change the name of the button for any visual reason (spelling, usablility, etc.) the method must change too.
-If your application supports multiple languages, this can't be used since it has to be an exactly one-to-one match.

The official party-line is to use a LookupDispatchAction, for more detailed information read Ted Husted's tip. Basically a Map is populated which links the name of the button to the method that should be called when the button is pressed. While this works fine, it's a little overkill to me.

Its a bit of a hack, but I prefer the following code. It encodes the name of the method to be called in the name of the submit button. The value of the submit button is only used for display purposes, nothing else.


<html:form action="/foo.do">
[...]
<html:submit name="method_add" value="Add">
<html:submit name="method_delete" value="Delete">
</html:form>


To make this work, your code that normally extends DispatchAction now needs to extend SimpleLookupDispatchAction, defined below:

public class SimpleLookupDispatchAction extends DispatchAction {
protected getMethodName(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String methodName) {
// First try to get the method the traditional way.
Method method = getMethod( methodName );

// If its not found, now try to find it based on the parameter name.
if( method == null ) {
methodName = getMethodName( request, mapping );
if( methodName != null ) {
return super.dispatchMethod(mapping, form, request, response, methodName);
}
throw new Exception("Unable to find parameter in post data: " + prefix);
} else {
return super.dispatchMethod(mapping, form, request, response, name);
}
}

/**
* Get the method name by using the SimpleLookupDispatchAction technique.
*/
public Method getMethod( HttpServletRequest request, Mapping mapping ) {
// loop through each parameter and try to find one starting with
String prefix = mapping.getParameter();
for( Enumeration e; request.getParameterNames(); e.hasMoreElements(); ) {
String paramName = (String) e.nextElement();
// The paramName should look like this: 'parameter_value.x', we need just 'value'
if (paramName.startsWith(prefix + "_")) {
String paramSplit = paramName.split("_");
String value = paramSplit[1];
// This last split is used to trim off the .x or .y if this was an image button.
methodName = value.split( "\\.")[0];
return methodName;
}
return null;
}
}


This is a lot less work, and I haven't had any problems with it yet...

Problems like this are common in opensource projects because they want to work in as many JDK version as possible, but want to use decent APIs. I've also been on two projects now where we've had to implement a wrapper to simulate the JDK1.4 Regex APIs in JDK1.3 using the Jakarta regex package. This is slowly becoming a non-issue as people migrate past JDK1.3, but has anyone seen an opensource project that does this already?

Speaking of backporting, here's a couple links to backporting features from 1.5:
-http://retroweaver.sourceforge.net/index.html
-http://www.amberarcher.org/projects/panno/

| Wednesday, July 07, 2004

I was recently tasked with adding authentication to our application. We had a database filled with users and passwords, but the old middleware vendor we were using was reluctant to give us the algorithm used to encrypt the passwords. Well, they did tell us it was DES, but refused to give us the key, or the ascii encoding algorithm used to store the binary data as a string in the database.

This is really the heart of the problem with using DES for passwords. Once you decrypt one password, you have them all. This is why most other password encoding schemes involve a one-way encryption. With one-way encryption, the encrypted password in the database cannot be decrypted. Instead, the password is re-encrypted and compared to the encrypted version in the database. Java's JCE package provides MessageDigest. It was very easy to use:

public boolean passwordMatches( String passwordFromUser, String passwordFromDatabase ) {
MessageDigest digester = MessageDigest.getInstance("MD5");
String digestedPassword = convert( digester.digest( passwordFromUser.getBytes() ) );
return digestedPassword.equals( passwordFromDatabase );
}

[...]

// These two methods are copied from the jakarta-tomcat project's HexUtil

/**
* Convert a byte array into a printable format containing a
* String of hexadecimal digit characters (two per byte).
*
* @param bytes Byte array representation
*/
public static String convert(byte bytes[]) {

StringBuffer buf = new StringBuffer(bytes.length * 2);
for (int i = 0; i < bytes.length; i++) {
buf.append(convertDigit(bytes[i] >> 4));
buf.append(convertDigit(bytes[i] & 0x0f));
}
return (buf.toString());
}

/**
* [Private] Convert the specified value (0 .. 15) to the corresponding
* hexadecimal digit.
*
* @param value Value to be converted
*/
private static char convertDigit(int value) {
value &= 0x0f;
if (value >= 10) {
return ((char) (value - 10 + 'a'));
} else {
return ((char) (value + '0'));
}
}


The next question is, how much more secure is this than just storing plain-text passwords in the database?

Arguements for not using plain-text passwords in the database:
-If someone cracks your database, they have direct access to all your passwords.
Well, I always figured if they cracked your database, its not much more work to find your source code, or find your binaries and decompile them. This give them access to your encryption algorithm, which gets them pretty close to cracking most of your passwords.
-If someone uses your database for reporting or something similar, they will have direct access to your passwords.
This can be solved with using decent access permissions in your database. Reporting users should just have read-only access to the tables they need to do reporting.

Do I think passwords should be plain-text in the database? No, not really. But I think there's something better out there. I'm sure someone smart has come up with a system of public/private key encryption for web applications. Why don't browsers help support something like this by already generating private keys for people?

| Tuesday, July 06, 2004

http://www.w3schools.com/browsers/browsers_stats.asp

This is great news for web standards. Maybe by this time next year, we'll actually have some browser wars again. Well, some crude (Excel trend lines) analysis says that it Mozilla won't overtake IE until March 2006. But, competition is, of course, a great thing. My only worry is will it be too late with technologies like XAML gaining steam.

Any takers from the 0 people who read this blog?

Toad has a nice tool, but Toad is of course expensive. I have a shell script I hacked together a while back to solve this problem. Its oracle specific, but could be easily modified to work on another database.

In a nutshell, it talks to sqlplus to get a list of tables, dumps the schema ('desc' output) to a file. It does this for each database, then just uses 'diff' to diff the files.


#!/bin/bash -x

if [ $# -ne 2 ]; then
echo "Usage: $0 [SCHEMA1] [SCHEMA2]"
exit 1;
fi

SCHEMA1=$1
SCHEMA2=$2

echo "set heading off;" > /tmp/tables.sql
echo "select table_name from tabs;" >> /tmp/tables.sql
echo "quit;" >> /tmp/tables.sql

function dump() {
mkdir -p /tmp/$1/tables

sqlplus -S $2 @/tmp/tables.sql | grep -v "rows selected" | sort -u >
/tmp/$1/tables.out

for table in `cat /tmp/$1/tables.out`; do
#echo $table
echo "set heading off;" > /tmp/$1/tables/$table.sql
echo "desc $table;" >> /tmp/$1/tables/$table.sql
echo "quit;" >> /tmp/$1/tables/$table.sql
sqlplus -S $2 @/tmp/$1/tables/$table.sql | grep -v "rows selected" |
grep -v " Name" | grep -v -e "-----" | sort > /tmp/$1/tables/$table.out
done
}

dump one $SCHEMA1
dump two $SCHEMA2

diff -r /tmp/one /tmp/two

| Friday, July 02, 2004

Good Java developers have done a great job of keep their skills up with respect to
-Java APIs
-Design Patterns
-3rd Party Applications / Tools

But man.. their UI skills are week. Most applications are still developing without CSS, with HTML instead of XHTML. This leads to sites that are less accessable and hard to render consistently across multiple browsers (amoung other things). If this is old news to you, great. Please help spread the word.

Here is a great view into the future. Try this w/o CSS:

http://www.aplus.co.yu/dots/109/

or

http://www.csszengarden.com/?cssfile=/106/106.css&page=0

How can you learn more?

http://www.456bereastreet.com/lab/developing_with_web_standards/

Are you kidding me?!?!?! What a waste of an hour!

Man.. that is so lame.. whats worse is the contructor for JspException takes a cause, but assigns it to rootCause, and doesn't pass it to its parent class, so your left thinking your doing the right thing..grrr

Here is the code in my error.jsp to display the exception.


ByteArrayOutputStream ostr = new ByteArrayOutputStream();
exception.printStackTrace(new PrintStream(ostr));
out.print(ostr);
ostr.reset();

if( exception instanceof JspException ) {
Throwable rootCause = ((JspException)exception).getRootCause();
if( rootCause != null ) {
out.println("");
out.println("Root cause:");
rootCause.printStackTrace(new PrintStream(ostr));
out.print(ostr);
}
}


Here is a link to a more detailed solution (except it doesn't mention the above problem.
http://jroller.com/page/prpatel?anchor=handling_the_three_kinds_of

I filed a bug with Sun after searching and finding nothing related. We'll see how it goes...

Update: Well, this happens with ServletException, NamingException (and all its children), plus a slew of others.

Update: I submitted the bug to Sun. Here's my response so far :)

************************************************
Your report has been assigned an internal review ID of: 282419

This review ID is NOT visible on the "Java Developer Connection" (JDC).

[...]