Tuesday, August 28, 2012

Resolving the "Not all jars signed with same certificate" issue

Almost a year ago I wrote posting on how to resolve the "Not all jars signed with the same certificate" problem encountered from time to time with signed Web Start applications.   My solution at that time was to use a short Groovy script to show each jar and a list of the certificates the jar was signed with.  For those familiar with Groovy, that's still my suggestion.

Recently one of our customer service folks was fighting this same problem at a customer site.  To make matters worse, they were not familiar with Groovy, so using my previous script was not an option.   I decided to bite the bullet and write the Java equivalent.

Below is my Java-equivalent of the Groovy script.  The code is saved as the JarSigners project at GitHub.


package util;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

/**
 *  Show which jars are signed with which code signing certificates
 */
public class JarSigners {

 private String folderName;
 private Map> map;
 private Set certs;
 
 public JarSigners(String folderName) {
  this.folderName = folderName;
  map = new HashMap>();
  certs = new HashSet();
 }
 
 public void listJarSigners() throws ZipException, IOException {
  File dir = new File(folderName);
  FilenameFilter filter = new FilenameFilter() {
      public boolean accept(File dir, String name) {
          return name.endsWith(".jar");
      }
  };
  String[] files = dir.list(filter);
  System.out.println("Found "+files.length+" files in folder: "+folderName);
  for (String fileName : files) {
   checkJarForCertificate(fileName);
  }
  System.out.println("Certificates used to sign the jars: "+certs);
  if (certs.size() > 1) {
   System.out.println("WARNING: More that one certificate shows up in the jars!");
  }
  System.out.println("Details jar->certificate: "+map);
 }
 
 private void checkJarForCertificate(String fileName) throws ZipException, IOException {
  File jar = new File(folderName, fileName);
  ZipFile zip = new ZipFile(jar);
  //System.out.println("Processing "+fileName);
  for (Enumeration entries = zip.entries(); entries.hasMoreElements();) {
         // Get the entry name
         String zipEntryName = ((ZipEntry)entries.nextElement()).getName();
         if (zipEntryName.endsWith(".RSA")) {
          if (!certs.contains(zipEntryName)) {
           certs.add(zipEntryName);
          }
          if (map.containsKey(fileName)) {
           List list = map.get(zipEntryName);
           list.add(zipEntryName);
          } else {
           List list = new ArrayList();
           list.add(zipEntryName);
           map.put(fileName, list);
          }
         }
     }
  zip.close();
  
 }

 /**
  * @param args
  * @throws IOException 
  * @throws ZipException 
  */
 public static void main(String[] args) throws ZipException, IOException {
  if (args.length != 1) {
   System.out.println("Invalid invocation: you must pass a folder name");
   return;
  }
  JarSigners js = new JarSigners(args[0]);
  js.listJarSigners();
  System.out.println("Done!");
 }

}


Hope this helps!

Thursday, August 23, 2012

Auto-completion for email addresses in Swing


Recently we added the ability to send Electronic Receipts from our Point-of-Sale product at work.   One of the requirements was to enhance the user interface around the capture of email addresses.   We were just using a standard JTextField to capture the value.  Specifically, we were looking to make the entry of the email address easier by way of auto-completion, much like a lot of ajax-enabled fields in web applications.


I didn't want to re-invent the wheel and felt certain that I could something to get me started with the help of Google!   After a couple searches, I found a great starting point for the new code.  Samuel Sjöberg's auto-completion was extremely close to what I wanted.   Rather than auto-completing a person's name, I wanted to provide auto-completion for the part of the email address after the "@".    I also modified the code a bit to read the auto-completion candidates from a properties file.  This allows you to provide a properties file containing some of the most common email addresses.   Below is a copy of a sample properties file.

