Hessian Client Extender With Dynamic Proxy Inside OSGi
III- Extender bundle listener
1- Listen to updated and installed bundle2- Check the MANIFEST.MF file
3- Get the url and parse an XML resource
4- Register a proxy service
package com.jtunisie.osgi.hessian.client;
import com.jtunisie.osgi.hessian.client.Parser.Pair;
import java.net.URL;
import java.util.Dictionary;
import java.util.List;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.SynchronousBundleListener;
import static org.osgi.framework.BundleEvent.*;
/**
*
* @author slim
*/
public class BundleListner implements SynchronousBundleListener {
private static final String HESSIAN_HEADER = "Hessian-File";
private final BundleContext context;
public BundleListner(BundleContext context) {
this.context = context;
}
@Override
public void bundleChanged(BundleEvent event) {
if (event.getType() == UPDATED || event.getType() == STARTED) {
try {
addBundle(event.getBundle());
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
}
if (BundleEvent.STOPPED == event.getType()) {
removeBundle(event.getBundle());
}
}
private void addBundle(Bundle bundle) throws ClassNotFoundException {
@SuppressWarnings("unchecked")
Dictionary<String, String> headers = bundle.getHeaders();
String indexPath = headers.get(HESSIAN_HEADER);
if (indexPath == null) {
return;
}
URL resource = bundle.getResource(indexPath);
if (resource == null) {
return;
}
List<Pair> remotes = Parser.parseRemoteconfig(resource);
if (remotes != null) {
for (Pair pair : remotes) {
pair.setServiceRegistration(new ClientExtender().registerService(context, pair));
}
}
}
private void removeBundle(Bundle bundle) {
// TODO
}
}
IV-DynamicImport-Package :
As this extender will manipulate unknown class and interfaces we must enable DynamicImport-Package to all packages. To do this, add <DynamicImport-Package>*</DynamicImport-Package> in pom file.
V- Dynamic Proxys
1 -Create generic proxy class GenericClientProxy to forward calls to remote implementation according to requested service and destination:
package com.jtunisie.osgi.hessian.client;
import com.caucho.hessian.client.HessianProxyFactory;
import com.jtunisie.osgi.hessian.client.Parser.Pair;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
*
* @author slim
*/
public class GenericClientProxy implements InvocationHandler, Serializable {
private Pair pair;
public GenericClientProxy(Pair pair) throws ClassNotFoundException {
this.pair = pair;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
HessianProxyFactory factory = new HessianProxyFactory();
Serializable service = (Serializable) factory.create(pair.getClazz(), pair.getRemoteAdress());
return method.invoke(service, args);
}
}
2- The ClientExtender class will register this proxy inside local OSGi as if it is present.
package com.jtunisie.osgi.hessian.client;
import com.jtunisie.osgi.hessian.client.Parser.Pair;
import java.lang.reflect.Proxy;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
/**
*
* @author slim
*/
public class ClientExtender {
ServiceRegistration registerService(BundleContext context, Pair pair) throws ClassNotFoundException {
String _interface = pair.getRemoteInterface();
Class[] interfaces = {pair.getClazz()};
GenericClientProxy clientProxy = new GenericClientProxy(pair);
Object newProxyInstance = Proxy.newProxyInstance(this.getClass().getClassLoader(), interfaces, clientProxy);
return context.registerService(_interface, newProxyInstance, null);
}
void unregisterService(BundleContext context, Pair pair) {
//TODO
}
}
VI- Execution
If we run the extender bundle and a test client, we will see that our extender will register the remote service inside the local registryosgi> b <extender-id>
........
Id=41, Status=ACTIVE Data Root=/home/slim/work/svn/hessianextender-read-only/env.client/configuration/org.eclipse.osgi/bundles/41/data
Registered Services
{com.jtunisie.osgi.hessian.IService}={service.id=47}
No services in use.
Exported packages
com.caucho.hessian.security; version="0.0.0"[exported]
com.caucho.hessian.io; version="0.0.0"[exported]
com.caucho.hessian.client; version="0.0.0"[exported]
com.caucho.hessian.server; version="0.0.0"[exported]
com.caucho.hessian.jmx; version="0.0.0"[exported]
com.caucho.hessian.mux; version="0.0.0"[exported]
com.caucho.hessian.util; version="0.0.0"[exported]
com.caucho.hessian.micro; version="0.0.0"[exported]
com.caucho.hessian; version="0.0.0"[exported]
com.caucho.hessian.test; version="0.0.0"[exported]
Imported packages
javax.crypto; version="0.0.0"<System Bundle [0]>
javax.management; version="0.0.0"<System Bundle [0]>
javax.naming; version="0.0.0"<System Bundle [0]>
javax.naming.spi; version="0.0.0"<System Bundle [0]>
javax.servlet; version="2.5.0"<initial@reference:file:plugins/web/pax-web-service-0.4.1.jar/ [37]>
javax.servlet.http; version="2.5.0"<initial@reference:file:plugins/web/pax-web-service-0.4.1.jar/ [37]>
javax.xml.namespace; version="0.0.0"<System Bundle [0]>
javax.xml.stream; version="0.0.0"<System Bundle [0]>
javax.xml.stream.events; version="0.0.0"<System Bundle [0]>
org.osgi.framework; version="1.4.0"<System Bundle [0]>
org.w3c.dom; version="0.0.0"<System Bundle [0]>
No fragment bundles
Named class space
com.jtunisie.osgi.hessian.client; bundle-version="1.0.0"[provided]
No required bundles
VII - Conclusion
Using extenders in OSGi is a powerful tool. This post outlines :
Extender pattern.
Bundle listener.
Dynamic proxy for remoting.
Stax XML parsing
Hessian service client.
It's not for production :) but as didactic use. If you want a powerful api check Eclipse Riena.
The Source code is available under svn link : svn checkout http://hessianclient.googlecode.com/svn/trunk/ hessianclient-read-only
(1 vote)
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)





Comments
Slim Ouertani replied on Wed, 2009/04/15 - 3:13am