package org.distributeme.core;
import net.anotheria.anoprise.metafactory.*;
import net.anotheria.moskito.core.dynamic.ProxyUtils;
import org.distributeme.core.conventions.SharedNamingUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A utility for convenient lookup of implementations for interfaces. This utility helps to reduce amount of code to instantiate a service if you
* follow the naming patterns.
*
* @author lrosenberg
* @version $Id: $Id
*/
public class ServiceLocator {
/**
* Log.
*/
private static Logger log = LoggerFactory.getLogger(ServiceLocator.class);
/**
* Returns a remote instance of a service aka stub. This is only useful for interface annotated with @DistributeMe and generated by the apt processor.
*
* @param pattern a {@link java.lang.Class} object.
* @return a T object.
*/
public static <T extends Service> T getAsynchRemote(Class<T> pattern){
try{
return MetaFactory.get(pattern, Extension.ASYNCH);
}catch(MetaFactoryException e){
//NOPMD
}
//try to lookup an instance by name
String className = SharedNamingUtils.getAsynchFactoryFullClassName(pattern);
try{
@SuppressWarnings("unchecked")Class<ServiceFactory<T>> factoryClazz = (Class<ServiceFactory<T>>)Class.forName(className);
MetaFactory.addFactoryClass(pattern, Extension.ASYNCH, factoryClazz);
}catch(ClassNotFoundException ignored){
ignored.printStackTrace();
}
try{
return MetaFactory.get(pattern, Extension.ASYNCH);
}catch(MetaFactoryException e){
log.error("getAsynchRemote("+pattern+")", e);
throw new RuntimeException("No asynch impl for "+pattern.getName()+" known (tried MetaFactory.get("+pattern.getName()+" and "+className+")");
}
}
/**
* Returns a remote instance of a service aka stub. This is only useful for interface annotated with @DistributeMe and generated by the apt processor.
*
* @param pattern a {@link java.lang.Class} object.
* @return a T object.
*/
public static <T extends Service> T getRemote(Class<T> pattern){
try{
return MetaFactory.get(pattern, Extension.REMOTE);
}catch(MetaFactoryException e){
//NOPMD
}
//try to lookup an instance by name
String className = SharedNamingUtils.getStubFactoryFullClassName(pattern);
try{
@SuppressWarnings("unchecked")Class<ServiceFactory<T>> factoryClazz = (Class<ServiceFactory<T>>)Class.forName(className);
MetaFactory.addFactoryClass(pattern, Extension.REMOTE, factoryClazz);
}catch(ClassNotFoundException ignored){
ignored.printStackTrace();
}
try{
return MetaFactory.get(pattern, Extension.REMOTE);
}catch(MetaFactoryException e){
log.error("getRemote("+pattern+")", e);
throw new RuntimeException("No remote impl for "+pattern.getName()+" known (tried MetaFactory.get("+pattern.getName()+" and "+className+")");
}
}
/**
* Returns a local instance of T. First this method tries to lookup T in the MetaFactory. If this fails it tries to
* lookup a factory for T or a direct implementation of T. In success case it will register a factory for T in the MetaFactory and create an instance.
*
* @param pattern class of T.
* @return a T object.
*/
public static <T extends Service> T getLocal(Class<T> pattern){
try{
return MetaFactory.get(pattern, Extension.LOCAL);
}catch(MetaFactoryException e){ //NOPMD
//ignore
}
//ok, we don't have a registered copy yet, lets check if we can find one.
String patternName = pattern.getName();
final String factoryName = patternName+"Factory";
final String implName = patternName + "Impl";
try{
log.debug("looking up factory "+factoryName+" for service "+patternName);
Class<ServiceFactory<T>> factoryClazz = (Class<ServiceFactory<T>>)Class.forName(factoryName);
MetaFactory.addFactoryClass(pattern, Extension.LOCAL, factoryClazz);
}catch(ClassNotFoundException factoryNotFound){
try{
// Even more convinient - try to instantiate the implementation directly
log.debug("looking up impl "+implName+" for service "+patternName);
Class<? extends T> implClazz = (Class<? extends T>)Class.forName(implName);
MetaFactory.createOnTheFlyFactory(pattern, Extension.LOCAL, implClazz.newInstance());
}catch(ClassNotFoundException implNotFound){
log.warn("Giving up trying to find an impl instance, tried "+implName+" and "+factoryName);
} catch (InstantiationException e) {
log.warn("Found implementation "+implName+" but failed to instantiate, ", e);
} catch (IllegalAccessException e) {
log.warn("Found implementation "+implName+" but failed to instantiate due", e);
}
} //...outer catch
try{
return MetaFactory.get(pattern, Extension.LOCAL);
}catch(MetaFactoryException e){
throw new IllegalArgumentException(pattern + " doesn't seem to be dynamically resolveable, its missing "+implName+" or "+factoryName);
}
}
/**
* Returns a moskito-monitored local instance of the requested interface.
*
* @param pattern a {@link java.lang.Class} object.
* @param monitorableInterfaces - additional interfaces the impl may implement. Since the returned instance will be proxied, the proxies should know of all interfaces
* that you are going to use to call the returned instance. Therefor they all have to be submitted. However, in most cases you can just omit this parameter.
* @return a T object.
*/
public static <T extends Service> T getMonitoredLocal(Class<T> pattern, Class<?> ... monitorableInterfaces){
T serviceInstance = getLocal(pattern);
return ProxyUtils.createServiceInstance(serviceInstance, "default", pattern, monitorableInterfaces);
}
}