All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.nill14.utils.init.impl.ServiceRegistry Maven / Gradle / Ivy

package com.github.nill14.utils.init.impl;

import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import javax.annotation.Nullable;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Qualifier;

import com.github.nill14.utils.init.api.BindingKey;
import com.github.nill14.utils.init.api.IBeanDescriptor;
import com.github.nill14.utils.init.api.IBeanInjector;
import com.github.nill14.utils.init.api.ILazyPojo;
import com.github.nill14.utils.init.api.IParameterType;
import com.github.nill14.utils.init.api.IPojoInitializer;
import com.github.nill14.utils.init.api.IPropertyResolver;
import com.github.nill14.utils.init.api.IServiceContext;
import com.github.nill14.utils.init.api.IServiceRegistry;
import com.github.nill14.utils.init.inject.PojoInjectionDescriptor;
import com.github.nill14.utils.init.meta.AnnotationScanner;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;

/**
 * 
 * The ServiceRegistry must not be serializable. Serializing the registry indicates a programming error.
 *
 */
@Deprecated
public class ServiceRegistry implements IServiceRegistry {
	
	private final ConcurrentHashMap> beans = new ConcurrentHashMap<>();
	private final ConcurrentHashMap, Map>> services = new ConcurrentHashMap<>();
	private final ConcurrentHashMap, Map>> qualifiers = new ConcurrentHashMap<>();
	
	private final ServiceRegistryPropertyResolver resolver = new ServiceRegistryPropertyResolver();
	
	public ServiceRegistry() {
	}
	
	private String generateGlobalName(Class type) { //TODO make sure the generated name doesn't conflict
		Named named = type.getAnnotation(Named.class);
		if (named != null) {
			return type.getTypeName() + "$" + named.value();
		
		} else {
			return type.getTypeName();
		}
	}
	
	@Override
	public  void addService(Class serviceBean, IServiceContext context) {
		addService(generateGlobalName(serviceBean), serviceBean, context);
	}
	
	
	private  IPropertyResolver getResolver(IServiceContext context) {
		Optional customResolver = context.getCustomResolver();
		if (customResolver.isPresent()) {
			ChainingPropertyResolver chainingPropertyResolver = new ChainingPropertyResolver();
			chainingPropertyResolver.append(customResolver.get());
			chainingPropertyResolver.append(resolver);
			return chainingPropertyResolver;
		} else {
			return resolver;
		}
	}
	
	@SuppressWarnings("unchecked")
	private  ILazyPojo newProxy(ILazyPojo lazyPojo, Class serviceBean) {
//		Object proxy = LazyJdkProxy.newProxy(lazyPojo);
//		Object proxy = LazyJavassistProxy.newProxy(lazyPojo);
//		return (ILazyPojo) LazyPojo.forSingleton(proxy, IPropertyResolver.empty());
		return (ILazyPojo) lazyPojo;
	}
	
	@Override
	public  void addService(String name, Class serviceBean, IServiceContext context) {
		
		IPropertyResolver resolver = getResolver(context);
		ILazyPojo lazyPojo = LazyPojo.forBean(serviceBean, resolver);
		
		ILazyPojo proxy = newProxy(lazyPojo, serviceBean);
		IBeanDescriptor pd = new PojoInjectionDescriptor<>(serviceBean);
		pd.getDeclaredTypes().forEach((type) -> addElement(type, name, proxy, getTypeQualifier(pd.getRawType())));
		Object old = beans.put(name, proxy);
		Preconditions.checkArgument(old == null, "Duplicate bean " + old);
	}
	
	@Override
	public > void addServiceFactory(
			Class iface, Class factoryBean, IServiceContext context) {
		addServiceFactory(iface, generateGlobalName(iface), factoryBean, context);
	}

	@Override
	public > void addServiceFactory(
			Class iface, String name, Class factoryBean, IServiceContext context) {
		
		IPropertyResolver resolver = getResolver(context);
		ILazyPojo lazyPojo = LazyPojo.forProvider(factoryBean, resolver);
		ILazyPojo proxy = newProxy(lazyPojo, factoryBean);
		
		IBeanDescriptor pd = new PojoInjectionDescriptor<>(iface);
		pd.getDeclaredTypes().forEach((type) -> addElement(type, name, proxy, getTypeQualifier(pd.getRawType())));
		
		beans.put(name, proxy);
	}

	@Override
	public  S getService(Class iface) {
		Optional optional = getOptionalService(iface);
		Preconditions.checkArgument(optional.isPresent(), String.format("Missing %s", iface));
		return iface.cast(optional.get());
	}
	
	@Override
	public  S getService(Class iface, String name) {
		S service = iface.cast(beans.get(name));
		Objects.requireNonNull(service);
		return service;
	}

	@Override
	public  Optional getOptionalService(Class iface) {
		Optional> first = getServiceMap(iface).values().stream().findFirst();
		return first.map(ILazyPojo::getInstance);
	}
	
	@Override
	public  Optional getOptionalService(Class iface, String name) {
		ILazyPojo bean = beans.get(name);
		if (bean != null && iface.isAssignableFrom(bean.getType().getRawType())) {
			return Optional.of(iface.cast(bean.getInstance()));
		}
		return Optional.empty();
	}

	private void addElement(Class registrable, String name, ILazyPojo proxy, @Nullable Annotation qualifier) {
		Map> s = services.computeIfAbsent(registrable, r -> new ConcurrentHashMap<>());
		s.put(name, proxy);
		
		if (qualifier != null) {
			Map> q = this.qualifiers.computeIfAbsent(registrable, r -> new ConcurrentHashMap<>());
			Object prev = q.put(qualifier, proxy);
			Preconditions.checkArgument(prev == null, 
					String.format("Duplicate qualifier %s for type %s", qualifier, registrable));
			
		}
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private  Map> getServiceMap(Class registrable) {
		Preconditions.checkNotNull(registrable);
		Map map = services.getOrDefault(registrable, Collections.emptyMap());
		if (map != null) {
			return map;
		
		} else {
			return ImmutableMap.of();
		}
	}
	
	@Override
	public  Collection getServices(Class registrable) {
		Map> map = getServiceMap(registrable);
		if (map != null) {
			return ImmutableList.copyOf(map.values().stream().map(ILazyPojo::getInstance).iterator());
		
		} else {
			return ImmutableList.of();
		}
	}
	
	@Override
	public  void addSingleton(T serviceBean) {
		addSingleton(Objects.toString(serviceBean), serviceBean);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public  void addSingleton(String name, T serviceBean) {
		ILazyPojo pojo = LazyPojo.forSingleton(serviceBean, IPropertyResolver.empty());
		IBeanDescriptor pd = new PojoInjectionDescriptor((Class) serviceBean.getClass());
		pd.getDeclaredTypes().forEach((type) -> addElement(type, name, pojo, getTypeQualifier(pd.getRawType())));
		beans.put(name, pojo);
	}
	

	public void addBinding(BindingKey BindingKey, ILazyPojo lazyPojo) {
		String globalName = generateGlobalName(lazyPojo.getType().getRawType());
		addElement(BindingKey.getRawType(), globalName, lazyPojo, BindingKey.getQualifier());
		Object old = beans.put(globalName, lazyPojo);
		Preconditions.checkArgument(old == null, "Duplicate bean " + old);
	}
	
	public Collection> getBeans() {
		Builder> builder = ImmutableList.builder();
		builder.addAll(services.keySet());
//		builder.addAll(providers.values())
		return builder.build();
	}
	
	public Collection getBeanNames() {
		return beans.keySet();
	}
	
	public  Map getBeansOfType(Class type) {
		Map> map = getServiceMap(type);
		return map.entrySet().stream().collect(
				Collectors.toMap(Map.Entry::getKey, e -> e.getValue().getInstance()));
	}
	
	public Object getBean(String name) {
		ILazyPojo pojo = beans.get(name);
		if (pojo != null) {
			return pojo.getInstance();
			
		} else {
			return null;
		}
	}
	
	@Override
	public IPropertyResolver toResolver() {
		return resolver;
	}
	
	@Override
	public IBeanInjector toBeanInjector() {
		return new BeanInjector(toResolver());
	}
	
	@SuppressWarnings("serial")
	private class ServiceRegistryPropertyResolver extends AbstractPropertyResolver {
		
		public ServiceRegistryPropertyResolver() {
			super();
		}

		@Override
		protected Object findByQualifier(IParameterType type, Annotation qualifier) {
			ILazyPojo lazyPojo = qualifiers.getOrDefault(type.getRawType(), Collections.emptyMap()).get(qualifier);
			if (lazyPojo != null) {
				return lazyPojo.getInstance();
			} else {
				return null;
			}
		}
		
		@Override
		protected Object findByName(String name, IParameterType type) {
			ILazyPojo bean = beans.get(name);
			if (bean != null && type.getToken().isAssignableFrom(bean.getType())) {
				return bean.getInstance();
			}
			return null;
		}

		@SuppressWarnings({ "rawtypes", "unchecked" })
		@Override
		protected Object findByType(IParameterType type) {
			Class clazz = type.getRawType();
			Map> serviceMap = getServiceMap((Class) clazz);
			Optional> first = serviceMap.values().stream().findFirst();
			return first.map(x -> x.getInstance()).orElse(null);
		}

		@Override
		protected Collection findAllByType(IParameterType type) {
			return getServices(type.getRawType());
		}

	};
	
	private Annotation getTypeQualifier(Class clazz) {
		return AnnotationScanner.findQualifier(clazz).orElse(null);
	}

	

}