Using an ArrayBlockingQueue

This example provides an example of all the significant methods in ArrayBlockingQueue.

Main.java:

import static java.lang.Math.*;
import java.util.concurrent.*;
import java.util.*;
  
public class Main
{
   public static void main(String []args) {
      BlockingQueue<Message> blockingQueue = new ArrayBlockingQueue<Message>(10);
  
      // put 10 elements on the queue
      Random r = new Random();
      for (int i=0; i<10; i++) {
         // abs() can be used without qualification because java.lang.Math is statically imported
         Message<String> m = new Message<String>("Random Message #" + abs(r.nextInt() % 100));
         System.out.println("Adding message '" + m + "'");
         try {
            blockingQueue.put(m);
         }
         catch(InterruptedException e) { } 
      }
       
      // removed 5 first elements and put them into a TreeSet, 
      // notice that the class Message implements Comparable to do so!
      System.out.println("nTreeSet");
      System.out.println("-------");
      TreeSet<Message> treeSet = new TreeSet<Message>();
      blockingQueue.drainTo(treeSet, 5);
      System.out.println(treeSet);
  
      // offer 6 elements to the (tail of the) queue, last element will not be inserted because 
      // queue is full and space does not free up within 5 seconds
      System.out.println("nOffer");
      System.out.println("-----");
      for (int i=0; i<6; i++) {
         Message<String> m = new Message<String>("Message #" + (i+1));
         System.out.println("Offering " + m);
         try {
            blockingQueue.offer(m, 5, TimeUnit.SECONDS);
         }
         catch(InterruptedException e) { }
      }
       
      // iterate over the elements left in the BlockingQueue
      System.out.println("nIterator");
      System.out.println("--------");
      Iterator<Message> iter = blockingQueue.iterator();
      while (iter.hasNext()) {
         System.out.println(iter.next());
      }
       
      System.out.println("nPeek (retrieve but not remove)");
      System.out.println("----");
      System.out.println(blockingQueue.peek());
  
      System.out.println("nPoll (retrieve and remove)");
      System.out.println("----");
      System.out.println("Removing " + blockingQueue.poll());
  
      System.out.println("nSize and Remaining capacity");
      System.out.println("-----------------");
      System.out.println("Size of queue: " + blockingQueue.size());
      System.out.println("Remaining capacity of queue: " + blockingQueue.remainingCapacity());
  
      System.out.println("nConverting to array Message[]");
      System.out.println("-----------------------------");
      Message[] messages = blockingQueue.toArray(new Message[] { });
      for (int i=0; i<messages.length; i++) {
         System.out.println("From array: " + messages[i]);
      }
   }
}
  
class Message<T extends Comparable> implements Comparable<T>
{
   private T contents;
    
   public Message(T contents) {
      this.contents = contents;
   }
    
   public T getContents() {
      return contents;
   }
    
   public String toString() {
      return ""+contents;
   }
    
   public int compareTo(T o) {
      return o.compareTo(contents);
   }   
}

outputs:

Adding message 'Random Message #68'
Adding message 'Random Message #53'
Adding message 'Random Message #27'
Adding message 'Random Message #35'
Adding message 'Random Message #40'
Adding message 'Random Message #14'
Adding message 'Random Message #61'
Adding message 'Random Message #50'
Adding message 'Random Message #81'
Adding message 'Random Message #5'
  
