Monday, April 2, 2012

Some parsing fun

    So I ran into some issues the other day where I wanted to allow nested variable declarations  in a script parser I am working on. I'm sort of partial to ant and jsp var declarations that take the format of ${varname}, and have quite happily been using some regex to to handle these.
String varName = currentStringBuffer.toString().replaceFirst(".*\\$\\{(.+)}.*", "$1");
    Sure this is a little simplistic, but it worked, for the majority of cases. I would simply loop over the string until I had replaced all of the declarations in it with their appropriate values. Simplistic, but functional. Then I ran into a problem...

<server:when test=".['${currentModified}' != '${${attribute}}']"> 
    I was passing in a variable called attribute, who's value was intended to be the name of the variable to resolve. The first pass should convert ${${attribute}} to ${lastModified} as the attribute variable is equal to 'lastModified'. The second pass should then convert ${lastModifed} to the appropriate value.
  <server:when test=".['${currentModified}' != '${lastModified}']">
  <server:when test=".['1330459479383' != '1330459479383}']">
    Well, my regex just wouldn't handle it.  So it was time to do it properly. Just one problem, after some serious research on the net, I discovered that regexs, at least in Java, really aren't supposed to handle what are called Context Free Grammars.

    Searching the net I found a number of algorithms to make sure that the parenthesis were balanced, but nothing that was really useful for what I wanted, which was to replace all of the vars with values and keep the rest of the original string. After some adaptation, here's what I came up with, which may be of some help to people.

Stack<StringBuffer> stack = new Stack<StringBuffer>();
            StringBuffer currentStringBuffer = new StringBuffer();
            for (int index = 0; index < varStringBuffer.length(); index++)
            {
                
                if (varStringBuffer.charAt(index) == '$' && varStringBuffer.charAt(index+1) == '{')
                {
                    stack.push(currentStringBuffer);
                    currentStringBuffer = new StringBuffer();
                    currentStringBuffer.append(varStringBuffer.charAt(index));
                }
                else if (varStringBuffer.charAt(index) == '}' && varStringBuffer.charAt(index-1) != '\\' && stack.empty() == false)
                {
                    //pop, and evaluate
                    currentStringBuffer.append(varStringBuffer.charAt(index));
                    String varName = currentStringBuffer.toString().replaceFirst(".*\\$\\{(.+)}.*", "$1");
                    String value = getVarValue(varName);
                    if (value == null)
                    {
                        value = "";
                     }
                    currentStringBuffer = stack.pop();
                    currentStringBuffer.append(value);
                }
                else
                {                    currentStringBuffer.append(varStringBuffer.charAt(index));   
                } 
            }
  • There is a StringBuffer called varStringBuffer. This is where the original string is stored. 
  • We make a new StringBuffer called currentStringBuffer as a place to keep whatever we are working on. 
  • Walking the original, one char at a time, we append that char to the currentStringBuffer if it's not important. 
  • If it is the start of a var declaration, we push the currentStringBuffer onto the stack and allocate a new one. 
  • If we've found a closing bracket, it means there should be a variable declaration in the currentStringBuffer. 
  • We evaluate it, get the previous currentStringBuffer off of the stack. Then append our value to it. 
  • Rinse and repeat. 
There are at least two assumptions being made here. One is that the brackets will always be balanced. The second is that the resolution of a variable will never result in an additional variable declaration. We could probably handle the second by appending to the original StringBuffer, as opposed to the buffer we just popped off of the stack. The first assumption will just result in a failed variable resolution, which will probably throw some cryptic error someplace else. So unless we are feeling nice, we can ignore it for now and file it under Technical Debt.

Friday, March 23, 2012

Working with Stream Filters

    I love StreamFilters. They're great. I frequently use them for compression or keeping statistics about how much data has been written through a pipe, producing MD5s of the data written, or even using them as event producers, so you can be notified with a stream has been closed. You can stack them like Legos and do all sorts of wonderful things.

    There are three things to keep in mind when working with StreamFilters. Make sure you're always conscience of the order in which you stack them, don't try to compress an encrypted stream for example, always encrypt your compressed stream.  Second, life is simpler if you push all of your different read or write methods into just one of them, and call you're actual filtering code from there. The third thing is that you generally get much better performance from a stream if you use the byte array read and write methods as opposed to the simple read() or write() methods that work with ints.

   When I write a stream filter for a single purpose I can generally just override a single read or write method and ignore the other two methods, but when writing a library, you don't get that luxury. So I sat down to write a complete implementation in an abstract class that I could use all over the place.

    The idea was simple enough, use a byte[] of length 1 to handle implementing the read() method, by calling the read(byte[],offset,length) method with a length of 1, and an offset of 0. This worked fine almost all of the time, but something wasn't right on occasion, my streams would close early. After way too much debugging and not much help from the internet I found the following problem. Bytes returned from read call and stored in a byte[] are signed, while the simple read method only returns unsigned ints. A simple bit masking fixes the problem, if you know that it's the cause.  The following code should give anyone enough information write their own InputStreamFilter and not run into any obscure sign issues.

    @Override
    public int read(byte[] b) throws IOException
    {
        return read(b,0,b.length);
    }
  
    @Override
    public int read() throws IOException
    {
         int bytesRead = read(smallBuffer, 0, 1);
         if (bytesRead > 0)  //because we are always passing a buffer of length 1, this should never be zero
         {
             //the bytes come in as signed, so we need to mask them to unsigned so that code using the int read() method always gets a postive result as expected
             return (int)(smallBuffer[0] & 0xff);
         }
         else //if we're closed, then return -1 to indicate as much
         {
             return -1;
         }
    }


Wednesday, February 29, 2012

Working With Java SSL keys

I've seen a number of posts around the net where people were struggling with getting their own private SSL keys to work, or trying to use a keystore that wasn't sitting on the filesystem somewhere. Having spent way too much time beating my head against this I figured I'd throw what I learned out there for posterity.

First things first.  "javax.net.ssl.SSLHandshakeException: no cipher suites in common" is a lie! This message has driven me close to insanity way too many times. Let me translate it for you. "I can't find a valid key or keystore." This generally  comes up when you follow most of the examples on the net that look like this:
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
That first 'null'  is the problem. You must pass in an array of keyManagers. Here is a complete example that will let you work with your own keystore regardless of where you got it.


 SSLContext sslContext = SSLContext.getInstance("SSL");

        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());  
        trustManagerFactory.init(keyStore);    
        
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keyStore, "password".toCharArray());
        
        sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
 You will also get the same error if you do not have a trusted certificate on the client side that matches the certificate used by the server. The aliases must match, as that's what The trust managers use to find the proper key/cert to check against.

If you don't want to write your own thing because your keystore is sitting on the file system someplace safe. You should just use the default SSLSocketFactory. Which is used by setting some system properties.
System.setProperty("javax.net.ssl.keyStore", "keystore path");
System.setProperty("javax.net.ssl.keyStorePassword", "password");
SSLSocketFactory sslSocketFactory = ((SSLSocketFactory)SSLSocketFactory.getDefault());
 I hope this helps.