Tuesday, July 20, 2010

Creating Log4J Custom PatternLayout

Suppose we need to add a username information into our logging, with Log4J this can be implemented easily. Log4J allows us to create our own PatternLayout.

PatternLayout has a method createPatternParser() that will basically delegate the task to PatternParser. Here, we can create our own MyPatternLayout by extending it from PatternLayout and delegating the task to our own PatternParser as shown below.

MyPatternLayout.java
package myproject;

import org.apache.log4j.PatternLayout; 
import org.apache.log4j.helpers.PatternParser;

public class MyPatternLayout extends PatternLayout {

    @Override 
    protected PatternParser createPatternParser(String pattern) { 
        return new MyPatternParser(pattern); 
    } 
} 

Since we normally just want to add a new character in the pattern instead of redefining all the characters, we need to make sure that we call super.finalizeConverter(c) as shown below.

PatternParser.java
package myproject;

import org.apache.log4j.helpers.PatternParser;

public class MyPatternParser extends PatternParser { 
    private static final char USERNAME_CHAR = 'u'; 
    
    public MyPatternParser(String pattern) { 
        super(pattern); 
    } 
    
    @Override 
    protected void finalizeConverter(char c) { 
        switch (c) { 
        case USERNAME_CHAR: 
            currentLiteral.setLength(0); 
            addConverter(new MyPatternConverter()); 
            break; 
        default: 
            super.finalizeConverter(c); 
        } 
    } 
} 

MyPatternConverter.java
package myproject;

import org.apache.log4j.helpers.PatternConverter; 
import org.apache.log4j.spi.LoggingEvent;

public class MyPatternConverter extends PatternConverter {

    @Override 
    protected String convert(LoggingEvent evt) { 
        // For simplicity, assume this information is retrieved from somewhere. 
        return "User1"; 
    } 
} 

Main.java
package myproject;

import org.apache.log4j.Logger;

public class Main {

    private static final Logger logger = Logger.getLogger(Main.class); 
    
    public static void main(String[] args) { 
        logger.info("Hello World"); 
        logger.debug("Hello World"); 
        logger.warn("Hello World"); 
        logger.error("Hello World"); 
    } 
} 


Make sure to change the layout to our own custom PatternLayout in the log4j.properties.
log4j.properties
log4j.rootLogger=DEBUG, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender

log4j.appender.A1.layout=myproject.MyPatternLayout 
log4j.appender.A1.layout.ConversionPattern=[%-5p] <%u> [%c.%t] - %m%n

Thursday, July 15, 2010

Storing and Retrieving an Object with JNDI

In this article, I'm gonna talk about how to store and retrieve an object with JNDI. To make it easier, I'll use File System provider which can be downloaded from
http://java.sun.com/products/jndi/downloads/index.html

Let's do the world famous Hello World application.

Hello.java
package myproject;

public interface Hello { 
    
    public String getMessage(); 
} 

HelloImpl.java
package myproject;

public class HelloImpl implements Hello { 
    
    @Override 
    public String getMessage() { 
        return "Hello World"; 
    } 
} 

HelloFactory.java
package myproject;

import java.util.Hashtable;

import javax.naming.Context; 
import javax.naming.Name; 
import javax.naming.spi.ObjectFactory;

public class HelloFactory implements ObjectFactory {

    @Override 
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, 
            Hashtable<?, ?> environment) throws Exception { 
        return new HelloImpl(); 
    } 
} 
This class is responsible in creating an instance of Hello.

Main.java
package myproject;

import java.util.Hashtable;

import javax.naming.Context; 
import javax.naming.InitialContext; 
import javax.naming.Reference;

public class Main {

    public static void main(String[] args) throws Exception { 
        Hashtable<String, String> env = new Hashtable(); 
        env.put(Context.INITIAL_CONTEXT_FACTORY, 
            "com.sun.jndi.fscontext.RefFSContextFactory"); 
        env.put(Context.PROVIDER_URL, "file:///C:/test"); 
        InitialContext ic = new InitialContext(env); 
        Reference ref = new Reference(Hello.class.getName(), 
            HelloFactory.class.getName(), null); 
        ic.rebind("whatever/object", ref); 
        
        System.out.println(((Hello) ic.lookup("whatever/object")).getMessage()); 
    } 
} 

Thursday, July 8, 2010

Solution to Code Golf: Word Frequency from StackOverflow (Hopefully!)

I just did this to kill my time. Not sure if it's the correct solution for this problem
http://stackoverflow.com/questions/3169051/code-golf-word-frequency-chart
but it seems to fulfill all the requirements :)

import java.io.*; 
import java.util.*;

public class WordFrequency {

