Wednesday, March 19, 2008

Best Commercial Equinox Application: Cyrano

Congratulations to Band XI on their Best Commercial Equinox Application victory with Cyrano. See all the winners here. Here's a photo of John accepting the award, but where's Brett and Patrick?

Wednesday, March 12, 2008

More Equinox Command Provider Enhacements

The bundle org.eclipse.soda.sat.equinox.console.cmdprov extends the Equinox console by registering four org.eclipse.osgi.framework.console.CommandProvider services.

  • Bundle Dependencies: These commands allow you to query the dependency and prerequisite relationships that exist between bundles.

  • Configuration Admin: These commands allow you to query the ConfigurationAdmin service for configuration PIDs, factory PIDs and configuration properties. This command provider requires a ConfigurationAdminservice to be registered.

  • Logging: These commands let you query and set the log level, and query and control whether tracing is on or off.

  • Missing Imported Services of Bundles and Configurations: These commands allow you to query the missing required and optional imported services of SAT bundles and configurations created by ConfigurationAdmin.

To use the Equinox console, launch the OSGi framework with the -console program argument, which is present by default for an OSGi Framework launch configuration. Typing help at the Equinox console will display the following additional command usage information:

---Bundle Dependencies---
adep - show all dependents of the specified bundle
apre - show all prerequisites of the specified bundle
dep - show dependents the specified bundle
pre - show prerequisites of the specified bundle
---Configuration Admin---
cprops [pid] - display the configuration properties
fpids [filter] - display the factory pids
pids [filter] - display the pids
---Logging---
logLevel [(debug|info|warning|error)] - query and control log level
trace [(on|off)] - query and control tracing
---Missing Imported Services of Bundles and Configurations---
ams [id] - show all missing imported services
mos [id] - show missing optional imported services
mrs [id] - show missing required imported services

Friday, March 7, 2008

SAT Support Proxy Service Equality

Previous posts talked about the problem of comparing the equality of two proxies, and a solution that involves unwrapping each proxy before performing the equality test. Today's post is short and sweet in that it simply shows the code to test SAT's support for proxy equality. The following snippet of code creates a proxied service and compares it with itself.

FactoryUtility utility = FactoryUtility.getInstance();
BundleContext context = getBundleContext();
IProxyServiceHandler handler = new ProxyServiceHandlerAdapter() {
public Object createService() {
return new HotdogVendor();
}
};
IServiceRecord record = utility.createExportProxyServiceRecord(context, VendorService.class, handler, null);
Object service = record.getService();
boolean equal = service.equals(service);
System.out.println("service.equals(service) = " + equal);

The console output is, as follows:

service.equals(service) = true

Thursday, March 6, 2008

Supporting Proxy Equality

Yesterday's post talked about how using the equals(Object) method to compare two Proxy objects always evaluates to false, even when the receiver and the parameter are the same instance. Today's topic is how to implement the InvocationHandler interface such that the equals(Object) method is handled correctly.

The solution is to intercept the invocation of the equals(Object) method and invoke it with an unwrapped receiver and parameter. Here's how...

public class MyInvocationHandler extends Object implements InvocationHandler {
private Object object;

public MyInvocationHandler(Object object) {
super();
this.object = object;
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
String name = method.getName();
if (name.equals("equals") && args.length == 1) {
MyInvocationHandler handler = toMyInvocationHandler(args [ 0 ]);
if (handler != null) {
Object[] parameters= new Object[] {
handler.object
};
result = method.invoke(object, parameters);
}
}

if (result == null) {
result = method.invoke(object, args);
}

return result;
}

private MyInvocationHandler toMyInvocationHandler(Object parameter) {
InvocationHandler handler = Proxy.getInvocationHandler(parameter);
boolean valid = handler instanceof MyInvocationHandler;
if (valid == false) return null;
MyInvocationHandler myHandler = (MyInvocationHandler) handler;
return myHandler;
}
}

Rather than anonymously implementing the InvocationHandler interface, we have the class MyInvocationHandler. The object field is used to store the object that is being proxied.

We must intercept the invocation of the equals(Object) method, and where the parameter is an instance of MyInvocationHandler, we must invoke the Method, passing as a parameter the MyInvocationHandler instance's object field.

This technique of unwrapping the parameter Proxy only works when the proxy is one that has an invocation handler that is an instance of MyInvocationHandler. In cases where the parameter is not an appropriate proxy, execute continues as normal in the invocation handler.

Wednesday, March 5, 2008

Question: When is an object not equal to itself?

Most developers would agree that the expression object.equal(object) aways evaluates to true, and I thought so too until a short while ago where I found that it can sometimes evaluate to false. Trust me, it can happen. Consider the following code:
  public interface IPerson {
//...
};

private class Person extends Object implements IPerson {
//...
};
The Person class inherits its equals(Object) method from Object, the implementation of which is:
  public boolean equals (Object object) {
return this == object;
}
So if the expression object == object always evaluates to true, and yes, it always evaluates to true, how could it be possible for the expression object.equals(object) to ever evaluate to false?

Answer: When it's a proxy. This is a serious problem, since in most cases where proxies are used the fact that you're using a Proxy should not be something you need to think about. Consider the following code that creates a proxy and then compares it with itself:
  final IPerson person = new Person();

ClassLoader classLoader = Person.class.getClassLoader();
Class[] interfaces = {
IPerson.class
};
InvocationHandler handler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(person, args);
}
};
Object object = Proxy.newProxyInstance(classLoader, interfaces, handler);

boolean equal = object.equals(object);
System.out.println("object.equals(object): " + equal);

boolean identical = object == object;
System.out.println("object == object: " + identical);
This code outputs the following to the console:
  object.equals(object): false
object == object: true
When I first saw this I could not believe my eyes, thinking that it must be a bug in either the JVM or the implementation of the Proxy class, when of course it was neither. The problem lies in the anonymous inner-class implementation of the InvocationHandler interface:
  InvocationHandler handler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(person, args);
}
};
The receiver is always a Proxy, so when we evaluate object.equals(object), execution quickly jumps to the handler's invoke method, where the expression method.invoke(person, args) is evaluated. The trick is to remember that this line of code is equivalent to:
  object.equals(person)
This evaluates to false because:
  • The object is an instance of Proxy.
  • The person is an instance of IPerson.
  • Both instances are distinct objects.
  • The Proxy class inherits its equals(Object) method from Object.
  • The expression object == person evaluate to false.

It is unfortunate that the Proxy class does not take care of ensuring that the equals(Object) method just works as you would expect, but sadly this is not the case. If you want it to work, and you really do want it to work, your InvocationHandler must be smarter. But I'm saving that for another day.

Planet Eclipse

Jazz Community News