Category: java

  • Convert Leptonica PIX data into Java BufferedImage

    This snippet allows us to convert Leptonica PIX data into Java BufferedImage, in my case the pix->data could be compressed using zlib so I am decompressing before recreating image.
    We are assuming here that this is bi-tonal image(1 bpp)

    File used in example :

    Image represented by the data:
    binary

    Sample Usage

       byte[] zlibData = FileUtil.read("pix-compressed.txt");
       BufferedImage image = LeptonicaUtil.convert(zlibData, width, height, 1);
       ImageIO.write(image, "png", new File("C:/temp/test.png"));
    

    Code

    
    import java.awt.image.BufferedImage;
    import java.awt.image.WritableRaster;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.nio.ByteBuffer;
    import java.nio.ByteOrder;
    import java.nio.IntBuffer;
    import java.util.zip.DataFormatException;
    import java.util.zip.Inflater;
    
    /**
     * Utility for help us work with leptonica pix data
     * @author gbugaj
     *
     */
    public class LeptonicaUtil
    {
    
        public static BufferedImage convert(byte[] indata, int width, int height, int depth)
        {
            try
            {
                // Decompress the bytes
    
                byte[] pixdata = zlibDecompress(indata);
    
                // 4 (4 Bytes per Int)
                int wpl = (width * depth + 31) / 32;
                int byteCount = 4 * wpl * height;
                int rowSize = 4 * wpl;
                assert (byteCount == pixdata.length);
    
                BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);
                WritableRaster raster = img.getRaster();
                byte[] rasterdata = new byte[width * height];
                int rasterindex = 0;
    
                for (int h = 0; h < height; ++h)
                {
                    int start = h * rowSize;
                    byte[] row = new byte[rowSize];
                    System.arraycopy(pixdata, start, row, 0, rowSize);
                    // Convert row bytes into linedata using 4 bytes to represent a
                    // 32 bit int
                    IntBuffer intBuffer = ByteBuffer.wrap(row).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
                    int[] linedata = new int[intBuffer.remaining()];
                    intBuffer.get(linedata);
                    for (int w = 0; w < width; ++w)
                    {
                        int word = w >> 5;
                        // need this to get proper byte ordering
                        int index = (31 - (w & 31));
                        byte val = (byte) (((linedata[word] >> index) & 1) ^ 1);
                        rasterdata[rasterindex] = val;
                        ++rasterindex;
                    }
                }
    
                raster.setDataElements(0, 0, width, height, rasterdata);
                return img;
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
            catch (DataFormatException e)
            {
                e.printStackTrace();
            }
            return null;
        }
    
        public static byte[] zlibDecompress(byte[] data) throws IOException, DataFormatException
        {
    
            /**
             * 78 01 - No Compression/low 78 9C - Default Compression 78 DA - Best
             * Compression
             */
    
            if (data.length < 2)
                return data;
    
            // Check magic headers
            if (!((data[0] & 0xff) == 0x78 && ((data[1] & 0xff) == 0x01 || (data[1] & 0xff) == 0x9C || (data[1] & 0xff) == 0xDA)))
            {
                return data;
            }
    
            Inflater inflater = new Inflater();
            inflater.setInput(data);
    
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
            byte[] buffer = new byte[1024];
            int count = -1;
            while (!inflater.finished() && count != 0)
            {
                count = inflater.inflate(buffer);
                outputStream.write(buffer, 0, count);
            }
    
            inflater.end();
            outputStream.close();
            return outputStream.toByteArray();
        }
    }
    
    
  • Convert Leptonica PIX data to Java BufferedImage

    This snippet allows us to convert Leptonica PIX data into Java BufferedImage, in my case the pix->data could be compressed using zlib so I am decompressing before recreating image.
    We are assuming here that this is bi-tonal image(1 bpp)

    Sample Usage

       BufferedImage image = LeptonicaUtil.convert(zlibData, width, height, 1);
       ImageIO.write(image, "png", new File("C:/temp/test.png"));
    

    Code

    
    import java.awt.image.BufferedImage;
    import java.awt.image.WritableRaster;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.nio.ByteBuffer;
    import java.nio.ByteOrder;
    import java.nio.IntBuffer;
    import java.util.zip.DataFormatException;
    import java.util.zip.Inflater;
    
    /**
     * Utility for help us work with leptonica pix data
     * @author gbugaj
     *
     */
    public class LeptonicaUtil
    {
    
        public static BufferedImage convert(byte[] indata, int width, int height, int depth)
        {
            try
            {
                // Decompress the bytes
    
                byte[] pixdata = zlibDecompress(indata);
    
                // 4 (4 Bytes per Int)
                int wpl = (width * depth + 31) / 32;
                int byteCount = 4 * wpl * height;
                int rowSize = 4 * wpl;
                assert (byteCount == pixdata.length);
    
                BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);
                WritableRaster raster = img.getRaster();
                byte[] rasterdata = new byte[width * height];
                int rasterindex = 0;
    
                for (int h = 0; h < height; ++h)
                {
                    int start = h * rowSize;
                    byte[] row = new byte[rowSize];
                    System.arraycopy(pixdata, start, row, 0, rowSize);
                    // Convert row bytes into linedata using 4 bytes to represent a
                    // 32 bit int
                    IntBuffer intBuffer = ByteBuffer.wrap(row).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
                    int[] linedata = new int[intBuffer.remaining()];
                    intBuffer.get(linedata);
                    for (int w = 0; w < width; ++w)
                    {
                        int word = w >> 5;
                        // need this to get proper byte ordering
                        int index = (31 - (w & 31));
                        byte val = (byte) (((linedata[word] >> index) & 1) ^ 1);
                        rasterdata[rasterindex] = val;
                        ++rasterindex;
                    }
                }
    
                raster.setDataElements(0, 0, width, height, rasterdata);
                return img;
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
            catch (DataFormatException e)
            {
                e.printStackTrace();
            }
            return null;
        }
    
        public static byte[] zlibDecompress(byte[] data) throws IOException, DataFormatException
        {
    
            /**
             * 78 01 - No Compression/low 78 9C - Default Compression 78 DA - Best
             * Compression
             */
    
            if (data.length < 2)
                return data;
    
            // Check magic headers
            if (!((data[0] & 0xff) == 0x78 && ((data[1] & 0xff) == 0x01 || (data[1] & 0xff) == 0x9C || (data[1] & 0xff) == 0xDA)))
            {
                return data;
            }
    
            Inflater inflater = new Inflater();
            inflater.setInput(data);
    
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
            byte[] buffer = new byte[1024];
            int count = -1;
            while (!inflater.finished() && count != 0)
            {
                count = inflater.inflate(buffer);
                outputStream.write(buffer, 0, count);
            }
    
            inflater.end();
            outputStream.close();
            return outputStream.toByteArray();
        }
    }
    
    
  • Encrypting application configuration files – Java AES

    There other day I needed to encrypt certain properties in our XML configuration files. But there were few challanges that needed to be addressed

    • Use existing Java installation
    • !!! How will Master Encryption Key be derives and stored !!!

    First restriction is very important as it limits us to what algorithmic and key sizes can be used, adding Java Cryptography Extension was not a option.

    This limited us to using AES with key size 128, using 256 would give us an exception.

    java.security.InvalidKeyException:Illegal key size or default parameters
    

    Second important question raised was “How will the master password be derived and possibly stored ?”, there are a number of approaches to this and each have its advantages, but the constraint here is that there should be no user interaction involved.

    Few of the approaches considered

    Passing password as a environment variable
    This requires users to modify the startup parameters and the password is plain text
    Prompting user to provider password when they start the application
    This is good method(KeePass use it) but it requires user interaction, which was not acceptable in my case
    Generating unique machine key
    This method is not the best but it does work well, as long as the attacker don’t know how the key was generated.

    Approach chosen by me was to use the machine generated key/id, I have chooses the MAC address as the machine key, I know it can be easily spoofed but if the attacker is that dedicated then I am screwed anyway.

    Machine key generation algorithm is is straight forward. First we will try to obtain the MAC Address from the network interface, if that fails we we try to use the machine name obtained from the RuntimeMXBean, this is very depended on what JVM we are using as there is no guarantee what getName() method will return, if this fails we will fall back to using
    System.getenv("COMPUTERNAME"), if all this fails we simply throw an exception.

    Encrypted Message Format

    CRYPT:Ss3iHK6kHLswAh7AyoHdo1dbbTJt0UpLxVNRZ9W+bks=

    Encrypted messages can be broken down into three parts

    Identifier : CRYPT used to indicate that this property have been encrypted
    Initial Vector + Data : Ss3iHK6kHLswAh7AyoHdo1dbbTJt0UpLxVNRZ9W+bks=

    Because we know the IV size(128 bits) we can prepend that to the begging of our encrypted message, so when we are decrypting the message we know that first 128bits will be the IV.

    Example Usage

      AESEncryptDecryptUtil util = new AESEncryptDecryptUtil();
            String encrypedMessage = util
                .encrypt("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin scelerisque sodales augue, ac tristique nibh euismod nec. ");
            boolean status = util.isEncrypted(encrypedMessage);
    
            System.out.println("Encrypted : " + encrypedMessage);
            System.out.println("Message encrypted : " + status);
            System.out.println("Decrypted : " + util.decrypt(encrypedMessage));
    

    Encrypting Properties

       public String getUserName()
        {
            try
            {
                AESEncryptDecryptUtil util = new AESEncryptDecryptUtil();
                if (util.isEncrypted(userName))
                {
                    return util.decrypt(userName);
                }
            }
            catch (EncryptDecryptException e)
            {
                e.printStackTrace();
            }
            return userName;
        }
    

    Output

    Encrypted : CRYPT:keGhiojDBpgsUkdepcT6aFvzR0Jpsmi4no3TwP3OzFbAmjvvBxkSQHMyQ8uM81PTCTEWebe3HrE5
    o/+jLgdmQB+CDxBMUvUrFRvFFAGsrCcS+cWgYTi+T5/LTUFonXBOc5r++GQbV2htxy9YislL2JPT
    vMQAGrgMjOFTDBZvuJdeUIC6uKWjtndJidxigxHB
    Message encrypted : true
    Decrypted : Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin scelerisque sodales augue, ac tristique nibh euismod nec.
    
    

    Code Listing

    
    import java.io.UnsupportedEncodingException;
    import java.lang.management.ManagementFactory;
    import java.lang.management.RuntimeMXBean;
    import java.net.InetAddress;
    import java.net.NetworkInterface;
    import java.net.SocketException;
    import java.net.UnknownHostException;
    import java.security.AlgorithmParameters;
    import java.security.InvalidKeyException;
    import java.security.spec.InvalidParameterSpecException;
    import java.security.spec.KeySpec;
    
    import javax.crypto.BadPaddingException;
    import javax.crypto.Cipher;
    import javax.crypto.IllegalBlockSizeException;
    import javax.crypto.SecretKey;
    import javax.crypto.SecretKeyFactory;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.PBEKeySpec;
    import javax.crypto.spec.SecretKeySpec;
    
    import sun.misc.BASE64Decoder;
    import sun.misc.BASE64Encoder;
    
    /**
     * AES Encryption/Decryption Utility that uses MAC address as the Master
     * Encryption Key Initial Vector need to be stored together with the encrypted
     * string so that we can use it to decrypt the message. This is the format that
     * will be produced from the encryption, both encrypted message and initial
     * vector are base 64 encoded, in UTF-8 char set
     * CRYPT:initialVector+encryptedMessage
     * 
     * @author gbugaj
     */
    public class AESEncryptDecryptUtil
    {
    
        private static final int ITERATIONS = 65536;
    
        private static final String STRING_ENCODING = "UTF-8";
    
        private static final String CRYPT_PREFIX = "CRYPT";
    
        /**
         * If we user Key size of 256 we will get java.security.InvalidKeyException:
         * Illegal key size or default parameters , Unless we configure Java
         * Cryptography Extension 128
         */
        private static final int KEY_SIZE = 128;
    
        private static final byte[] SALT = { (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0 };
    
        private SecretKeySpec secret;
    
        private Cipher cipher;
    
        private BASE64Encoder base64Encoder;
    
        private BASE64Decoder base64Decoder;
    
        private AESEncryptDecryptUtil() throws EncryptDecryptException
        {
            try
            {
                /* Derive the key, given password and salt. */
                SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
                KeySpec spec;
    
                spec = new PBEKeySpec(getHardwareKey(), SALT, ITERATIONS, KEY_SIZE);
                SecretKey tmp = factory.generateSecret(spec);
                secret = new SecretKeySpec(tmp.getEncoded(), "AES");
    
                // CBC = Cipher Block chaining
                // PKCS5Padding Indicates that the keys are padded
                cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    
                // For production use commons base64 encoder
                base64Decoder = new BASE64Decoder();
                base64Encoder = new BASE64Encoder();
            }
            catch (Exception e)
            {
                throw new EncryptDecryptException("Unable to initialize", e);
            }
    
        }
    
        /**
         * Get hardware key to be used for encryption, we rely on MAC address as the
         * key, with fallback to JVM name then windows machine name
         * 
         * @return key as char[]
         * @throws EncryptDecryptException
         */
        private char[] getHardwareKey() throws EncryptDecryptException
        {
    
            // Exceptions from this are ignored as we move to next available method
            try
            {
                InetAddress address = InetAddress.getLocalHost();
                NetworkInterface nic = NetworkInterface.getByInetAddress(address);
                if (nic != null)
                {
                    byte[] mac = nic.getHardwareAddress();
                    if (mac != null && mac.length > 0)
                    {
                        return new String(mac, STRING_ENCODING).toCharArray();
                    }
                }
            }
            catch (UnknownHostException e)
            {
                e.printStackTrace();
            }
            catch (SocketException e)
            {
                e.printStackTrace();
            }
            catch (UnsupportedEncodingException e)
            {
                e.printStackTrace();
            }
    
            // Could not obtain MAC Address so we are falling back on the computer
            // name then on the JVM Name
            RuntimeMXBean rmx = ManagementFactory.getRuntimeMXBean();
            String jvmName = rmx.getName();
            String[] parts = jvmName.split("@");
            if (parts.length > 0)
            {
                String name = parts[1];
                if (name != null && !name.isEmpty())
                {
                    return name.toCharArray();
                }
            }
    
            String name = System.getenv("COMPUTERNAME");
            if (name != null && !name.isEmpty())
            {
                return name.toCharArray();
            }
            throw new EncryptDecryptException("Unable to obtain Secure Key");
        }
    
        /**
         * Encrypt given input string
         * 
         * @param input
         * @return
         * @throws EncryptDecryptException
         */
        public String encrypt(String input) throws EncryptDecryptException
        {
            try
            {
                byte[] inputBytes = input.getBytes(STRING_ENCODING);
                cipher.init(Cipher.ENCRYPT_MODE, secret);
                AlgorithmParameters params = cipher.getParameters();
                byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
                byte[] ciphertext = cipher.doFinal(inputBytes);
                byte[] out = new byte[iv.length + ciphertext.length];
                System.arraycopy(iv, 0, out, 0, iv.length);
                System.arraycopy(ciphertext, 0, out, iv.length, ciphertext.length);
                return CRYPT_PREFIX + ":" + base64Encoder.encode(out);
            }
    
            catch (IllegalBlockSizeException e)
            {
                throw new EncryptDecryptException("Unable to encrypt", e);
            }
            catch (BadPaddingException e)
            {
                throw new EncryptDecryptException("Unable to encrypt", e);
            }
            catch (InvalidKeyException e)
            {
                throw new EncryptDecryptException("Unable to encrypt", e);
            }
            catch (InvalidParameterSpecException e)
            {
                throw new EncryptDecryptException("Unable to encrypt", e);
            }
            catch (UnsupportedEncodingException e)
            {
                throw new EncryptDecryptException("Unable to encrypt", e);
            }
        }
    
        /**
         * Decrypt input string
         * 
         * @param input
         * @return decrypted string
         * @throws EncryptDecryptException
         */
        @SuppressWarnings("restriction")
        public String decrypt(String input) throws EncryptDecryptException
    
        {
            if (!input.startsWith(CRYPT_PREFIX))
            {
                throw new EncryptDecryptException("Unable to decrypt, input string does not start with 'CRYPT'");
            }
    
            try
            {
                byte[] data = base64Decoder.decodeBuffer(input.substring(6, input.length()));
                int keylen = KEY_SIZE / 8;
                byte[] iv = new byte[keylen];
                System.arraycopy(data, 0, iv, 0, keylen);
                cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
                return new String(cipher.doFinal(data, keylen, data.length - keylen), STRING_ENCODING);
            }
            catch (Exception e)
            {
                throw new EncryptDecryptException("Unable to decrypt ", e);
            }
        }
    
        /**
         * Helper Exception to wrap arround Encryption/Decryption exceptions
         */
        @SuppressWarnings("serial")
        private static class EncryptDecryptException extends Exception
        {
            public EncryptDecryptException()
            {
                super();
            }
    
            public EncryptDecryptException(String msg)
            {
                super(msg);
            }
    
            public EncryptDecryptException(String msg, Throwable t)
            {
                super(msg, t);
            }
    
            public EncryptDecryptException(Throwable t)
            {
                super(t);
            }
        }
    
        /**
         * Check if given string is already encrypted
         * 
         * @param input
         * @return
         */
        private boolean isEncrypted(String input)
        {
            if (input == null || input.isEmpty())
            {
                return false;
            }
            return input.startsWith(CRYPT_PREFIX);
        }
    
        public static void main(String[] args) throws EncryptDecryptException
        {
            AESEncryptDecryptUtil util = new AESEncryptDecryptUtil();
            String encrypedMessage = util
                .encrypt("TEST");
            boolean status = util.isEncrypted(encrypedMessage);
    
            System.out.println("Encrypted : " + encrypedMessage);
            System.out.println("Message encrypted : " + status);
            System.out.println("Decrypted : " + util.decrypt(encrypedMessage));
        }
    
    }
    
  • java.lang.ClassNotFoundException: org.springframework.web.context.support.StandardServletEnvironment

    While upgrading from Spring 3.0.5 to 3.1.1.RELEASE I started getting following exception.

    java.lang.ClassNotFoundException: org.springframework.web.context.support.StandardServletEnvironment
    	at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1714)
    	at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1559)
    	at org.springframework.web.servlet.HttpServletBean.(HttpServletBean.java:90)
    	at org.springframework.web.servlet.FrameworkServlet.(FrameworkServlet.java:211)
    	at org.springframework.web.servlet.DispatcherServlet.(DispatcherServlet.java:323)
    	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
    	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
    	at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    	at java.lang.Class.newInstance0(Class.java:355)
    	at java.lang.Class.newInstance(Class.java:308)
    

    After some research I came to conclusion that the file have been factored out into ‘spring-web’ subproject.

    So to solve this all we need to do is include ‘spring-web’ dependency

                
    
             	org.springframework
    			spring-web
    			${spring.version}
    			
    				
    					commons-logging
    					commons-logging
    				
    			
    		
    

    Hope this might help someone save some time.

  • PhantomSQL released

    Finally I have released my PhantomSQL project.

    PhantomSQL is a domain specific language designed for mining content from static and dynamic sources, It closely resembles SQL with features borrowed from other popular dynamic languages.
    It can be run as a interpreter or ‘server’ mode, it comes with type 4 JDBC driver for ease of integration with java applications.

    Sample Queries

    Here are just few examples taken from the project site to display some of the syntax of the language.

    Hello World

    Following example illustrates how to query google.com for some blog search results.

    
     select first css("#ires a") as title from https://www.google.com/search
      using get with {'q': "josh bloch", 'tbm':"blg"}
      
      print @title +" : "+ @title['href']
    

    Crawling Flicker.com

    Flicker Integration
    This query does nothing more than query flicker.com for ‘nabilishes’ and then crawl the site using GET while the ‘css(“a.Next”)’ condition matches, at the end it prints how many results have been found.

     @result = select css(".pc_img") from http://www.flickr.com/search 
     using get with {'q': "nabilishes", 'm' : "text"}
     crawl(css("a.Next")) 
     print @result.length()
    
    

    Extracting images from Flicker.com search results

    Here we build on previous example by adding the ‘WHILE-SELECT’ construct and actually saving the image with the ‘save’ function.

    while select css(".pc_img", "src", true) as img from http://www.flickr.com/search  
           using get with {'q': "nabilishes", 'm' : "text"}
           crawl(css("a.Next")) 
     begin
       save (@img)
     end
    

    Retrieving Binary Content

    Following two examples are equivalent.

    Following example illustrates how to retrieve binary content from a site and save it on the filesystem when running via interpreter.

    
    select first css("#main_image", "src", true) as item from https://1saleaday.com
    save(@item)
    

    Following example illustrates how to retrieve binary content from a site with JDBC Driver and dump the file to file system.

        public void retieveFile()
        {
            try
            {
                try
                {
                    Class.forName("com.gbltech.phantomsql.driver.PhantomDriver");
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
                Connection conn = DriverManager.getConnection("jdbc:phantomsql://localhost?characterEncoding=utf8");
                Statement statement = conn.createStatement();
                ResultSet resultSet = statement
                    .executeQuery("select first css(\"#main_image\", \"src\", true) as item from https://1saleaday.com");
                if (resultSet.next())
                {
                    OutputStream out = null;
                    Blob blob = resultSet.getBlob(1);
                    try
                    {
                        out = new FileOutputStream(new File("./test.jpg"));
                        InputStream is = blob.getBinaryStream();
                        byte[] buff = new byte[1024];
                        int read = 0;
                        while ((read = is.read(buff, 0, buff.length)) != -1)
                        {
                            out.write(buff, 0, read);
                        }
                        out.flush();
                    }
                    catch (FileNotFoundException e)
                    {
                        e.printStackTrace();
                    }
                    catch (IOException e)
                    {
                        e.printStackTrace();
                    }
                    finally
                    {
                        if (out != null)
                        {
                            try
                            {
                                out.close();
                            }
                            catch (IOException e)
                            {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
            catch (SQLException e)
            {
                e.printStackTrace();
            }
        }
    

    This is just a taste of what the PhantomSQL can do, there is much more so go check it out.
    I am looking for feedback, criticism and bug reports to let me make the project better, so if you have something drop me a line.

  • Convert given excel column name to column Index, ex ‘A=0’, ‘AA=26’

    Other day I needed to convert excel spreadsheet column name to column index when having 200+ columns is easier to express as ‘FB’ instead of 157

    A = 0 
    B = 1
    AA = 26
    AA = 27
    FB = 157
    

    This code will work for any number of column names.

    	/**
    	 * Convert given excel column name to column Index, ex 'A=0', 'AA=26'
    	 * @param columnName
    	 * @return 0 based index of the column
    	 */
    	private static short convert2ColumnIndex(String columnName) {
    		columnName = columnName.toUpperCase();
    		short value = 0;
    		for (int i = 0, k = columnName.length() - 1; i < columnName.length(); i++, k--) {
    			int alpabetIndex = ((short) columnName.charAt(i)) - 64;
    			int delta = 0;
    			// last column simply add it
    			if (k == 0) {
    				delta = alpabetIndex - 1;
    			} else { // aggregate
    				if (alpabetIndex == 0)
    					delta = (26 * k);
    				else
    					delta = (alpabetIndex * 26 * k);					
    			}
    			value += delta;
    		}
    		return value;
    	}
    

    Converting from index to column name

    This process is trivial we simply keep on taking mod 26 from index till we have nothing left, and converting that value to char.

  • StringOutputStream backed by ByteArrayOutputStream

    The other day I needed to read output stream directly into a string, but JDK does not provides us with a stream directly for that. There have been numerous discussions on why and why not we should have this build into the JDK, but this was a case where I did need it and knew the encoding.

    My implementation

    StringOutputStream backed by ByteArrayOutputStream that simplifies reading of OutputStream directly into string.

    
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.nio.charset.Charset;
    
    /**
     * StringOutputStream backed by ByteArrayOutputStream that simplifies  reading of {@link OutputStream}  directly into string
     * @author Grzegorz Bugaj
     *
     */
    public class StringOutputStream extends OutputStream{
    	private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    	private static final Charset DEFAULT_CHARACTER_SET = Charset.forName("UTF-8");
    	private Charset characterSet;
    	
    	@Override
    	public void write(int b) throws IOException {
    		buffer.write(b);
    	}
    	
    	@Override
    	public void write(byte[] b) throws IOException {
    		buffer.write(b);
    	}
    
    	@Override
    	public void write(byte[] b, int off, int len) throws IOException {
    		buffer.write(b, off, len);
    	}
    	
    	/**
    	 * Get currently set character set if none set default charset of UTF-8 will be used
    	 * @return Charaset 
    	 */
    	public Charset getCharacterSet() {
    		if(characterSet == null)
    			return DEFAULT_CHARACTER_SET;
    		
    		return characterSet;
    	}
    
    	public void setCharacterSet(Charset characterSet) {
    		this.characterSet = characterSet;
    	}
    	
    	
    	@Override
    	public String toString() {
    		return new String(buffer.toByteArray(), getCharacterSet());
    	}
    }
    

    Usage Example

    	/**
    	 * Read in given stream into a string
    	 * @param stream
    	 * @return
    	 */
    	public static String readAsString(InputStream stream) throws IOException{
    		OutputStream sos = new StringOutputStream(); 		
    		// Set chunk size to 64K chunk
    		byte[] buffer = new byte[0x10000];
    		int readLen;
    		while((readLen = stream.read(buffer, 0, buffer.length)) != -1){
    			sos.write(buffer, 0, readLen);
    		}
    		return sos.toString();
    	}
    
  • Set memory for Sonatype Nexus

    While starting Nexus I was getting following error, this was on a machine that was running at 1GB of memory.

    Error occurred during initialization of VM
    Could not reserve enough space for object heap

    So the solution was to edit /usr/local/nexus/bin/jws/wrapper.conf
    and add wrapper.java.additional.4=-Xmx128m parameter this setup java to use only 128mb of memory for the new VM.

    Solution 2

    After further analysis of the config file there are following options that can be set as well, just need to uncomment them

    # Size Java memory, in MB (-Xms)
    #wrapper.java.initmemory=128
    # Size Java memory, in MB (-Xmx)
    #wrapper.java.maxmemory=256

  • Implementing GroupBy using Google guava Multimap and Function

    This is one way of creating a group by like functionality for collections using google guava Multimap and Function.

    Code is straight forward we simply use the index method of the Multimap to group our data by, in here we use our 2 column to group it by the department.

    Sample output

    key = Dev
      1 : Greg
      3 : Roman
    key = Support
      2 : Leo
      4 : Jobby
    

    Code

    package com.gregbugaj.guava;
    
    import java.util.Collection;
    import java.util.Iterator;
    import java.util.List;
    
    import com.google.common.base.Function;
    import com.google.common.collect.Lists;
    import com.google.common.collect.Multimap;
    import com.google.common.collect.Multimaps;
    
    public class GroupByMultimap {
    	public static void main(String[] args) {
    		Object[] o1 = new Object[] { 1, "Greg", "Dev" };
    		Object[] o2 = new Object[] { 2, "Leo", "Support" };
    		Object[] o3 = new Object[] { 3, "Roman", "Dev" };
    		Object[] o4 = new Object[] { 4, "Jobby", "Support" };
    
    		List rows = Lists.newArrayList(o1, o2, o3, o4);
    		Multimap grouped = Multimaps.index(rows,
    				new Function() {
    					@Override
    					public String apply(Object[] item) {
    						return (String) item[2];
    					}
    				});
    
    		Iterator keyIterator = grouped.asMap().keySet().iterator();
    		while (keyIterator.hasNext()) {
    			String key = keyIterator.next();
    			System.out.println("key = " + key);
    			Collection dataRows = grouped.get(key);
    			for (Object[] o : dataRows) {
    				System.out.println(String.format("  %d : %s", o[0], o[1]));
    			}
    		}
    	}
    }
    
    
  • Rich modalpanel events not populating form values

    So here is a problem I was running into, I usually use rich:modalpanel just to display some data to the user, but the other day I needed to collect some input. After happily submitting my page all the submitted values were null, what the hell. After some research here are my conclusions on this problem.

    We need to put “form” elements inside the modalPanel in order for this to work, and make sure that the ui:insert in our template is not nested within out top form.

    Reason for this is because of where modalpanel is appended to the DOM, that is also a reason why we have a4j:form inside the panel.

    
    
     
    	 
    		
    	
    		
    			
    	   
     
         CONTENT HERE
     
      
       
    
    

    Using Facelets and JSF 1.2