    public static void main(String[] args) throws Exception { 
        File f = new File(args[0]); 
        final Map<String, Integer> map = new HashMap<String, Integer>(); 
        BufferedReader br = null; 
        try { 
            br = new BufferedReader(new InputStreamReader(new FileInputStream(f))); 
            String line = ""; 
            while ((line = br.readLine()) != null) { 
                String[] words = line.toLowerCase().trim().replaceAll("[^a-z]", " ").split("\\s+"); 
                for (String word : words) { 
                    word = word.trim(); 
                    if (!word.matches("the|and|of|to|a|o|i|i[tns]|or|")) { 
                        if (!map.containsKey(word)) { 
                            map.put(word, new Integer(1)); 
                        } else { 
                            map.put(word, map.get(word) + 1); 
                        } 
                    } 
                } 
            } 
            List<string> sortedKeysByValue = new ArrayList<string>(map.keySet()); 
            Collections.sort(sortedKeysByValue, new Comparator<string>() { 
                @Override 
                public int compare(String key1, String key2) { 
                    return map.get(key2).compareTo(map.get(key1)); 
                } 
            }); 
            sortedKeysByValue = sortedKeysByValue.subList(0, 22); 
            List<string> sortedKeysByLength = new ArrayList<string>(sortedKeysByValue); 
            Collections.sort(sortedKeysByLength, new Comparator<string>() { 
                @Override 
                public int compare(String str1, String str2) { 
                    return Integer.valueOf(str2.length()).compareTo(Integer.valueOf(str1.length())); 
                } 
            }); 
            String longestString = sortedKeysByLength.get(0); 
            int maxWidth = 80 - 4 - longestString.length(); 
            double scale = 1 / ((double) map.get(sortedKeysByValue.get(0)) / (double) maxWidth); 
            double val = 0.0; 
            for (String key : sortedKeysByValue) { 
                val = ((double) map.get(key) / (double) maxWidth); 
                double len = val * scale * maxWidth; 
                System.out.print(" "); 
                for (int n = 0; n < len; n++) { 
                    System.out.print("_"); 
                } 
                System.out.println(); 
                System.out.print("|"); 
                for (int n = 0; n < len; n++) { 
                    System.out.print("_"); 
                } 
                System.out.println("| " + key + " "); 
            } 
        } finally { 
            if (br != null) { 
                br.close(); 
            } 
        } 
    } 
}

The 'cat WordFrequency.java | wc -m' gives me 2794 characters :p Anyway, I didn't bother to obfuscate my solution to make it shorter. It was all for fun. Enjoy! :)

Friday, July 2, 2010

Getting Started with Java Debugger

In this article I'm gonna explain how to use Java Debugger with Eclipse and NetBeans.
1. Run the application in command line.
java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8888 \
-jar whatever.jar
The suspend=y means that the application will be suspended until the debugger has been attached to it.
2. In Eclipse, click 'Debug Configuration' in the Debug icon. Go to 'Remote Java Application' and specify the project that we want to debug, the connection (Socket Attach), hostname, and port number.
In NetBeans, click on 'Attach Debugger' and specify the connection type (SocketAttach), hostname, and port number.

Getting Started with Java Profiler

In this article, I'm gonna explain how to use Java Profiler.

==============
Using VisualVM
==============
Sun JDK ships with VisualVM. It's a profiler tool that is best on NetBeans platform. To use it, do the following:
1. Type this in command line to start the application.
java -Dcom.sun.management.jmxremote.port=9999 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false -jar whatever.jar
2. Run VisualVM. From command line, type jvisualvm.
3. By default all the applications that run on the JVM will be listed there, but we can also add the JXM connection by right clicking on Local and choose 'Add JMX Connection' and specify in the port. In this case, it will be 'localhost:9999'. Right click then click 'Profile'.

==============
Using NetBeans
==============
Since VisualVM is based on NetBeans platform, the NetBeans profiler is somewhat similar to the VisualVM. To use it, do the following:
1. Type this in command line to start the application.
java "-agentpath:$NETBEANS_HOME/profiler/lib/deployed/jdk16/linux
/libprofilerinterface.so=$NETBEANS_HOME/profiler/lib,5140" -jar whatever.jar
2. Open NetBeans and click 'Attach Profiler'.

=============
Using Eclipse 
=============
1. Install the TPTP plugin in Eclipse.
2. Type this in command line to start the application.
java "-agentpath:$ECLIPSE_HOME/plugins
/org.eclipse.tptp.platform.jvmti.runtime_4.6.0.v201006071900/agent_files/linux_ia32
/libJPIBootLoader.so=JPIAgent:server=enabled;CGProf" -jar whatever.jar
Using agentpath only works in TPTP version 4.6 onwards. For lower version of TPTP, refer to the official guide.
2. Click 'Profile Configuration' in the Profile icon.
3. In the 'Attach to Agent'. Go to the 'Agent' tab and click that agent.
4. Click 'Profile'

This tutorial has been tested using VisualVM for JDK 1.6.0_20, Eclipse Helios, and NetBeans 6.9 on Linux. For Windows, use the appropriate library (.so file) for Windows.