Friday, December 30, 2011

Atlas Debugged: The Fountainhead, YAGNI and "clean code"

The last time I re-read The Fountainhead, I felt like many of the ideas in the book could be useful for software developers.  It seemed like there are many parallels between the values demonstrated by the book's hero, Howard Roark, and good development practices.

Many of the ideas dovetail nicely with some Agile development principles, especially the various "keep it simple" ones like YAGNI and DRY (along with DRY's cousins,"once and only once", and "three strikes and refactor").  Roark's designs are driven entirely by purpose, function and constraints, with an open disdain for non-functional or ornamental additions.  For example, about the house he builds for Austen Heller, Roark says:

“Every piece of it is there because the house needs it – and for no other reason… You can see each stress, each support that meets it... But you’ve seen buildings with columns that support nothing, with purposeless cornices, with pilasters, moldings, false arches, false windows… Your house is made by its own needs. The others are made by the need to impress. The determining motive of your house is in the house. The determining motive of others is the audience.”

Keeping things simple and focused is hard work, just as agile development and YAGNI is not an excuse for sloppiness.  If done correctly, it should be quite the opposite.  In Roark's designs, “Not a line seemed superfluous, not a needed plane was missing. The structures were austere and simple, until one looked at them and realized what work, what complexity of method, what tension of thought had achieved the simplicity.” [emphasis added]

This made me think of the quote in Uncle Bob's Clean Code: "Learning to write clean code is hard work... You must sweat over it. You must practice it yourself, and watch yourself fail. You must watch others practice it and fail."

There were some other ideas I'm hoping to examine in more depth:
- right tool for the job - using technology idiomatically vs. legacy patterns with new technology
- the architect as a hands-on practitioner (vs. ivory tower)
- leveraging innovations - new methods, technologies etc.
- professional satisfaction / motivation
- interactions with business stakeholders, "people skills" and organizational politics
- making best of bad situations - looking for best possible solution even if you don't agree with the business problem to be solved

Saturday, November 12, 2011

Primefaces global AJAX events

You can use jQuery global AJAX events with PrimeFaces to refactor behaviors that appear on multiple components.  A good example is if you have a data grid with multiple p:commandLinks and other controls that execute different methods and re-render the grid, and need to run the same onComplete in all of them.

For example, if you start with something like this:

<h:panelGroup id="grid">...</h:panelGroup>
<p:commandLink update="grid" actionListener="#{bean.update1}" onComplete="updateGrid()"/>
<p:commandLink update="grid" actionListener="#{bean.update2}" onComplete="updateGrid()"/>
<p:commandLink update="grid" actionListener="#{bean.update3}" onComplete="updateGrid()"/>

You could pull the updateGrid() statement out into an AJAX listener like this


jQuery(document).ajaxComplete(function(e, xhr, opts) {
  $response = jQuery(xhr.responseXML);
  if ($response.find("div[id$='grid']").length > 0) {
     updateGrid();
  }
});
By using ajaxComplete and parsing the XHR response object, you can see which DIV was going to be impacted by the partial update.  That way, if you had some other AJAX controls (say, an autocompleter) that you didn't want to trigger the updateGrid() function, you could filter that out.  Another option would be to set global=false on the specific Primefaces components that you don't want to fire the global jQuery ajaxComplete event.  The Primefaces autocompleter doesn't support this in Primefaces 2.x but does in 3.0.  

Tuesday, November 08, 2011

Primefaces p:ajax and jQuery AJAX events

All of Primefaces' JSF components use the jQuery AJAX engine, which means you can catch global AJAX events with $(document).ajaxStart and $(document).ajaxStop.

Standard JSF components like h:selectOneMenu will also go through jQuery when using a p:ajax facet (vs. standard f:ajax).

Conversely, jsf.ajax.onEvent does not appear to work with Primefaces components as it does for f:ajax.  

See also:

IE9 and strict JSON checking

Got an error from IE9 complaining about "invalid character". Turned out this was because the JSON response string had control characters (newlines) in it. These need to be escaped.

Other browsers will let you get away with it, but IE9 is particularly strict and jQuery doesn't check the JSON response string before handing it off to JSON.parse.

 Similar issue found here: https://mail.mozilla.org/pipermail/es-discuss/2010-June/011420.html

Wednesday, October 26, 2011

Serialization, class hierarchy and preserving sessions

If you have a class that implements Serializable, superclass fields do NOT get serialized unless the parent class also explicitly implements Serializable. That bit me the other day with session-scoped objects and preserving sessions across restarts, using Glassfish 3.1.1. Glassfish will complain if any objects in session scope or their properties don't implement serializable, but if those objects extend some parent class, the parent class fields are silently ignored and end up being null on session restore. So the source of the problem wasn't immediately clear, like it would have been if individual session objects or properties within them weren't serializable.

Saturday, October 22, 2011

Quick and dirty SSO with LTPA

If you have WebSphere application server in your environment, it is in fact possible to decode the "LtpaToken" cookie in code for quick-and-dirty SSO with non-WebSphere apps.

The main reason you might want to do this is if you have a portal-like application on WebSphere and want to link to other applications on different non-WebSphere servers.  This is only useful if WebSphere is your main point of entry.

Here's how you do it:

  • Export the LTPA encryption key to a file from WebSphere using the admin console.  You provide a passphrase and a filename.  
  • Find the "com.ibm.websphere.ltpa.3DESKey" value in the exported file.  This is the encrypted key.
  • Base64 decode the above key and decrypt with 3DES, using the passphrase provided.  The decrypted value is the actual key for decrypting LTPA tokens.
  • Take the "LtpaToken" cookie, base64 decode it, and decrypt it with the key.  The legacy LtpaToken cookie (which you can get with "interoperability mode") is encrypted with 3DES; the newer LtpaToken2 cookie uses AES.
  • Convert to String and parse.  The string looks like "values%expiration%signature" where the expiration is a standard UNIX timestamp, which you should use to ensure the token is still valid; and the values somewhere will contain the user DN (e.g., uid=user,ou=company,dc=com).
The Alfresco codebase contains a good example of how to do this in Java

Relevant fragments of code:
    private static final String AES_DECRIPTING_ALGORITHM = "AES/CBC/PKCS5Padding";
   private static final String DES_DECRIPTING_ALGORITHM = "DESede/ECB/PKCS5Padding";

    private byte[] getSecretKey(String ltpa3DESKey, String ltpaPassword) throws Exception
    {
        MessageDigest md = MessageDigest.getInstance("SHA");
        
        md.update(ltpaPassword.getBytes());
        
        byte[] hash3DES = new byte[24];
        
        System.arraycopy(md.digest(), 0, hash3DES, 0, 20);
        
        Arrays.fill(hash3DES, 20, 24, (byte) 0);
        
        final Cipher cipher = Cipher.getInstance(DES_DECRIPTING_ALGORITHM);
        
        final KeySpec keySpec = new DESedeKeySpec(hash3DES);
        
        final Key secretKey = SecretKeyFactory.getInstance("DESede").generateSecret(keySpec);

        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        
        byte[] secret = cipher.doFinal(Base64.decodeBase64(ltpa3DESKey.getBytes()));
        
        return secret;
    }
    
    private byte[] decrypt(byte[] token, byte[] key, String algorithm) throws Exception
    {
        SecretKey sKey = null;

        if (algorithm.indexOf("AES") != -1)
        {
            sKey = new SecretKeySpec(key, 0, 16, "AES");
        }
        else
        {
            DESedeKeySpec kSpec = new DESedeKeySpec(key);
            SecretKeyFactory kFact = SecretKeyFactory.getInstance("DESede");
            sKey = kFact.generateSecret(kSpec);
        }
        Cipher cipher = Cipher.getInstance(algorithm);

        if (algorithm.indexOf("ECB") == -1)
        {
            if (algorithm.indexOf("AES") != -1)
            {
                IvParameterSpec ivs16 = generateIvParameterSpec(key, 16);
                cipher.init(Cipher.DECRYPT_MODE, sKey, ivs16);
            }
            else
            {
                IvParameterSpec ivs8 = generateIvParameterSpec(key, 8);
                cipher.init(Cipher.DECRYPT_MODE, sKey, ivs8);
            }
        }
        else
        {
            cipher.init(Cipher.DECRYPT_MODE, sKey);
        }
        return cipher.doFinal(token);
    }
    
    private IvParameterSpec generateIvParameterSpec(byte key[], int size)
    {
        byte[] row = new byte[size];
        
        for (int i = 0; i < size; i++)
        {
            row[i] = key[i];
        }
        
        return new IvParameterSpec(row);
    }
// How to use it:
byte[] secretKey = getSecretKey(ltpa3DESKey, ltpaPassword);
byte[] ltpaTokenBytes = Base64.decodeBase64(ltpaToken.getBytes());
String token = new String(decrypt(ltpaTokenBytes, secretKey, DES_DECRIPTING_ALGORITHM));
// or
String token = new String(decrypt(ltpaTokenBytes, secretKey, AES_DECRIPTING_ALGORITHM));

Wednesday, October 12, 2011

Bizarre Glassfish JSF/EL performance issue

I found a performance issue with JSF on Glassfish 3.1.1 (and prior) in an unexpected place: a seemingly innocuous line of code like

<h:outputText rendered="#{request.requestURL.indexOf('page') ne -1}" .... />

It turns out that invoking methods through EL expressions (new in EL 2.2) triggers creating a new ExpressionFactory, which in turn calls findResource/getResourceAsStream - a file I/O operation.

These expressions in the "rendered" attribute are particularly bad because they get executed multiple times in various parts of the JSF lifecycle.

See:
http://java.net/jira/browse/JAVASERVERFACES-2223
http://java.net/jira/browse/UEL-19

On the plus side - jvisualvm totally rocks.  I am shocked by how easy it was to get started with for tracking down these issues and it was right there in $JAVA_HOME/bin all along.

Tuesday, October 11, 2011

Infinite loop in Glassfish exception logging

When Glassfish catches a ServletException, it calls getRootCause recursively on each successive unwrapped, nested exception by reflection.  This is fine for wrapped ServletException and JspException, which have well-defined semantics for this method, but creates a big problem if you have an application-defined exception that happens to use the same "getRootCause" name with different semantics.

So I created a Glassfish bug report:
http://java.net/jira/browse/GLASSFISH-17390

Related original bug in Tomcat, since fixed:
https://issues.apache.org/bugzilla/show_bug.cgi?id=39088

Sunday, September 25, 2011


Connecting MicroStrategy 8.x Web SDK to a MicroStrategy 9.x Intelligence Server (iServer) doesn't work.  That in itself isn't a surprise.  What is surprising, though, is that the failure persists and then prevents the 8.x Web SDK from working with an 8.x iServer like it's supposed to on subsequent connections, resulting in an error "The required encryption level is not supported in this release."

Apparently, this is a result of initializing "static" fields on the first session creation, such that those values get stuck for the lifetime of the classloader.  In this case, the connection to 9.x sets some of these values in ways that are incompatible with 8.x.  If the first session creation is to an 8.x iServer, then there's no problem - attempting to connect to 9.x will break but won't prevent subsequent connections to 8.x.

This is an unusual edge case, to be sure.  I only encountered it during development in a heterogeneous environment, with a custom portal that connects to multiple iServers.

Friday, September 23, 2011

solved windows 7 sleep problem

Sleep mode was grayed out on new Dell laptop (Latitude E6420) on Windows 7.

The fix was to disable the legacy VGA driver, which came up with a yellow warning icon in Device Manager ("device cannot start").

Simply installing the latest Intel Graphics drivers did not fix the issue - the legacy VGA driver had to be explicitly disabled.

Saturday, September 17, 2011

502 Proxy Error solved

I was getting mysterious, intermittent proxy errors using Apache mod_proxy to talk to an older version of Glassfish (2.x). It turned out the root cause was size of HTTP headers as cookies were accumulating, which caused the underlying app server to close the socket without a response.

A similar phenomenon was documented here:
http://kenai.com/jira/browse/KENAI-2727

The fix was trivial: increase the "receive-buffer-in-bytes" setting in domain.xml to 8192 bytes.

Finding it was a pain, because the older version of Glassfish had a bug such that the underlying IOException that aborted the request never got logged anywhere!
http://java.net/jira/browse/GLASSFISH-5181

Wednesday, September 07, 2011

JSF + JPA without EJB or Spring

I've had success using JSF 2.0 with JPA on Glassfish 3.0.1, without using Spring or EJB for managing middle-tier services or DAOs. (I actually do like Spring - the goal in this case was to minimize dependencies, moving parts and learning curve, rather than to avoid any particular technology.)

A similar approach was detailed here:
http://www.swview.org/blog/best-way-use-jpa-web-tier

 Using only the JSF @ManagedBean and @ManagedProperty annotations, along with the JPA @PersistenceContext, you can refactor business services or DAOs into other JSF managed beans that are injected into the one exposed directly to the UI. What this looks like in practice:

 orders.xhtml:
 

OrderBean.java:
@ManagedBean
public class OrderBean {
  @ManagedProperty(value="#{orderDao}")
  private OrderDao orderDao;
  private List orders;
   
  @PostConstruct
  public void init() { 
    this.orders = orderDao.getOrders();
  } 
  public List getOrders() { 
    return orders;
  }
}

OrderDao.java:
@ManagedBean
@ApplicationScoped
public class OrderDao {
   @PersistenceContext(...)
   private EntityManager em;
   
   public List<Order> getOrders() { 
     return em.findAll(Order.class);
   }
}
In this example, the DAO (OrderDao) is just another JSF managed bean, but not actually referenced directly from a page, only from another managed bean. This approach lets you isolate the logic tied to EntityManagers from the rest of your "normal" JSF managed beans, and makes it (marginally) easier to unit-test your managed beans because you can mock the DAO at a higher level instead of mocking the entity manager.

 This does NOT buy you declarative transactions or any of the other good stuff you get with Spring or EJB. (You can inject a UserTransaction with @Resource, and call it explicitly, though.) So it works best for simple apps with mostly read operations and basic single-object CRUD transactions.

 Also, with Java EE 6 CDI, all this may become moot because @Named and @Inject annotations effectively blur the line between all the different managed bean flavors (EJB, JSF, Spring/POJO), although I haven't found a good way to replace JSF @ViewScoped without buying into Seam.

Saturday, September 03, 2011

WebSphere "invalid Oracle URL specified" error


Another unhelpful WebSphere error, this time with an assist from Oracle.

This happened to me when I configured my JDBC data source with the default wpdbJDBC_oracle JDBC provider, using the XA datasource (OracleXADataSource), and used the "container managed" J2C authentication alias instead of "component managed".  The WebSphere admin console will successfully test the connection, but when you use it in a web application, it will fail with this "Invalid Oracle URL specified" error.  It was so hard to track down because it made me focus on the JDBC URL, which wasn't ever the problem.  It never occurred to me that the admin console and the web applications would somehow be getting connections and signing into Oracle differently, which tricked me into thinking that my configuration was really ok when it wasn't.

For the record, the web application was just doing a straight JNDI datasource lookup without any resource-ref mapping in web.xml, using the same JNDI name as bound in the server.

Also, changing to a different non-XA JDBC provider using plain OracleConnectionPoolDataSource resulted in "invalid username/password".

When I changed the datasource to use the component-managed alias instead, and restarted, everything worked.

Tuesday, August 30, 2011

WebSphere Portal error - "Puma requested entity type Group from VMM but received Entity"

Got this error when trying to upgrade from WebSphere Portal 6.0 to 7.0.

com.ibm.portal.puma.SchemaViolationException: EJPSG0053E: Puma requested entity type Group from VMM but received Entity.

This sets a record for one of the most unhelpful errors ever. It can be caused by a bad entry in LDAP - in my particular case, it was a few groups whose objectClasses (groupOfUniqueNames and top) had somehow been saved with base-64 encoding instead of as plain text, although the values themselves when decoded were correct. The same exact LDAP directory was also working fine with WebSphere 6.0.

One of IBM's pages implies that the configuration itself is broken - when in my case, the configuration was perfectly fine and the problem was a bad entry in LDAP. The error gives no help tracking down the broken entry.

https://www-304.ibm.com/support/docview.wss?uid=swg21419580

Other IBM resources imply that the specific configuration issue is making sure that the group and user LDAP object classes are unique - i.e., don't use "top". In my case, I was doing pretty standard stuff - groupOfUniqueNames and inetOrgPerson.

http://publib.boulder.ibm.com/infocenter/wpzosdoc/v6r1/index.jsp?topic=/com.ibm.wp.msg.doc/messages/com.ibm.wps.puma.resources.Messages.html

https://www-304.ibm.com/support/docview.wss?uid=swg1PK80507

Friday, August 26, 2011

Javascript, getYear and cookie expiration

Chrome and Firefox both return an offset from 1900 for date.getYear() instead of a four-digit year (pre-Y2K, a two-digit year). So new Date().getYear() returns 111 instead of 2011.

Old news, right? Well, I've been getting away with this for a while and not realizing it, in logic to expire cookies, and didn't notice until switching to Chrome.

The cookie expiration function was something like this

Date d = new Date();
d.setYear(d.getYear() - 1); // last year = 110
document.cookie = "COOKIE_NAME=; expires=" + d.toGMTString();

Firefox was happily setting the cookie's expiration to the year 0110, which was wrong but still accomplishing the end goal of expiring the cookie.

Chrome, on the other hand, mangled toGMTString to only print the three digits "110" and was not interpreting the cookie as expired.

Of course, the better question is why write this code yourself anyway, when jQuery, Dojo, etc., all have their own cookie APIs.

Wednesday, August 24, 2011

Javascript associative array and iteration order

I just noticed that some browsers, like IE9 and Chrome, don't preserve insertion order of keys when iterating with a for-in loop ["for (key in array)"]. Although technically Javascript never guaranteed preserving order in a for-in loop, so many popular browsers did anyway that I was taken by surprise.

The Chrome behavior is especially odd - it preserves order of insertion for non-numeric keys, but iterates numeric keys in numeric order first. It also coerces numeric strings to numbers.

This code example illustrates

var foo=new Object();

foo["111_"] = 1;
foo["222_"] = 2;
foo["333_"] = 3;
foo["444"] = 4;
foo["555"] = 5;
foo["666"] = 6;

var str = "";
for (var key in foo) { 
  str = str + foo[key] + " ";
}
document.write(str);

In Chrome this prints "4 5 6 1 2 3". In IE 7, IE 8, and Firefox, it prints "1 2 3 4 5 6".

This behavior (particularly the coercion of numeric strings) has been tracked as a bug in Chrome - but unclear whether it will ever be fixed
http://code.google.com/p/chromium/issues/detail?id=37404

ECMAScript spec says "order of enumerating... is not defined"
http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf