package com.softwaremill.common.cdi.objectservice.auto; import com.softwaremill.common.cdi.objectservice.extension.ObjectServiceSpecification; import com.softwaremill.common.util.dependency.BeanManagerDependencyProvider; import javax.enterprise.inject.spi.BeanManager; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.TypeVariable; import java.util.HashMap; import java.util.Map; /** * Handler for the @OS invocation */ public class AutoOSInvocationHandler implements InvocationHandler { private AutoObjectServiceExtension autoObjectServiceExtension; private BeanManager beanManager; /** * Cache for already resolved beans */ private Map<Class, Object> beanCache = new HashMap<Class, Object>(); public AutoOSInvocationHandler(AutoObjectServiceExtension autoObjectServiceExtension, BeanManager beanManager) { this.autoObjectServiceExtension = autoObjectServiceExtension; this.beanManager = beanManager; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Class toService = null; for (int i = 0; i < method.getGenericParameterTypes().length; i++) { if (method.getGenericParameterTypes()[i] instanceof TypeVariable) { // found it, invoke toService = args[i].getClass(); Object bean = null; // first check the cache if ((bean = beanCache.get(toService)) != null) { } else { Class<?> interfaceClass = method.getDeclaringClass(); Class serviceClass = autoObjectServiceExtension.autoOSMap.get(interfaceClass) .get(toService); if (serviceClass == null) { // try with inheritance if there's no direct match serviceClass = serviceForObject(toService, interfaceClass); } try { beanCache.put(toService, bean = new BeanManagerDependencyProvider(beanManager).inject( serviceClass, serviceClass.getAnnotation(OSImpl.class))); } catch (Exception e) { throw new AutoOSException("Cannot resolve implementation of @OS " + interfaceClass + " for object of type " + toService, e); } } if (bean != null) { return method.invoke(bean, args); } } } throw new AutoOSException("Couldn't find appropriate ObjectServiceImpl for class " + toService); } private Class serviceForObject(Class<?> objClass, Class<?> serviceClass) { // TODO: cache results Class bestSoFar = null; // Checking all registered specifications for (Class toService : autoObjectServiceExtension.autoOSMap.get(serviceClass).keySet()) { // Checking if the service is suitable for the requested one // Here we allow subclasses of objClass, to make sure that e.g. Hibernate Proxies work. However, this // will not work if there are object services for non-terminal nodes (non-leaves) in the inheritance tree. if (toService.isAssignableFrom(objClass)) { // Checking if it's better (more specific) than the one currently found if (bestSoFar == null || bestSoFar.isAssignableFrom(toService)) { bestSoFar = toService; } // TODO: check if there's no ambiguency (two "best") } } return autoObjectServiceExtension.autoOSMap.get(serviceClass).get(bestSoFar); } }