/**
* Created on Mar 13, 2006
*
* $Id$
* $Revision$
*/
package org.springmodules.jini;
import java.io.IOException;
import java.rmi.RemoteException;
import net.jini.core.entry.Entry;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.lookup.ServiceTemplate;
import net.jini.discovery.DiscoveryEvent;
import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.LookupDiscovery;
import net.jini.lookup.entry.Name;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.core.NestedRuntimeException;
import org.springframework.remoting.RemoteAccessException;
import org.springframework.remoting.RemoteLookupFailureException;
/**
* JiniServiceFactoryBean for JINI environments. The class is made up from various samples found on jini.org
* customized in a Spring specific way. The search will be executed using the provided ServiceTemplate
* or, if it is null, one will be created using the serviceClass and serviceName. If the lookup operation
* times out (30 seconds by default), a null service will be returned.
* For most cases the serviceClass and serviceNames are enought and hide the jini details from the client.
* <p/>
* The factoryBean can be configured to do a lookup each time before returning the object type by setting
* the "singleton" property to false.
*
* @author Costin Leau
*
*/
public class JiniServiceFactoryBean extends AbstractFactoryBean {
private static final Log log = LogFactory.getLog(JiniServiceFactoryBean.class);
private ServiceTemplate template;
// utility properties
private Class serviceClass;
private String serviceName;
private String[] groups = LookupDiscovery.ALL_GROUPS;
// 30 secs
private long timeout = 30 * 1000;
// used to pass out information from inner classes
private Object proxy;
/**
* @see org.springmodules.beans.factory.FactoryBean#getObjectType()
*/
public Class getObjectType() {
// try to discover the class type if possible to make it work with autowiring
if (proxy == null) {
// no template - look at serviceClass
if (template == null) {
return (serviceClass == null ? null : serviceClass);
}
// look at the template and
// return the first class from the template (if there is one)
if (template.serviceTypes != null && template.serviceTypes.length > 0)
return template.serviceTypes[0];
}
return proxy.getClass();
}
/**
* @see org.springmodules.beans.factory.config.AbstractFactoryBean#createInstance()
*/
protected Object createInstance() throws Exception {
ServiceTemplate templ;
if (template == null) {
Class[] types = (serviceClass == null ? null : new Class[] { serviceClass });
Entry[] entry = (serviceName == null ? null : new Entry[] { new Name(serviceName) });
templ = new ServiceTemplate(null, types, entry);
}
else
templ = template;
final ServiceTemplate finalTemplate = templ;
LookupDiscovery lookupDiscovery = null;
try {
lookupDiscovery = new LookupDiscovery(groups);
// hook listener for finding the service
lookupDiscovery.addDiscoveryListener(new DiscoveryListener() {
public void discovered(DiscoveryEvent ev) {
log.debug("received discovery event " + ev);
ServiceRegistrar[] reg = ev.getRegistrars();
// once the proxy if found, bail out
for (int i = 0; i < reg.length && proxy == null; i++) {
findService(finalTemplate, reg[i]);
}
}
public void discarded(DiscoveryEvent ev) {
}
});
if (log.isDebugEnabled())
log.debug("Awaiting discovery event...");
if (proxy == null) {
synchronized (this) {
this.wait(timeout);
}
}
if (log.isDebugEnabled())
log.debug("Terminating discovery service");
}
catch (IOException e) {
throw new RemoteLookupFailureException("cannot create lookup discovery", e);
}
catch (InterruptedException e) {
throw new NestedRuntimeException("lookup interrupted", e) {
};
}
finally {
// make sure to close the lookup threads
if (lookupDiscovery != null)
lookupDiscovery.terminate();
}
return proxy;
}
/**
* Find the service and notify once it is found.
*
* @param templ
* @param lus
*/
private void findService(ServiceTemplate templ, ServiceRegistrar lus) {
try {
synchronized (this) {
proxy = lus.lookup(templ);
//System.out.println(lus.lookup(new ServiceTemplate(null,
// new Class[] { TransactionManager.class }, null)));
if (proxy != null) {
if (log.isDebugEnabled())
log.debug("Discovered proxy " + proxy.getClass());
this.notify();
}
else {
if (log.isDebugEnabled())
log.debug("template " + templ + " not found in registrar " + lus);
}
}
}
catch (RemoteException re) {
throw new RemoteAccessException("can not find service", re);
}
}
/**
* @return Returns the groups.
*/
public String[] getGroups() {
return groups;
}
/**
* @param groups The groups to set.
*/
public void setGroups(String[] groups) {
this.groups = groups;
}
/**
* @return Returns the serviceClass.
*/
public Class getServiceClass() {
return serviceClass;
}
/**
* @param serviceClass The serviceClass to set.
*/
public void setServiceClass(Class serviceClass) {
this.serviceClass = serviceClass;
}
/**
* @return Returns the serviceName.
*/
public String getServiceName() {
return serviceName;
}
/**
* @param serviceName The serviceName to set.
*/
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
/**
* @return Returns the template.
*/
public ServiceTemplate getTemplate() {
return template;
}
/**
* @param template The template to set.
*/
public void setTemplate(ServiceTemplate template) {
this.template = template;
}
/**
* @return Returns the timeout.
*/
public long getTimeout() {
return timeout;
}
/**
* @param timeout The timeout to set.
*/
public void setTimeout(long timeout) {
this.timeout = timeout;
}
}