TreeSet
-------
[Random Message #27, Random Message #35, Random Message #40, Random Message #53,
 Random Message #68]
  
Offer
-----
Offering Message #1
Offering Message #2
Offering Message #3
Offering Message #4
Offering Message #5
Offering Message #6
  
Iterator
--------
Random Message #14
Random Message #61
Random Message #50
Random Message #81
Random Message #5
Message #1
Message #2
Message #3
Message #4
Message #5
  
Peek (retrieve but not remove)
----
Random Message #14
  
Poll (retrieve and remove)
----
Removing Random Message #14
  
Size and Remaining capacity
-----------------
Size of queue: 9
Remaining capacity of queue: 1
  
Converting to array Message[]
-----------------------------
From array: Random Message #61
From array: Random Message #50
From array: Random Message #81
From array: Random Message #5
From array: Message #1
From array: Message #2
From array: Message #3
From array: Message #4
From array: Message #5

Get started with an ArrayBlockingQueue

An ArrayBlockingQueue is a FIFO blocking queue with an array used by the underlying implementation. At construction, you specify the maximum space used by the queue. Attempting to put more elements in the queue than is allowed by its capacity constraint will result in a blocking wait. Similarly, attempting to take an element from an empty queue will block.

The following example starts up two threads, a consumer and a producer, that communicate with each other with an ArrayBlockingQueue and generic Message objects. The capacity is 5 elements. The producer tries to put 10 elements on the queue, but blocks until space in the queue is freed up by the consumer.

Main.java:

import java.util.concurrent.*;
 
public class Main
{
   public static void main(String []args) {
      BlockingQueue blockingQueue = new ArrayBlockingQueue<Message>(5);
       
      Producer p = new Producer(blockingQueue);
      new Thread(p).start();
 
      // wait a bit before consuming to allow the queue to fill up
      // and force a blocking wait
      try { Thread.sleep(1000); } catch(InterruptedException e) { }
      Consumer c = new Consumer(blockingQueue);
      new Thread(c).start();
   }
}
 
class Producer implements Runnable
{
   private BlockingQueue<Message> blockingQueue;
 
   public Producer(BlockingQueue<Message> blockingQueue) {
      this.blockingQueue = blockingQueue;
   }
 
   public void run() {
      for (int i=0; i<10; i++) {
         Message<String> m = new Message<String>("message contents #" + i);
         System.out.println("Producing '" + m + "'");
         try {
            blockingQueue.put(m);
         }
         catch(InterruptedException e) {
            return;
         }  
      }
   }
}
 
class Consumer implements Runnable
{
   private BlockingQueue<Message> blockingQueue;
   
   public Consumer(BlockingQueue<Message> blockingQueue) {
      this.blockingQueue = blockingQueue;
   }
    
   public void run() {
    
      while (true) {
         try {
            Message m = blockingQueue.take();
            System.out.println("tConsuming '" + m + "'");
         }
         catch(InterruptedException e) { } 
      }
   }
}
 
class Message<T> {
   private T contents;
    
   public Message(T contents) {
      this.contents = contents;
   }
    
   public T getContents() {
      return contents;
   }
    
   public String toString() {
      return ""+contents;
   }
}

outputs:

Producing 'message contents #0'
Producing 'message contents #1'
Producing 'message contents #2'
Producing 'message contents #3'
Producing 'message contents #4'
Producing 'message contents #5'
Producing 'message contents #6'
        Consuming 'message contents #0'
Producing 'message contents #7'
        Consuming 'message contents #1'
Producing 'message contents #8'
        Consuming 'message contents #2'
Producing 'message contents #9'
        Consuming 'message contents #3'
        Consuming 'message contents #4'
        Consuming 'message contents #5'
        Consuming 'message contents #6'
        Consuming 'message contents #7'
        Consuming 'message contents #8'
        Consuming 'message contents #9'

Notice that the following warnings are generated when compiling the above code:

Note: Main.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

The reason for this is that the ArrayBlockingQueue is cast to a BlockingQueue without a specified type parameter (Message). This means that type safety may be broken. In fact, we could simply insert the following line of code to produce a ClassCastException at runtime:

      BlockingQueue blockingQueue = new ArrayBlockingQueue<Message>(5);
 
      try { blockingQueue.put("hello"); } catch(InterruptedException e) { }

Solve this by specifying the type parameter:

      BlockingQueue<Message> blockingQueue = new ArrayBlockingQueue<Message>(5);

An example of the other methods of the ArrayBlockingQueue can be found here.

Catching exceptions in threads in JDK1.5

Sometimes, a thread may abruptly terminate, for example due to a NullPointerException. In large applications, a bug due to a thread that vanished just like that may be very hard to spot. In JDK1.5, there is a new inner interface Thread.UncaughtExceptionHandler that will make it a lot easier to detect exceptions.

When a thread is about to terminate because of an uncaught exception, the thread’s uncaughtExceptionHandler is called. If the thread’s uncaughtExceptionHandler is null, the thread’s ThreadGroup (which implements UncaughtExceptionHandler) is called. The ThreadGroup‘s uncaughtExceptionHandler calls the Thread DefaultUncaughtExceptionHandler if it is set otherwise it prints out the exception to System.err.

Excerpt from ThreadGroup.java:

    public void uncaughtException(Thread t, Throwable e) {
	if (parent != null) {
	    parent.uncaughtException(t, e);
	} else {
            Thread.UncaughtExceptionHandler ueh = 
                Thread.getDefaultUncaughtExceptionHandler();
            if (ueh != null) {
                ueh.uncaughtException(t, e);
            } else if (!(e instanceof ThreadDeath)) {
		System.err.print("Exception in thread ""
				 + t.getName() + "" ");
                e.printStackTrace(System.err);
            }
        }
    }

The following example forces a NullPointerException in a Thread which is dealt with with our own MyUncaughtExceptionHandler.

Main.java:

public class Main
{
   public static void main(String []args) {
      Thread t = new Task();
      t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
      t.start();
   }
}
 
class Task extends Thread
{
   private Object o = null;
 
   public void run() {
      o.toString();
   }
}
 
class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler
{
   public void uncaughtException(Thread t, Throwable e) {
      System.out.println("Exception occurred in Thread " + t);
 
      e.printStackTrace();
   }
}

Publishing a Websphere MQ ConnectionFactory in an LDAP

The following working code sample allows you to create a QueueConnectionFactory (MQQueueConnectionFactory) and register it as an administered object in an LDAP tree.

I used iPlanet Directory Server 5.0 to test it out.

RegisterWebsphereMQ.java:

import  com.ibm.mq.jms.MQQueueConnectionFactory;
import  javax.naming.*;
import  javax.jms.*;
 
import  java.util.*;
 
public class RegisterWebsphereMQ
{
   public static void main(String args[])
   {
      if (args.length != 5) {
         System.err.println("Usage: RegisterWebsphereMQ LDAP-url base queue-manager username password");
         System.err.println("  eg.:      ldapurl        ldap://127.0.0.1:389");
         System.err.println("            providername   ou=dev,ou=test,o=esus.com");
         System.err.println("            queue-manager  name of the queue manager, eg. MQS1");
         System.err.println("            username       uid=user,ou=People,o=esus.com");
         System.err.println("            password       pw");
         System.exit(1);
      }
      String ldapurl=args[0];
      String base=args[1];
      String queuemgr=args[2];
      String username=args[3];
      String password=args[4];
 
      try {
         Context context = getInitialContext(ldapurl, username, password);
 
         createLDAPObject(context, base, "QueueConnectionFactory");
         createQueueConnectionFactory(context, base, queuemgr);
 
         System.out.println("Registration successful");
      } catch(Exception e) {
         System.out.println("Registration failed: " + e);
         e.printStackTrace();
      }
   }
 
   public static Context getInitialContext(String ldapurl, String username, String password)
                            throws NamingException
   {
      Properties env=System.getProperties();
      env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
      env.put(Context.PROVIDER_URL, ldapurl);
      env.put(Context.SECURITY_PRINCIPAL, username);
      env.put(Context.SECURITY_CREDENTIALS, password);
 
      // Retrieve the initial context
      return new InitialContext(env);
   }
 
   public static void createLDAPObject(Context context, String base, String ouName)
                        throws NamingException
   {
      try {
         context.bind("ou=" + ouName + "," + base, new LDAPObject(ouName));
         System.out.println("Created OU [" + ouName + "]");
      }
      catch(NameAlreadyBoundException e) {
         System.out.println("Name " + ouName + " is already bound under " + base);
      }
   }
 
   public static void createQueueConnectionFactory(Context context, String base, String queuemgr)
                         throws NamingException, JMSException
   {
      QueueConnectionFactory qcf = new MQQueueConnectionFactory();
      ((MQQueueConnectionFactory) qcf).setQueueManager(queuemgr);
 
      // Bind the connection factory into the JNDI repository
      context.rebind("cn=queuefactory,ou=QueueConnectionFactory,"+base, qcf);
   }   
}

LDAPObject.java:

import javax.naming.*;
import javax.naming.directory.*;
import java.util.*;
 
class LDAPObject implements DirContext {
   Attributes attributes;
   String name;
    
   public LDAPObject(String name) {
      this.name = name;
 
      attributes = new BasicAttributes(true);
      // case ignore
      Attribute oc = new BasicAttribute("objectclass");
      oc.add("organizationalunit");
      oc.add("top");
 
      attributes.put(oc);
   }
 
   public Attributes getAttributes(String name) throws NamingException {
      if (!name.equals("")) {
         throw new NameNotFoundException();
      }
      return (Attributes) attributes.clone();
   }
 
   public Attributes getAttributes(Name name) throws NamingException {
      return getAttributes(name.toString());
   }
 
   public Attributes getAttributes(String name, String[] ids)
          throws NamingException {
      if (!name.equals("")) {
         throw new NameNotFoundException();
      }
 
      Attributes answer = new BasicAttributes(true);
      Attribute target;
      for (int i = 0; i < ids.length; i++) {
         target = attributes.get(ids[i]);
         if (target != null) {
            answer.put(target);
         }
      }
      return answer;
   }
 
   public Attributes getAttributes(Name name, String[] ids)
          throws NamingException {
      return getAttributes(name.toString(), ids);
   }
 
   public String toString() {
      return name;
   }
 
   public Object lookup(Name name) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public Object lookup(String name) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public void bind(Name name, Object obj) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public void bind(String name, Object obj) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public void rebind(Name name, Object obj) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public void rebind(String name, Object obj) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public void unbind(Name name) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public void unbind(String name) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public void rename(Name oldName, Name newName) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public void rename(String oldName, String newName) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public NamingEnumeration list(Name name) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public NamingEnumeration list(String name) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public NamingEnumeration listBindings(Name name) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public NamingEnumeration listBindings(String name) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public void destroySubcontext(Name name) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public void destroySubcontext(String name) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public Context createSubcontext(Name name) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public Context createSubcontext(String name) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public Object lookupLink(Name name) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public Object lookupLink(String name) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public NameParser getNameParser(Name name) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public NameParser getNameParser(String name) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public String composeName(String name, String prefix)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public Name composeName(Name name, Name prefix)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public Object addToEnvironment(String propName, Object propVal)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public Object removeFromEnvironment(String propName)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public Hashtable getEnvironment() throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public void close() throws NamingException {
      throw new OperationNotSupportedException();
   }
 
// -- DirContext
   public void modifyAttributes(Name name, int mod_op, Attributes attrs)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public void modifyAttributes(String name, int mod_op, Attributes attrs)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public void modifyAttributes(Name name, ModificationItem[] mods)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public void modifyAttributes(String name, ModificationItem[] mods)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public void bind(Name name, Object obj, Attributes attrs)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public void bind(String name, Object obj, Attributes attrs)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public void rebind(Name name, Object obj, Attributes attrs)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public void rebind(String name, Object obj, Attributes attrs)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public DirContext createSubcontext(Name name, Attributes attrs)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public DirContext createSubcontext(String name, Attributes attrs)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public DirContext getSchema(Name name) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public DirContext getSchema(String name) throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public DirContext getSchemaClassDefinition(Name name)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public DirContext getSchemaClassDefinition(String name)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public NamingEnumeration search(Name name,
         Attributes matchingAttributes,
         String[] attributesToReturn)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public NamingEnumeration search(String name,
         Attributes matchingAttributes,
         String[] attributesToReturn)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public NamingEnumeration search(Name name,
         Attributes matchingAttributes)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public NamingEnumeration search(String name,
         Attributes matchingAttributes)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public NamingEnumeration search(Name name,
         String filter,
         SearchControls cons)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public NamingEnumeration search(String name,
         String filter,
         SearchControls cons)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public NamingEnumeration search(Name name,
         String filterExpr,
         Object[] filterArgs,
         SearchControls cons)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public NamingEnumeration search(String name,
         String filterExpr,
         Object[] filterArgs,
         SearchControls cons)
          throws NamingException {
      throw new OperationNotSupportedException();
   }
 
   public String getNameInNamespace() throws NamingException {
      throw new OperationNotSupportedException();
   }
}

Sample run:

C:myldap>java RegisterWebsphereMQ ldap://localhost:2000 ou=dev,ou=epais,o=philips.com MQS1 uid=user,ou=People,o=esus.com pw
Created OU [QueueConnectionFactory]
Registration successful

Using the ClassFileTransformer in JDK 1.5

The package java.lang.instrument allows you to modify class classfiles as they are loaded. On the command line, you register your own implementation of the ClassFileTransformer and this will be called by the VM every time a class is loaded.

On this page, you’ll find a working example of a ClassFileTransformer that will add a log statement to every method of every class that is about to be loaded. To perform the bytecode manipulation, I used the well-known Jakarta BCEL library.

Two related Q&A’s to achieve this kind of functionality are
How do I get started with writing a dynamic proxy class?
How do I get started with AspectJ?

MethodInstrument.java:

import java.lang.instrument.Instrumentation;
import java.security.*;
  
import org.apache.bcel.Constants;
import org.apache.bcel.Repository;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.InstructionConstants;
import org.apache.bcel.generic.InstructionFactory;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.PUSH;
import org.apache.bcel.generic.Type;
 
import java.io.*;
import java.lang.reflect.*;
 
public class MethodInstrument 
{
   public static void premain(String options, Instrumentation instrumentation) {
      instrumentation.addTransformer(new EntryExitMethodTransformer());
   }
   
   public static class EntryExitMethodTransformer implements ClassFileTransformer 
   {
      public byte[] transform(ClassLoader loader, String cn, Class classBeingRedefined, 
                              ProtectionDomain protectionDomain, byte[] classfileBuffer) 
                                  throws IllegalClassFormatException
      {
         //System.out.println("Transforming class " + cn);
         if (cn.startsWith("java") || cn.startsWith("javax") || cn.startsWith("sun")) {
            return classfileBuffer;
         }
         try {
            ByteArrayInputStream bais = new ByteArrayInputStream(classfileBuffer);
             
            ClassParser parser = new ClassParser(bais, cn);
            JavaClass clazz = parser.parse();
        
            ClassGen classGen = new ClassGen(clazz);
        
            Method[] methods = clazz.getMethods();
            for (int i=0; i<methods.length; i++) {
               InstructionFactory instructionFactory = new InstructionFactory(classGen);
               InstructionList instructionList = new InstructionList();
               ConstantPoolGen constantPoolGen = classGen.getConstantPool();
               String className = classGen.getClassName();            
  
               Method method = methods[i];
               MethodGen wrapGen = new MethodGen(method, className, constantPoolGen);
               instructionList = wrapGen.getInstructionList();
                
               String text = "Call to method " + cn + "." + method.getName();
               instructionList.insert(instructionFactory.createInvoke(
                        "java.io.PrintStream", "println"
                        , Type.VOID, new Type[] { Type.STRING }
                        , Constants.INVOKEVIRTUAL
                      ));
               instructionList.insert(new PUSH(constantPoolGen, text));
               instructionList.insert(instructionFactory.createFieldAccess(
                        "java.lang.System", "out", new ObjectType("java.io.PrintStream")
                        , Constants.GETSTATIC
                      ));
   
               wrapGen.stripAttributes(true);
               wrapGen.setMaxStack();
               wrapGen.setMaxLocals();
      
               classGen.removeMethod(method);
               classGen.addMethod(wrapGen.getMethod());
            }
    
            return classGen.getJavaClass().getBytes();
         }
         catch(Exception e) {
            throw new IllegalClassFormatException(e.getMessage());
         }
      }
   }
}

Be sure to include the BCEL library in your classpath (eg. bcel-5.1.jar).

A simple program to test our EntryMethodTransformer:

Test.java:

public class Test
{
   public static void main(String []args) {
      a();
   }
   
   public static void a() {
      b();
   }
   
   public static void b() {
      c();
   }
   
   public static void c() {
      System.out.println("C reached!");
   }
}

Now run Test, but provide the MethodInstrument agent on the command line:

java -javaagent:MethodInstrument Test

outputs:

Call to method Test.main
Call to method Test.a
Call to method Test.b
Call to method Test.c
C reached!

Get Started with AspectJ

  1. Download AspectJ
  2. Run java -jar aspectj-[version].jar and perform the installation.
  3. Add the bin directory of to your path to make it easier to run the AsjpectJ compiler ajc.
  4. Add aspectjrt.jar to your classpath.
  5. The goal of this example is to extend this simple class with logging functionality. Every time a method is called or exited, a message must be logged to stdout.

    Test.java:

    public class Test
    {
       public static void main(String []args) {
          a();
       }
       
       public static void a() {
          b();
       }
       
       public static void b() {
          c();
       }
       
       public static void c() {
          System.out.println("C reached!");
       }
    }
    
  6. Running this code yields:
    C reached!
    
  7. Now write the Logging Aspect.

    TraceAspect.java:

    import org.aspectj.lang.*;
     
    public aspect TraceAspect
    {
       pointcut traceMethods() 
          : (execution(* *.*(..))
            || execution (*.new(..))) && !within(TraceAspect);
             
       before() : traceMethods() {
          Signature signature = thisJoinPointStaticPart.getSignature();
            
          System.out.println("Entering method " + signature.getDeclaringType().getName() + "." +
                                                  signature.getName());
       }
     
       after() : traceMethods() {
          Signature signature = thisJoinPointStaticPart.getSignature();
          
          System.out.println("Exiting method " + signature.getDeclaringType().getName() + "." +
                                                 signature.getName());
       }
    }
    
  8. Compile both the .java files with the ajc:
    ajc *.java
    
  9. Running Test again results in the logging statements.
    Entering method Test.main
    Entering method Test.a
    Entering method Test.b
    Entering method Test.c
    C reached!
    Exiting method Test.c
    Exiting method Test.b
    Exiting method Test.a
    Exiting method Test.main
    
  10. Notice that the original code in Test.java was not touched. But if you decompile Test.class with Jad, you’ll see why Test behaves the way it does, with the logging:
    // Decompiled by Jad v1.5.6d. Copyright 1997-99 Pavel Kouznetsov.
    // Jad home page: http://www.geocities.com/SiliconValley/Bridge/8617/jad.html
    // Decompiler options: packimports(3) 
    // Source File Name:   Test.java
     
    import java.io.PrintStream;
    import org.aspectj.runtime.reflect.Factory;
     
    public class Test
    {
        public Test()
        {
            try
            {
                TraceAspect.aspectOf().ajc$before$TraceAspect$1$b314f86e(ajc$tjp_0);
            }
            catch(Throwable throwable)
            {
                TraceAspect.aspectOf().ajc$after$TraceAspect$2$b314f86e(ajc$tjp_0);
                throw throwable;
            }
            TraceAspect.aspectOf().ajc$after$TraceAspect$2$b314f86e(ajc$tjp_0);
        }
     
        public static void main(String args[])
        {
            try
            {
                TraceAspect.aspectOf().ajc$before$TraceAspect$1$b314f86e(ajc$tjp_1);
                a();
            }
            catch(Throwable throwable)
            {
                TraceAspect.aspectOf().ajc$after$TraceAspect$2$b314f86e(ajc$tjp_1);
                throw throwable;
            }
            TraceAspect.aspectOf().ajc$after$TraceAspect$2$b314f86e(ajc$tjp_1);
        }
     
        public static void a()
        {
            try
            {
                TraceAspect.aspectOf().ajc$before$TraceAspect$1$b314f86e(ajc$tjp_2);
                b();
            }
            catch(Throwable throwable)
            {
                TraceAspect.aspectOf().ajc$after$TraceAspect$2$b314f86e(ajc$tjp_2);
                throw throwable;
            }
            TraceAspect.aspectOf().ajc$after$TraceAspect$2$b314f86e(ajc$tjp_2);
        }
     
        public static void b()
        {
            try
            {
                TraceAspect.aspectOf().ajc$before$TraceAspect$1$b314f86e(ajc$tjp_3);
                c();
            }
            catch(Throwable throwable)
            {
                TraceAspect.aspectOf().ajc$after$TraceAspect$2$b314f86e(ajc$tjp_3);
                throw throwable;
            }
            TraceAspect.aspectOf().ajc$after$TraceAspect$2$b314f86e(ajc$tjp_3);
        }
     
        public static void c()
        {
            try
            {
                TraceAspect.aspectOf().ajc$before$TraceAspect$1$b314f86e(ajc$tjp_4);
                System.out.println("C reached!");
            }
            catch(Throwable throwable)
            {
                TraceAspect.aspectOf().ajc$after$TraceAspect$2$b314f86e(ajc$tjp_4);
                throw throwable;
            }
            TraceAspect.aspectOf().ajc$after$TraceAspect$2$b314f86e(ajc$tjp_4);
        }
     
        private static final org.aspectj.lang.JoinPoint.StaticPart ajc$tjp_0;
        private static final org.aspectj.lang.JoinPoint.StaticPart ajc$tjp_1;
        private static final org.aspectj.lang.JoinPoint.StaticPart ajc$tjp_2;
        private static final org.aspectj.lang.JoinPoint.StaticPart ajc$tjp_3;
        private static final org.aspectj.lang.JoinPoint.StaticPart ajc$tjp_4;
     
        static 
        {
            Factory factory = new Factory("Test.java", Class.forName("Test"));
            ajc$tjp_0 = factory.makeSJP("constructor-execution", 
                                        factory.makeConstructorSig("1--Test----"), 1);
            ajc$tjp_1 = factory.makeSJP("method-execution",
                                        factory.makeMethodSig("9-main-Test-[Ljava.lang.String;:-args:--void-"), 4);
            ajc$tjp_2 = factory.makeSJP("method-execution",
                                        factory.makeMethodSig("9-a-Test----void-"), 8);
            ajc$tjp_3 = factory.makeSJP("method-execution",
                                        factory.makeMethodSig("9-b-Test----void-"), 12);
            ajc$tjp_4 = factory.makeSJP("method-execution",
                                        factory.makeMethodSig("9-c-Test----void-"), 16);
        }
    }
    

Get the current process ID in JDK 1.5

The following example is platform-dependent and was only tested with Sun’s JVM 1.5.0 on Windows XP. It first shows you how to get it using the standard management API’s and then using some reflection tricks and the undocumented sun.* classes.

Main.java (Using RuntimeMBean):

import java.lang.management.*;

public class Main
{
   public static void main(String []args) {
      RuntimeMBean mbean = ManagementFactory.getRuntimeMBean();
      String name = mbean.getName();
       
      System.out.println(name);
   }
}

Main.java (Using reflection):

import sun.management.*;
import java.lang.reflect.*;

public class Main
{
   public static void main(String []args) throws Exception {
      int pid = getProcessId();
      System.out.println("Current processID: " + pid);
       
      try {
         Thread.sleep(10000);
      }
      catch(Exception e) { }
   }
    
   public static int getProcessId() throws Exception {
      // next line to trigger loading of library
      ManagementFactory.getThreadMXBean();
       
      Class c = Class.forName("sun.management.VMManagementImpl");
      //VMManagementImpl impl = new VMManagementImpl();
         
      Constructor constructor = c.getDeclaredConstructor(new Class[] { });
      constructor.setAccessible(true);
      Object impl = constructor.newInstance(new Object[] { });
       
      Method m = c.getDeclaredMethod("getProcessId", new Class[] { });
      m.setAccessible(true);
      Object result = m.invoke(impl, new Object[] { });
      
      return (Integer) result;
   }
}

Note that I’ve written a DLL / so before to get the process ID using JNI in all JDK versions.
It can be found here.

Decompiling JDK 1.5 classes

jad expects class file versions (major.minor) to be 45.3 or 46.0. Sources compiled with 1.5 have the file version 48.0. The following error message is displayed when trying to decompile a 1.5 .class file:

Parsing RequestForEnhancement.class...The class file version is 48.0, expected 45.3 or 46.0
JavaClassFileParseException: Class file version mismatch

To be able to use JAD, we can simply change the class file version of a 1.5 compiled class into 46. The following small application sets the magic number of a .class file or all classes in a jar file to whatever you desire.

ChangeMagicNumber.java:

import java.io.*;
import java.util.*;
import java.util.jar.*;
import java.util.zip.*;
 
public class ChangeMagicNumber
{
   public static void main(String []args) throws Exception {
      if (args.length != 2) {
         System.out.println("Usage: java ChangeMagicNumber [file.class | file.jar] magicNumber (eg. 46)");
         System.exit(1);
      }
       
      String filename = args[0];
      byte desiredMagicNumber = (byte) Integer.parseInt(args[1]);
      if (filename.toLowerCase().endsWith(".jar")) {
         processJar(filename, desiredMagicNumber);
      }
      else {
         processFile(filename, desiredMagicNumber);
      }
   }
    
   public static void processFile(String filename, byte desiredMagicNumber) throws Exception {
      RandomAccessFile raf = new RandomAccessFile(filename, "rw");
      raf.seek(7);
      byte b = raf.readByte();
      if (b != desiredMagicNumber) {
         raf.seek(7);
         raf.writeByte((byte) desiredMagicNumber);
      }
      raf.close();
   }
    
   public static void processJar(String filename, byte desiredMagicNumber) throws Exception {
      JarFile jf = new JarFile(filename);
      JarOutputStream jos = new JarOutputStream(new FileOutputStream(filename + "." + desiredMagicNumber));
      CRC32 crc = new CRC32();
      Enumeration enum = jf.entries();
      while (enum.hasMoreElements()) {
         ZipEntry entry = (ZipEntry) enum.nextElement();
         BufferedInputStream bis = new BufferedInputStream(jf.getInputStream(entry));
         int length = bis.available();
         byte[] buffer = null;
         if (length > 0) {
            buffer = new byte[length];
            bis.read(buffer, 0, length);
            
            buffer[7] = desiredMagicNumber;
            crc.reset();
            crc.update(buffer, 0, length);
            entry.setCrc(crc.getValue());
         }
         
         String e = entry.toString();
         jos.putNextEntry(entry);
         if (length > 0) {
            jos.write(buffer, 0, length);
         }
      }
      jf.close();
      jos.close();
   }
}

For example:

java ChangeMagicNumber rt.jar 46  

produces rt.jar.46, all classes in JAR file can be decompiled with JAD

Now the decompilation of the following 1.5 enum-example source works fine, after changing its file class version to 46.

Main.java:

enum DestinationType
{
   queue,
   topic
}
 
public class Main
{
   public static void main(String []args) {
      DestinationType dt = DestinationType.queue;

      // Outputting an enumerated type is user-friendly
      System.out.println(dt);   // prints out "queue"
      
      // Switch can be used on an enumerated type, no need to qualify the name of the constant
      switch (dt) {
         case queue:  System.out.println("Using a queue");
                      break;
         case topic:  System.out.println("Using a topic");
                      break;
         default:
      }
   }
}

Execute:

   java ChangeMagicNumber DestinationType.class 46
   jad DestinationType.class

results in:

DestinationType.jad:

// Decompiled by Jad v1.5.6d. Copyright 1997-99 Pavel Kouznetsov.
// Jad home page: http://www.geocities.com/SiliconValley/Bridge/8617/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   Main.java
 
native class DestinationType extends Enum
{
    public static final DestinationType[] values()
    {
        return (DestinationType[])_2B_VALUES.clone();
    }
 
    public static DestinationType valueOf(String s)
    {
        DestinationType adestinationtype[] = _2B_VALUES;
        int i = adestinationtype.length;
        for(int j = 0; j < i; j++)
        {
            DestinationType destinationtype = adestinationtype[j];
            if(destinationtype.name().equals(s))
                return destinationtype;
        }
 
        throw new IllegalArgumentException(s);
    }
 
    DestinationType(String s, int i)
    {
        super(s, i);
    }
 
    public volatile int compareTo(Enum enum)
    {
        return super.compareTo((DestinationType)enum);
    }
 
    public static final native DestinationType queue;
    public static final native DestinationType topic;
    private static final DestinationType _2B_VALUES[] = {
        queue, topic
    };
 
    static 
    {
        queue = new DestinationType("queue", 0);
        topic = new DestinationType("topic", 1);
    }
}

Get a verbose output of the classes loaded by the VM

With JDK 1.5 java.lang.management package:

Main.java:

import java.lang.management.*;
 
public class Main
{
   public static void main(String []args) {
      ClassLoadingMBean mbean = ManagementFactory.getClassLoadingMBean();
      mbean.setVerbose(true);
       
      A a = new A();
   }
}
 
class A
{
}

outputs:

[Loaded A from file:/C:/my/jdk1.5/classloadingmbean/verbose/]
[Loaded java.lang.Shutdown from shared objects file]
[Loaded java.lang.Shutdown$Lock from shared objects file]