#################################################################################
# Commonly used Email Addresses                            
#                                                          
# This file will be read when the component starts up      
# and used to create an ArrayList of names to be used 
# for auto-completion of email addresses entered into 
# fields that assign the EmailAddressAutoCompletionDocument                      # to the field.  
#
# For better performance, put the most commonly used/encountered
# email addresses at the top of the list.
#
#################################################################################

 hotmail=hotmail.com
 gmail=gmail.com
 yahoo=yahoo.com
 verizon=verizon.net
 comcast=comcast.net
 aol=aol.com
 outlook=outlook.com
 mail=mail.com
 bigstring=bigstring.com
 shortmail=shortmail.com
 lycos=lycos.com
 excite=excite.com
 zoho=zohomail.com
 zzn=zzn.com

Below is the interesting part of the auto-completion of the email address field. First, I included a check for a max length of 255 to prevent too much data for our database columns. Next, we check for an "@" sign entered.  If no @ sign, add the text.  There is also a check to prevent a second @ from being added to the field.  Once we get past that, its time to call the completion service looking for a match on the email address. If a match is found, we add the rest of the string to the text box and select the part of the address that wasn't actually entered by the user.

 public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
        if (str == null || str.length() == 0) {
            return;
        }

        // check for max length first
        if (str == null || ((str.length() + getLength() > maxLength) && maxLength > 0))
   return;
        
        String text = getText(0, offs);        // Current text.
        int atSignIdx = text.indexOf(AT_SIGN); // 1st @ sign
        if (atSignIdx != -1 && offs > atSignIdx) {
         String fullText = text+str;
         //prevent entry of 2nd @ sign
         if (fullText.indexOf(AT_SIGN, atSignIdx+1) != -1) {
          return;
         }
         String address = fullText.substring(fullText.indexOf(AT_SIGN)+1);
         String completion = complete(address);
         int length = offs + str.length();
         if (completion != null && text.length() > 0) {
                super.insertString(offs, completion.substring(address.length()-1), a);
                documentOwner.select(length, getLength());
         } else {
          super.insertString(offs, str, a);
         }
        } else {
            super.insertString(offs, str, a);
        }
    }

I also added automatic validation of the full address by providing a regular expression to match against and then assigned a KeyListener to help implement the validation with color coding.   As the user types an email address, the foreground color is red and then when the email address is complete, either through auto-completion of the address portion or just typing, the foreground color is changed to green for a valid address.

Thanks to Samuel for posting his code.  My updated version can be found at GitHub.

Hope this helps!

Monday, August 13, 2012

Book Review: MongoDB in Action

Overview
 I am a big fan of Manning Publications books and so when it came time to do some personal research on NoSQL, I started with the Manning website to what books they had that covered the topic.  I didn't really have any specific requirements, like document model vs key value pairs vs graph database.  I just wanted something fairly current and covering one of the main players in the area.

I found this book on the website and the publication date (2012) helped ensure that I was getting was something current and not a couple years old.  The other nice surprise was the size of the book.  For anyone that's read an 'In Action' book from Manning, you know they generally tend to be fairly large, on the scale of 300-400 page or more.   This book weighs in at 278 pages and that includes 5 appendices!

Contents
The book is broken down into 3 parts:

  1. Getting Started, which covers about 50 pages talking about MongoDB at a high-level, the core server, tools, replication, the JavaScript shell and writing programs and using drivers to interact with the database.
  2. Application Development in MongoDB covers principles of schema design, MongoDB databases, collections, Mongo's query language, updates atomic operations and deletes.
  3. MongoDB Mastery covers index and query optimization, replication, sharding and deployment. 
Summary
An excellent book by Kyle Banker and yet another excellent book from the folks at Manning Publications.  I blew through the first two parts of the book, eager to learn more about NoSQL and MongoDB.  The author does an excellent job explaining concepts within MongoDB and backs up his statements with the reasons why things are done the way they are.

It took a bit longer for me to get through part 3 of the book, no fault of the author or book.  Since this was really my own personal research and there is no way I will be implementing MongoDB or NoSQL at my day job, other than maybe some small personal tools/utilities, the topics of replication and sharding just didn't have the same appeal as learning about the core server, the query language and schema design!

Overall, I would recommend this book to anyone interested in learning about MongoDB.