Thursday, 12 July 2012

Ignored EJB3 @Column name attribute

I am working on an EJB3 project after a few months on other work and I ran into a small issue with the EJB3 @Column name attribute. The attribute is meant to allow the mapping of a column to a different name in an entity. If your database table has an unusable (or just unfriendly) column name you can map it to a more logical field name in the entity.

In my case the table is a mapping table that I am creating an object model against and I wanted to map the "sub_account_name" column to "name". My initial implementation was as follows:


@Entity
@Table(name = "portfolio_subaccount")
public class Portfolio implements Serializable {
    private static final long serialVersionUID = 3450488234689579105L;

    @Id
    @GeneratedValue
    private long id;
    private String name;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    @Column(name = "sub_account_name", nullable = true)
    public String getName() {
        return name;
    }

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


The deployment does not report any errors however the test client code below causes a GenericJDBCException to be thrown.


public class Client {

    public static void main(String[] args) {
        try {
            Context context = getInitialContext();
            BeanRemote remote = (BeanRemote) context.lookup("SomeBean/remote");
            Portfolio portfolio = remote.getPortfolio(352);
            System.out.println(portfolio.getId() + ": " + portfolio.getName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static Context getInitialContext() throws NamingException {
        Properties props = new Properties();
        props.put(Context.PROVIDER_URL, "jnp://server:1099");
        props.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
        props.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");

        return new InitialContext(props);
    }
}

The exception (from the server logs) indicates that the name column is not found on the table:

2012-07-12 11:37:28,049 ERROR [org.hibernate.util.JDBCExceptionReporter] (WorkerThread#2[172.19.216.159:34038]) Invalid column name 'name'.

Which is quite interesting given the annotation indicating the column is actually called "sub_account_name". The problem is caused by the location of the annotation, it seems that Hibernate does not handle some annotations being on the variable declaration while others are on the accessor methods. Moving the @Column annotation to the variable declaration corrects the problem. The corrected class is below.

@Entity
@Table(name = "portfolio_subaccount")
public class Portfolio implements Serializable {
    private static final long serialVersionUID = 3450488234689579105L;

    @Id
    @GeneratedValue
    private long id;

    @Column(name = "sub_account_name", nullable = true)
    private String name;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id id;
    }

    public String getName() {
        return name;
    }

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


Friday, 8 June 2012

Getting intranet website authentication wrong

Website usability is as important for internal sites as for publicly accessible sites, yet for some reason the corporate whip-crackers are willing to accept substandard implementations when only their employees are being forced to use a service. The points below are problems in a leave management system provided by a major international company. It is unclear whether this international company or their local implementation team is responsible for the usability idiocy but here are some of the more annoying design problems:

  • Authentication integration. In most large corporations some form of central authentication service is typically available. There is very rarely a need to reinvent the security wheel by having internal websites with their own username and password authentication system. This is one of the worst usability failures as it is pure laziness by the implementer to use the out-of-box authentication rather than integrating to the central authentication service.
  • Username selection. Assuming laziness wins out and the site implements its own internal authentication, try to allow logical usernames. An employee ID code is a great database key but expecting employees to memorise their employee ID is totally ridiculous, particularly for a system they are not accessing daily. If you are unable/unwilling to let the users log in with their network credentials at least allow them to use an easy to remember username, their email address at the company for example.
  • Password constraints. Continuing with the custom authentication assumption, try to minimise user frustration with password selection criteria. Weak passwords are obviously a security concern but for internal sites where local network access is required this is less of an issue. Simplistic password constraints only serve to frustrate users. The most ridiculous (actual) example is not allowing repeating characters, thus the strong password "M@ss!veAtt4ck" is rejected yet "Idiot123" is allowed. This is not increasing security, it is needlessly annoying your users.
  • Reset password process. Should the user need to reset their password, try to simplify the process. On the login screen there will typically be a "Forgot password" link, it is really trivial to use the username already entered on the login page to populate the reset password page. Requiring the user to re-enter the username on the reset password page is redundant, even if it is an easily remembered value. Similarly, once the password reset is complete (via email redirect for example) rather populate the username on the login screen automatically, or preferably log the user in automatically with the newly created password.
The issues above only relate to the process of logging into the website, it is not hard to imagine the (un)usability of the rest of the site.

Thursday, 7 June 2012

Bash script to extract dependencies from Java WebStart

A quick script to retrieve all the WebStart artifacts from a JNLP file and generate a corresponding classpath:


#!/bin/bash


SERVER=http://server:8082/e-calypso/
wget -Nrq -nd $SERVER/html/jaws/calypso.jnlp
CP=""
for jar in `cat calypso.jnlp | grep jar | cut -d"\"" -f 2`
do
        CP="$CP:./lib/"`echo $jar | cut -d"/" -f 3`
        wget -Nrq -nd $SERVER$jar
done
echo $CP > classpath


The function of the script is pretty simple, it retrieves the JNLP file from the server and then extracts all jar references in the file to retrieve the corresponding JAR files. As the JAR files are being retrieved they are added (in the correct order) to a variable to be used as the classpath. 

The need for this script arose from a requirement to have a client application launch using the correct dependencies and classpath ordering as used by the WebStart application. An alternative approach would have been to create a new WebStart JNLP with the new application as the entry point, unfortunately this was not practical given the organisational constraints (no access to deploy the change and 6+ week lead time for deployment via the approved process).

Some useful side effects of this approach:
  • It can be used to launch the application in the JNLP without requiring a WebStart launcher
  • The application can be launched with additional JVM arguments not supported by WebStart
A few notes on the script (as always):
  1. The -Nrq command line option to wget uses time stamps (-N) to ensure only new files are downloaded. The existing files are overwritten (-r) with the latest version, rather than having a number appended to the file name as is the default behaviour for wget. The output from wget is suppressed by the -q option so as not to print out unnecessary information.
  2. The -nd command line option prevents wget from creating directory structures when retrieving the files, this simplifies the classpath creation.
  3. The generation of the classpath could be improved with a regular expression rather than expecting a specific format in the JNLP file.

Thursday, 12 April 2012

Calypso v12 API changes - Disconnecting a client from the DataServer

In previous versions of Calypso the following code would have worked as expected:

DSConnection ds = ConnectionUtil.connect(username, password, appName, environmentName);
// Do some stuff ...
ds.disconnect();

In the v12 API (SP5) the disconnect() method has been deprecated and unfortunately the JavaDocs were not updated to specify that the DSConnection.logout() method must be used instead. This is also not mentioned in the developer guide, nor is the fact that the client application will not be able to complete cleanly (at least without a call to System.exit()) before the logout() method is called.

A more logical deprecation implementation would have been to call DSConnection.logout() from the disconnect() method.

Tuesday, 31 January 2012

Spot the defect - XML string manipulation

So I have been a bit lazy about posting recently, time to look at a defect found in some production code:


            String details = XPathReader.getValueAt(detailPath, xml);
            if (details != null && details.length() > 0) {
                String mappedDetails = getFormattedDetails(details, accId, pay);
                logger.info("Details [" + details + "] replaced with [" + mappedDetails + "]");
                xml = xml.replace("Details=\"" + details + "\"", "Details=\"" + mappedDetails + "\"");
            }
            return xml;

At first glance there is nothing particularly wrong with this code, the XPathReader class extracts a value from a specific path in the XML, some mapping is done on the value and the original value us replaced with the updated details. Unfortunately there is a sneaky defect in this code. The log message printed for the problematic input is:

INFO  [MappingSessionBean] Details [Book##VALUE&DEV] replaced with [Book VALUE 111111111]

Take a moment to see if you are able to spot the problem... Not yet apparent? The input XML (with some details replaced):


<RequestXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <Header Sending_System="SOURCE" Message_Identifier="Update"
            Message_Status="New" Message_Name="Nostro Update" />
      <AccountingInfo BICCode="XXXXXXXX" CurrencyCode="USD"
            TradingArea="Book" Amount="12345.67/NEW/11111111/444444.44"
            ValueDate="2011-11-11+0200" Details="Book##VALUE&amp;DEV"
            TransactionReference="111111111" AccountType="PRINCIPAL" />
</RequestXML>


Still not clear? Notice the difference between the input XML Details field and the log message?

Details="Book##VALUE&amp;DEV" 
Details [Book##VALUE&DEV]

The Details value in the XML has the ampersand escaped as "&amp;" while the call to XPathReader.getValueAt() returns the attribute value without the escaping. The getFormattedDetails method merrily goes about its job enriching the details string to be "Book VALUE 111111111" but then the code fails when trying to do the replace call:

        xml = xml.replace("Details=\"" + details + "\"", "Details=\"" + mappedDetails + "\"");

The contents of the details variable are now "Book##VALUE&DEV" instead of the original "Book##VALUE&amp;DEV" so no replacement is performed. Given that this code is due to be replaced shortly the quick and easy fix is to use the Apache commons-lang StringEscapeUtils.escapeXml() method. Thus the replace call becomes:

        xml = xml.replace("Details=\"" + StringEscapeUtils.escapeXml(details) + "\"", "Details=\""
                + StringEscapeUtils.escapeXml(mappedDetails) + "\"");

The alternative (and more correct solution) would be to convert the XML to an object using your favourite XML library and then perform the data manipulation on the object before converting back to XML.