Tag Archives: javassist

Java Properties dupe key detect using Javassist

In a prior post, I used Commons Configuration to detect duplicate properties in a Properties file. The code was very simple.

Here I present a solution using a Dynamic Proxy class. A complexity is that though Properties extends the Map interface, that interface does not declare a load method. And, the JDK Dynamic Proxy support can only be used for interfaces, not concrete classes. The Javassist library can be used for this purpose as shown in listing 1 below.

Listing 1, dupe detect with Javassist Source Gist

package com.octodecillion.util;

import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;

/**
 * Detect duplicate properties when loading Properties from stream.
 * <p>
 * This version uses <a href="https://jboss-javassist.github.io/javassist/">Javassist</a> Dynamic Proxy support.
 * 
 * @author jbetancourt
 * @since Nov 18, 2015
 */
public class DuplicatePropertyDetectorWithJavassist {

    /**
     * Detect duplicate keys in Properties resource.
     * <p>
     * @see Properties#load(Reader)
     * 
     * @param reader The reader is <b>NOT</b> closed.
     * @return Map Map where map key is duplicated key and list is values of those keys
     * @throws IOException reading the stream
     * @throws RuntimeException for any issue with proxying
     */
    public Map<String, List<String>> load(Reader reader) throws IOException {
        if(reader==null){ throw new NullPointerException("reader cannot be null");}
        
        final Map<String, List<String>> dupeResults = new HashMap<>();
        final String put_method_name = "put";
        
        try {
            ProxyFactory factory = new ProxyFactory();
            factory.setSuperclass(Properties.class);
            factory.setFilter(new MethodFilter() {
                @Override
                public boolean isHandled(Method m) {                    
                    return m.getName().equals(put_method_name);
                }
            });

            ((Properties) factory.create(new Class<?>[0], new Object[0], 
              new MethodHandler() {
                @Override
                public Object invoke(Object self, Method thisMethod, 
                    Method proceed, Object[] args) throws Throwable {

                    String key = (String) args[0],value = (String) args[1];

                    if (((Properties) self).containsKey(key)) {
                        List<String> list = new ArrayList<>();
                        if (dupeResults.containsKey(key)) {
                            list = dupeResults.get(key);
                        } else {
                            dupeResults.put(key, list);
                        }

                        list.add(value);
                    }

                    return proceed.invoke(self, args);
                }

            })).load(reader);

        } catch (NoSuchMethodException | IllegalArgumentException 
                | InstantiationException | IllegalAccessException
                | InvocationTargetException e) {
            throw new RuntimeException(e);
        }

        return dupeResults;
    }    
}

Links
Javassist

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.