Wednesday, July 28, 2010

Log SQL Statements with iBatis

Here's what I've done to enable logging of SQL statements in an iBatis context. This adds to the various bits of advice I've found on the web.

Perhaps it's due to the environment I'm using (Java 1.6, iBatis 2.3.4, SLF4J 1.5.11, Log4J 1.2.15), but the guidance offered in the iBatis Dev Guide is, in my experience, not quite right. In fairness, something must have changed since last I tried that advice (was I using Java 1.5 back then? I'm not sure), since, as I recall, it resulted in the SQL logging I needed. However, at the moment I find that simply configuring this:
<logger name="java.sql.PreparedStatement">
    <level value="debug"/>
</logger>
...yields no SQL output at all.

On target is the advice given in the Apache Bookstore, i.e. to specify this:
<logger name="java.sql">
    <level value="debug"/>
</logger>
This yields all of connection information, prepared statements, values inserted as prepared statement parameters and result sets, something like this:
Connection
{conn-100008} Preparing Statement:  select name, description  from mytable where name like ?      
{pstm-100009} Executing Statement:  select name, description  from mytable  where name like ?               
{pstm-100009} Parameters: [name%]
{pstm-100009} Types: []
{rset-100010} ResultSet
{rset-100010} Header: [NAME, DESCRIPTION]
{rset-100010} Result: [name2, Desc for name2]
{rset-100010} Result: [name3, Desc for name3]
You'll possibly get even more output, depending on your app - since the java.sql namespace is more than just these things. If this is a concern, use the following:
    <logger name="java.sql.Connection">
        <level value="debug"/>
    </logger>

    <logger name="java.sql.PreparedStatement">
        <level value="debug"/>
    </logger>

    <logger name="java.sql.ResultSet">
        <level value="debug"/>
    </logger>
I've personally found that the output from PreparedStatement and ResultSet are the most useful - but omitting Connection appears to preclude these log statements from appearing. The Connection logger also appears to control the output level of PreparedStatement and ResultSet - i.e., setting Connection to INFO results in zero output from any of them. I begin to suspect that Connection has a parent relationship to the other two; perhaps this is what's changed since last I did this.

In either event, here are some SQL-interceptor tools recommended by various posts (though I've yet to try either one):

p6spy: http://www.p6spy.com/
jdbcdslog: http://code.google.com/p/jdbcdslog/

And here are all the web references I used for this post:

Stack Overflow:
http://stackoverflow.com/questions/3014318/how-to-do-ibatis-version-of-show-sql
http://stackoverflow.com/questions/2635058/ibatis-get-executed-sql
Apache Bookstore: http://www.apachebookstore.com/confluence/oss/display/IBATIS/How+do+I+get+SqlMapClient+to+log+SQL+statements
iBatis Dev Guide: http://ibatisnet.sourceforge.net/DevGuide.html#d0e2600

Tuesday, July 27, 2010

Generic Conversion Between List<T> and String

Here's a reusable generic "converter" interface for converting between comma-separated strings and Lists of a given type:

/**
* Converts between comma-separated string and lists of type T.
*/
public abstract class AbstractConverter<T> {

protected abstract T getInstance(String s);

/**
* Converts a list of items into a comma-separated string
*/
public String toString(List<T> items)
{
String s= "";
String comma = "";
for (T item : items) {
s += comma + item;
comma = ",";
}
return s;
}

/**
* Converts a comma-separated string into a list of items.
*/
public List<T> toList(String csv)
{
if (csv == null)
{
return new ArrayList<T>();
}

String[] items = csv.split(",");
List<T> theList = new ArrayList<T>();
for (String item : items) {
T thisItem = getInstance(item);
theList.add(thisItem);
}
return theList;
}

/**
* Converts a comma-separated string into a list of items, excluding any items in the given
* exclude list.
*/
public List<T> toList(String csv, List<T> excludes)
{
if (csv == null)
{
return new ArrayList<T>();
}

String[] items = csv.split(",");
List<T> theList = new ArrayList();
for (String item : items) {
T thisItem = getInstance(item);
if (!excludes.contains(thisItem))
{
theList.add(thisItem);
}
}
return theList;
}
}

I can now provide concrete converter classes of types as needed, for example:

/**
* Converts between comma-separated string and lists of MyType objects.
*/
public class MyConverter extends AbstractConverter<MyType> {

// provide an instance of the custom type
protected MyType getInstance(String s)
{
return new MyType(s);
}

// optionally enforce a singleton, if needed
private MyConverter() {
// EMPTY
}

// provide a convenience for static utility classes
private static AbstractConverter<MyType> cvt = new MyConverter();
public static AbstractConverter<MyType> inst()
{
return cvt;
}
}

Since I want to make things convenient for clients of this, I now provide a static utility class that precludes the need to instantiate anything:

public class MyHelper {

private static AbstractConverter cvt = MyConverter.inst();

public static String toString(List<MyType> items)
{
return cvt.toString(items);
}

public static List<MyType> toList(String csv)
{
return cvt.toList(csv);
}

public static List<MyType> toList(String csv, List<MyType> excludes)
{
return cvt.toList(csv, excludes);
}
}

Now my clients can call e.g. MyHelper.toList("x, y, z") instead of MyConverter.inst().toList("x, y, z"). A small advantage, but it does help with readability and reinforces the notion that this is a collection of utility functions.

Friday, July 23, 2010

When Log4j Logs Messages Twice...

Since my Google'ing only gave me a clue instead of the complete answer on this one, I post it here for future reference.

When a given logger in a log4j configuration is outputting messages twice to your appender, you might try adding the additivity attribute as a fix:
<logger name="myLogger" additivity="false">
    <level value="info"/>
    <appender-ref ref="myFile"/>
</logger>

Same idea for logger or for category. From the DTD (I'm using version 1.2.15):
<!ELEMENT category (param*,(priority|level)?,appender-ref*)>
<!ATTLIST category
  class         CDATA   #IMPLIED
  name  CDATA #REQUIRED
  additivity (true|false) "true"  
>

<!ELEMENT logger (level?,appender-ref*)>
<!ATTLIST logger
  name  CDATA #REQUIRED
  additivity (true|false) "true"  
>

This worked for me. As you can see, the additivity defaults to true. There may be other fixes, but I didn't dig any further after fixing my problem.

Thursday, July 22, 2010

Determine Which Row is Selected in a JSF Table

Let's say you have a JSF table with numerous rows, and that when one of them is "selected" you'd like to know which one it is. Let's assume a JSF event is fired off when you click on the row, resulting in a callback to your application. In this callback, you want to determine the identity of the row in a way that correlates to your application, such that you can proceed with whatever action is needed. Here's the sequence:
  1. Table is constructed with rows representing application objects A, B and C
  2. User clicks somewhere in row B, e.g. in an input text box
  3. JSF event fires and your application is called back
  4. The application determines that B has been selected and processes that application object as needed
Now the obvious way to "determine that B has been selected" would be to simply set the ID attribute for each row to an identifying value at table construction time. But, using such variables for the ID attribute is not allowed in JSF. Don't ask me why. Here's one way to get around this:
  1. Add a parameter to the UIComponent with the identity as needed
  2. Register an interest in the JSF event that will get fired when the UIComponent is selected
  3. Examine the event received in your application callback to find the UIParameter child
  4. Examine the UIParameter to extract the identity of the row
The code snippets, first for the JSF markup addressing step #1 and #2. Note that I'm using IceFaces, setting the per-row variable for use in each UIComponent comprising each row:
<ice:dataTable 
     var="thisRow"
     value="#{app.allRows}"
    ....
>
....
    <ice:inputText valueChangeListener="#{app.callback}">
        <f:param name="name" value="#{thisRow.id}"/>
    </ice:inputText>
....
</ice:dataTable>
Here are steps #3 and #4, the application callback:
public void callback(ValueChangeEvent event) { 

    for (UIComponent child : event.getComponent().getChildren()) {
        if (child instanceof UIParameter) {
            UIParameter param = (UIParameter) child;
            if ("name".equals(param.getName())) {
                String thisID = (String)param.getValue();
                if (thisID.equals(theIDofInterest)) {
                    // process as needed
                }
            }
        }
    }

Tuesday, July 20, 2010

Using JSF Conversion with Custom Objects

My goal is to provide a user-facing input text box that accepts a comma-separated list of values, and converts these to an application-specific object. Since I'm using JSF, I'll take advantage of their built-in conversion facility - but I want to remain as decoupled from JSF as possible. This means I'll implement their Converter class, but I'll do so only with very minimal high-level business logic (as opposed to emulating their example usage in the JEE Tutorial example). That way, I can reuse my conversion logic in other frameworks, for other clients, etc.

My application object is a collection of name objects. The name objects look something like this:
public class MyName {   

    private String name;

    public MyName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    public boolean equals(Object obj) {
        if (null == obj) {
            return true;
        }

        if ((obj == null) || (getClass() != obj.getClass())) {
            return false;
        }

        MyName other = (MyName) obj;
        return new EqualsBuilder().append(this.name, other.name).isEquals();
    }

    public int hashCode() {
        return new HashCodeBuilder(3, 13).append(this.name).toHashCode();
    }

    public String toString() {
        return name;
    }
}
Note that I've implemented toString as well as the equals and hashCode methods. The builder classes are provided by Apache Commons Lang; I've blogged about them here.

The collection object uses the Java List to wrap the name objects:
public class MyNameList {

    private List<MyName> names = new ArrayList<MyName>();

    public MyNameList (List<MyName> names) {
        setNames(names);
    }

    public static MyNameList getInstance(String csv) {
        if (csv == null) {
            return new MyNameList();
        }
        String[] names = csv.split(",");
        List<MyName> nameList = new ArrayList();
        for (String name : names) {
            nameList.add(new MyName(name));
        }
        return new MyNameList(nameList );
    }

    public List<MyName> getNames() {
        return names;
    }

    public void setNames(List<MyName> names) {
        this.names = names;
    }

    public String toString() {
        StringBuffer sb = new StringBuffer();
        String comma = "";
        for (MyName name: names) {
            sb.append(comma).append(name);
            comma = ",";
        }
        return sb.toString();
    }
}

Here's what the JSF converter implementation might look like - there's not much there, as planned:
public class MyNameListConverter implements Converter {

    public Object getAsObject(FacesContext context,
                              UIComponent component, String newValue)
            throws ConverterException {

        return MyNameList.getInstance(newValue);
    }

    public String getAsString(FacesContext context,
                              UIComponent component, Object value)
            throws ConverterException {

        return value == null? "" : value.toString();
    }
}
I must register the converter with JSF:
<converter>
    <description>
        Converter for CSV list of name values
    </description>
    <converter-id>MyNameListConverter</converter-id>
    <converter-class>
        com.mybiz.MyNameListConverter
    </converter-class>
</converter>
Finally I reference the converter in my JSF page:
 <ice:inputText id="nameValues" partialSubmit="true"
     converter="MyNameListConverter"
     value="#{bean.nameList}"/>

Lots of moving parts are needed when working with JSF. But, the more I can encapsulate, the less it will cost to migrate to a different web framework down the road.

Monday, July 19, 2010

What Database Am I Connecting To?

If you run your functional tests in different environments (your local box, the nightly build, etc.), in addition to running your components in a production setting, you might want to get feedback at startup time around just what database you're connecting to. Since I was recently bit by nightly tests that were wiping out my development-local database tables - since I'd hardwired the connection URL - I was motivated to put this kind of feedback in place. Here are some details around how I did this.

Our nightly build has certain environment variables set:

dbip=BuildHost
dbinstance=TestDatabase
dbuser=username
dbpass=password

To simulate this environment, I set these same variables in my shell (Unix, Cygwin, ...), changing the values as needed to facilitate connecting to my local database. Next I set up a property configuration in my Spring startup file that uses these variables, replacing things per the environment:
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="properties">
            <value>
                database.driver=oracle.jdbc.OracleDriver
                database.url=jdbc:oracle:thin:@//${dbip}:1521/${dbinstance}
                database.user=${dbuser}
                database.password=${dbpass}
            </value>
        </property>
    </bean>
These Spring-generated properties are used to create a data source, which is used to configure an iBatis SQLMapClient, which I then use in my DAO:
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
          destroy-method="close">
        <property name="driverClassName" value="${database.driver}"/>
        <property name="url" value="${database.url}"/>
        <property name="username" value="${database.user}"/>
        <property name="password" value="${database.password}"/>
    </bean>
....
    <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
        <property name="configLocation" value="classpath:SqlMapConfig.xml"/>
        <property name="dataSource" ref="dataSource"/>
    </bean>
....
    <bean id="ibatisDao" class="com.mybiz.resource.db.ibatis.IbatisDao">
        <property name="sqlMapClient" ref="sqlMapClient"/>
    </bean>
The DAO implements ApplicationContextAware so that it can examine the Spring context, in particular the properties of interest in the data source:
public class MyDao implements ApplicationContextAware
{
    private ApplicationContext context;

    public void setApplicationContext(ApplicationContext ctx)
    {
        context = ctx;
        dumpDataSource(ctx);
    }

    public void dumpDataSource(ApplicationContext ctx)
    {
        BasicDataSource dataSource =  ctx.getBean(BasicDataSource.class);
        logger.info("============>>> Data Source: " + dataSource.getUrl()
                + " (user " + dataSource.getUsername() + ")");
    }
}
Finally, I get myself into the habit of looking for that log output at the start of my test runs to be sure I'm connecting to the expected database. Once burned, twice shy.

Friday, July 16, 2010

JEE Authentication: Login Errors, Roles, Access Denied, and Logout

In a previous post around LDAP authentication using Jetty, I had some unfinished business. Here I'll deal with login errors, restrict the login to a given role, deal with subsequent access denied scenarios, display the currently logged-in user name, and provide log-out functionality.

The web.xml I'd started with already specified a login-error page, but I was simply pointing it to the same page as the login form. This results in that page simply refreshing without any indication to the user of why that happened. What we want is some kind of message displayed that indicates the given username or password was not valid.

Now, I could simply copy the login.jsp to another JSP, say login-error.jsp, add a message to that page and alter the web.xml to specify that new page on login error:
<login-config>
    <auth-method>FORM</auth-method>
    <realm-name>ldap</realm-name>
    <form-login-config>
        <form-login-page>/faces/login/login.jsp</form-login-page>
        <form-error-page>/faces/login/login-error.jsp</form-error-page>
    </form-login-config>
</login-config>
But now both login.jsp and login-error.jsp contain the same FORM snippet. Now if I next want to specify yet another login page to which the user is directed on an "access denied" error (which we'll deal with momentarily), and I'm still afflicted with copy-paste fever, I'll have the same login form in three places. Let's factor it out instead into a JSP snippet named loginform.jsp:
<form method=post action="j_security_check">
    <label for="j_username" style="font-weight:bold">Username</label>
    <input type="text" name="j_username" id="j_username" style="margin-left:10px"/>
    <label for="j_password" style="font-weight:bold">Password</label>
    <input type="password" name="j_password" id="j_password" style="margin-left:10px"/>
    <input type="submit" value="Log In"/>
</form>
The usual login page now looks like this:
<div style="margin-top:25px; margin-left:25%">
    <h2 style="text-decoration:underline; color:blue;margin-left:-10px">Management App</h2>
    <h3>Please Log In</h3>
    <%@include file="loginform.jsp"%>
</div>
And I'll provide an "access denied" page that looks like this:
<div style="margin-top:25px; margin-left:25%">
    <h2 style="text-decoration:underline; color:blue;margin-left:-10px">Management App</h2>
    <h3>Please Log In</h3>
    <div style="color:red;font-weight:bold;">Authentication failed. User is not in Required Role.</div>
    <%@include file="loginform.jsp"%>
</div>
Configuring HTTP-403 responses (i.e. access denied) to navigate to this page is done like so:
<error-page>
    <error-code>403</error-code>
    <location>/login/accessDenied.jsp</location>
</error-page>
Access denied problems will occur if a given user is not in the expected role. So far, the web.xml has granted authorization to all roles by virtue of the wild-card for the role-name. We can restrict that by naming a role instead:
<security-constraint>
    <web-resource-collection>
        <web-resource-name>Protected Resources</web-resource-name>
        <url-pattern>*.iface</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>admin</role-name>
    </auth-constraint>
    <user-data-constraint>
        <transport-guarantee>
            CONFIDENTIAL
        </transport-guarantee>
    </user-data-constraint>
</security-constraint>
Now, once a user provides his/her credentials at the login form, these are first checked by the LDAP module (configured as a Jetty realm, as per the previous post); if those are valid, the user is next confirmed to be assigned the admin role. If that is the case, all is well and navigation will proceed as configured by the JSF navigation rule (again, please see the previous post). If the credentials are not valid, the user will be redirected to the login-error page, this time with an informative error message about the login problem. If the credentials are good but the user is not assigned the admin role, the user will be redirected to the access-denied page, again with a informative message.

Displaying the current username is a simple matter of leveraging the built-in getRemoteUser(), provided by HttpServletRequest. Since, after logging in, I've transitioned into a JSF application - and because I'm adverse to using JSP scriptlets to accomplish use of that getter once I'm in JSF - I simply provide a getter in one of my JSF managed beans that fetches the HTTP request and returns the user name:
public String getUserName() {
    return getServletRequest().getRemoteUser();
}
...referencing it, as usual, with the JSF expression language:

User: #{svh.userName}

Finally, I'll provide log-out functionality. First, a command link (done with the IceFaces framework):
<ice:commandLink 
    action="logout" immediate="true" value="Logout" 
    style="margin-left:5px;color:blue;font-size:medium"/>
The logout action is mapped with a navigation rule:
<navigation-rule>
    <description>Logout</description>
    <from-view-id>/*</from-view-id>
    <navigation-case>
        <from-outcome>logout</from-outcome>
        <to-view-id>/login/logout.jsp</to-view-id>
        <redirect/>
    </navigation-case>
</navigation-rule>
...taking us to the logout.jsp page, which invalidates the session and invites the user to log back in:
<% session.invalidate(); %>

<div style="margin-top:5px; margin-left:25%">
    <h2 style="text-decoration:underline; color:blue;margin-left:-10px">Management App</h2>
    <h3>Logout Succeeded</h3>
    <p>
        You are now logged out of the Management UI.
    </p>
    <a href="/index.jsp" style="text-decoration:underline">Return to Login page.</a>
</div>
The index.jsp redirects to the application's JSF-based home page:
<html>
    <head>
        <title>Management UI</title>
    </head>
    <body>
    <%
        String redirectURL = "./index.iface";
        response.sendRedirect(redirectURL);
    %>
    </body>
</html>
And, as mentioned in the first post, all iface resources are protected by a security constraint, so this will redirect to the login page.

Wednesday, July 14, 2010

LDAP Authentication with Jetty

If you noticed nothing else but the titles of my last two posts, you might suspect that I went down the Spring-Security road and back-pedaled to a standard JAAS approach. You would be correct.

As it turned out - to my surprise and with great disappointment - I found Spring Security to be impenetrable. Now, Spring makes a lot of things in my life easier, and in fact that's the only reason I use it. But when it becomes a tangled snarl of undocumented opaqueness, and especially when I read that even an expert in Acegi had trouble with it (see the Wrap-Up in that article), I decide to find another way.

Here are my basic building blocks for a JAAS approach with a JSF application in Jetty. First, I configure my web.xml with an authorization constraint:
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Protected Resources</web-resource-name>
            <url-pattern>*.iface</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>*</role-name>
        </auth-constraint>
        <user-data-constraint>
            <transport-guarantee>
                CONFIDENTIAL
            </transport-guarantee>
        </user-data-constraint>
    </security-constraint>
My web.xml has already mapped an IceFaces PersistentFacesServlet to *.iface, so that's what the URL pattern is about. My initial naive attempt was to use a URL pattern of /*, but that's much too broad - it will preclude e.g. loading image resources as part of your login page. That login page is also configured in the web.xml:
    <login-config>
        <auth-method>FORM</auth-method>
        <realm-name>ldap</realm-name>
        <form-login-config>
            <form-login-page>/faces/login/login.jsp</form-login-page>
            <form-error-page>/faces/login/login.jsp</form-error-page>
        </form-login-config>
    </login-config>
The login page (./login/login.jsp) is dirt-simple so far. I'm only working on basic functionality at this point, and there's nothing pretty about it:
    <form method=post action="j_security_check">
        <label for="j_username">Username</label>
        <input type="text" name="j_username" id="j_username"/>
        <br/>
        <label for="j_password">Password</label>
        <input type="password" name="j_password" id="j_password"/>
        <br/>
        <input type="submit" value="Login"/>
    </form>
The realm-name portion in the web.xml references the container environment; configuring the realm is delegated to the container in JEE. My container is (embedded) Jetty, and I configure the Jetty realm via a simple entry in my jetty.xml file:
    <Call name="addUserRealm">
        <Arg>
            <New class="org.mortbay.jetty.plus.jaas.JAASUserRealm">
                <Set name="name">ldap</Set>
                <Set name="LoginModuleName">ldapmodule</Set>
            </New>
        </Arg>
    </Call>
By the way, I followed the Jetty tutorial on JAAS to make all of this happen. My previous post mentioned a gotcha in that article around the LDAP Login Module package name. Depending on which version of Jetty you're using, you may need to make the change discussed there.

In either event, the Jetty realm configuration references a LoginModuleName of "ldapmodule", and as per standard JAAS, this configuration is captured in a file (in my case, a file named ./etc/ldap.conf) referenced by the JVM argument -Djava.security.auth.login.config=etc/ldap.conf. That file is basically a replica of the ldaploginmodule example in the Jetty tutorial (again, except for the package name of the LdapLoginModule class), configured of course with the proper schema references and credentials for my LDAP environment.

Finally, I configure a navigation rule so JSF will take me to my target page after successful login:
    <navigation-rule>
        <description>After Login</description>
        <from-view-id>/login/login.jsp</from-view-id>
        <navigation-case>
            <to-view-id>/index.jsp</to-view-id>
            <redirect/>
        </navigation-case>
    </navigation-rule>
Note that this a bare-bones scaffold for authentication. I have yet to deal with login failures, roles, JSF-messaging for the user, and the like.

Tip: Package Name Correction for Jetty LdapLoginModule

If you're following the Jetty tutorial for establishing JAAS authentication, and you have copied the sample configuration file for the LDAP module, you may come across this error message after you enter your username and password on your login page:

javax.security.auth.login.LoginException: unable to find LoginModule class: org.mortbay.jetty.plus.jaas.spi.LdapLoginModule

The reason is that, at least for the version of Jetty I'm using (6.1.21), the package name has changed to org.mortbay.jetty.plus.jaas.ldap.LdapLoginModule. Make that change in the ldaploginmodule sample in that tutorial and you'll solve this problem (assuming of course you have all the needed dependencies).

For your reference, here's the complete dependency list I'm using for a JSF-based app running with embedded Jetty 6.1.21:
        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty</artifactId>
            <version>${jettyVersion}</version>
        </dependency>

        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty-util</artifactId>
            <version>${jettyVersion}</version>
        </dependency>

        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jsp-2.1-jetty</artifactId>
            <version>${jettyVersion}</version>
        </dependency>

        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty-plus</artifactId>
            <version>${jettyVersion}</version>
        </dependency>

        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty-ldap-jaas</artifactId>
            <version>${jettyVersion}</version>
        </dependency>

Tip: Provide the Spring Security NamespaceHandler Explicitly

If you're working with Spring Security, you might start your pom with an entry like so:
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>3.0.0.RELEASE</version>
</dependency>
This will bring in the spring-web, spring-security-core and commons-logging artifacts, and I would not fault you for thinking you're good to go. You're possibly following this tutorial or another among the many out there, and are providing a Spring config that references the http://www.springframework.org/schema/security namespace - and, at least for me, here's where things did not go as expected. At runtime, the error message I received was this:

Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/security]

After a fair amount of Google'ing and Stack-Overflow'ing, I found the problem - and I reproduce it here to save myself (and hopefully you) the headache next time around: there is a dependency missing that looks like this:
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>3.0.0.RELEASE</version>
 </dependency>

Now you should be good to go.

Friday, July 2, 2010

Using JDK keytool To Generate Keys and Certs

In a previous post, I stepped through use of IBM's KeyMan GUI to generate SSL keys and certificates, placing them in KeyMan's "token", or as more commonly known, a keystore. I then attempted to use this keystore in both a Windows and Linux deployment of my webapp, and met with mixed success - since I used the Windows version of the KeyMan tool, this worked out OK for the Windows webapp, but not so much for the Linux deployment.

My subsequent attempts to use KeyMan's Unix shell script (km) instead - under a Cygwin environment - were much the same: on deployment, an "Invalid keystore format" exception was issued. Clearly, this is about the difference between DOS and Unix file formats. The next obvious step was to simply execute the km script directly under Linux, foregoing any file encoding or translation issues that might be happening with Cygwin. Here, however, after appearing to generate the key pair, the KeyMan GUI went into some kind of blocking wait - or maybe an infinite loop? a deadlock? There was no way to tell; the GUI simply became unresponsive. Followup exercises to include setting KM_HOME in the environment, unpacking the native library support ZIP file and setting the LD_LIBRARY_PATH to point to them, and etc. all proved fruitless.

Finally, I reverted to using the JDK keytool utility, and - no surprise - this works out just fine in both Windows and Linux (i.e. in terms of generating a keystore that is recognized by the webserver). Here is the script I use to generate things, in both Linux and Cygwin:

######################################
#
# generate-keystore.sh - Generate key and certificate
#
######################################

CN=MyKeystore
OU='Web - Development'
ORG='My Biz Inc.'
COUNTRY=US
ALIAS=MyBizKeystore
PASS=password
KEYSTORE=keystore
CERTFILE=cert
EXPIRY=730

# remove it if it's there
[ -f "$KEYSTORE" ] && /bin/rm $KEYSTORE

# generate the keystore with a self-signed cert and an RSA keypair
$JAVA_HOME/jre/bin/keytool -genkeypair -keyalg RSA \
-dname "cn=$CN, ou=$OU, o=$ORG, c=$COUNTRY" \
-alias $ALIAS -keypass $PASS -keystore $KEYSTORE \
-storepass $PASS -validity $EXPIRY

# export the certificate so we can look at it
$JAVA_HOME/jre/bin/keytool -exportcert -alias $ALIAS -file $CERTFILE -keystore $KEYSTORE -storepass $PASS

# print the certificate
$JAVA_HOME/jre/bin/keytool -printcert -file $CERTFILE

If you bump into this error message in the generate step:

Incorrect AVA format

...you'll want to make sure you didn't embed any commas or other special characters in the values you provide. For example, I started out with an Organizational Unit (OU) of 'My Biz, Inc.' - but that provoked the error message. Embedded dashes and periods are apparently OK, but note that I've enclosed any values with embedded spaces in single quotes. That's more a shell issue than a keytool problem.

If you bump into an error message something like this, in the print-certificate step:

lengthTag=109, too big

...you might be trying to pass in the entire keystore to the printcert command; that's why I export the certificate first in the script above, using just that piece as the argument to print it out.