Browse DevX
Sign up for e-mail newsletters from DevX


Distribute JavaBean Events Across Java Virtual Machines : Page 3

The JavaBean event model provides simple messaging, but it works only within a Java virtual machine (JVM). JMS allows you to send messages across a network to other JVMs, but it is quite complex. This article presents a quicker, simpler solution: transparently distributing JavaBean events over JMS.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Using Java Reflection
While JMS does the actual work in distributing JavaBean events, Java reflection makes it possible and transparent. Using reflection, the Event Distributor "intercepts" events by creating interface proxy classes. Reflection also enables it to receive the events by discovering the interfaces implemented by event listener components. The Event Distributor also dissects and wraps the events themselves, again using reflection.

All of this happens transparently, and it is non-intrusive to the source and listener components. It's a real showcase of the power of Java and reflection.

The Proxy Class
The java.lang.reflect library contains the Proxy class and InvocationHandler interface. With these, your code can at runtime assemble classes that implement one or more interfaces, as shown in Listing 5.

Listing 5. Create an Interface Proxy
With the following code, you can create an object at runtime that implements any valid interface:

protected Object createProxy(String interfaceName) { try { InvocationHandler handler = new EventInvocationHandler(); Class eventInterface = Class.forName( interfaceName ); // Use the Java API's Proxy class Object prxy = Proxy.newProxyInstance( eventInterface.getClassLoader(), new Class[] { eventInterface }, handler ); return prxy; } catch ( Exception e ) { return null; } }

First, create an instance of a class that implements the interface InvocationHandler (described in the next section). Basically, this class fields the method calls on the interfaces being proxied. Next, create a Class object for the interface being proxied using the interface name. Finally, call the Proxy.newProxyInstance method to create the proxy object itself.

The call to Proxy.newProxyInstance takes the following parameters:

  • A ClassLoader object
  • An array of Class objects, one for each interface being implemented
  • A reference to an object that implements the InvocationHandler interface

The end result is an object that implements the supplied interfaces. In the case of the Event Distributor, this object is given to the event source object via the addListener method. The InvocationHandler object supplied when creating the proxy handles all method calls on the Proxy object.

The InvocationHandler Interface
An invocation handler is an object that implements the InvocationHandler interface. As discussed in the previous section, a reference to such an object is used in creating an interface proxy object. When a proxy object calls methods, the invocation handler's invoke method fields them. The invoke method provides three parameters: the proxy object on which the call was made, the actual method that was called, and the method's arguments.

Listing 6 shows the code for a sample invoke method. The first step is to get the Class object for the proxy by calling proxy.getClass. Next, call getInterfaces on the proxy Class object to retrieve the implemented interfaces as an array of Class objects.

Listing 6. Invoking a Method on a Proxy Object
The following code is an implementation of the invoke method defined in Java's InvocationHandler interface. Java will call this method to provide you with a reference to your proxy object, the method that was called, and the method's arguments:

public Object invoke(Object proxy, Method mthd, Object[] args) throws Throwable { try { Class proxyClass = proxy.getClass(); Class[] interfaces = proxyClass.getInterfaces(); String interfaceName = interfaces[0].getName(); String methodName = mthd.getName(); Class[] argTypes = mthd.getParameterTypes(); log(" interface=" + interfaceName ); log(" method=" + methodName ); log(" parameters:"); for ( int p = 0; p < argTypes.length; p++ ) { log(" Param " + (p + 1) + ": " + argTypes[p].getName() + ", val=" + args[p]); } } catch ( Exception e ) { // ... } }

You can prove that the proxy's interface truly implemented the invoked method by calling proxyClass.getMethods. Iterating through the returned Method object array should find a match.

Continuing with this example, the invoked method name is retrieved via a call to Method.getName. A call to Method.getParameterTypes then retrieves the argument types for the invoked method. The result is an array of Class objects, one for each argument of the method call.

By now I'm sure you've noticed a pattern to using Java reflection, which allows you to dive deeper into classes, methods, argument types, and argument values. The remainder of the example uses this pattern to output the details of the method call, the parameters, and the parameter types.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date