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

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

There is a newer version: 0.4.0
Show newest version
package com.github.nill14.utils.init.impl;

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 com.github.nill14.utils.init.api.ILazyPojo;
import com.github.nill14.utils.init.api.IPojoFactory;
import com.github.nill14.utils.init.api.IPojoInitializer;
import com.github.nill14.utils.init.api.IPropertyResolver;
import com.github.nill14.utils.init.api.IServiceRegistry;
import com.github.nill14.utils.init.api.IType;
import com.github.nill14.utils.init.inject.PojoInjectionDescriptor;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableSet;

/**
 * 
 * The ServiceRegistry must not be serializable. Serializing the registry indicates a programming error.
 *
 */
public class ServiceRegistry implements IServiceRegistry {
	
	private final ConcurrentHashMap beans = new ConcurrentHashMap<>();
	private final ConcurrentHashMap, Map> services = new ConcurrentHashMap<>();
	private IPropertyResolver delegateResolver;
	
	/**
	 * 
	 * Not thread-safe
	 * @param delegateResolver The extra resolver which can provide the answer first.
	 */
	public void setDelegateResolver(IPropertyResolver delegateResolver) {
		if(this.delegateResolver != null) {
			throw new IllegalStateException("can be set only once");
		}
		this.delegateResolver = delegateResolver;
	}
	
	private String generateName(Class type) {
		return type.getTypeName();
	}
	
	@Override
	public  void addService(Class serviceBean) {
		addService(generateName(serviceBean), serviceBean);
	}
	
	@Override
	public  void addService(String name, Class serviceBean) {
		ILazyPojo lazyPojo = LazyPojo.forClass(serviceBean, annotationInitializer);
		Object proxy = LazyJdkProxy.newProxy(lazyPojo);
		Set> types = new PojoInjectionDescriptor(serviceBean).getDeclaredTypes();
		types.forEach((type) -> addElement(type, name, proxy));
		Object old = beans.put(name, proxy);
		Preconditions.checkArgument(old == null, "Duplicate bean " + old);
	}
	
	@Override
	public > void addServiceFactory(
			Class iface, Class factoryBean) {
		addServiceFactory(iface, generateName(iface), factoryBean);
	}

	@Override
	public > void addServiceFactory(
			Class iface, String name, Class factoryBean) {
		ILazyPojo lazyPojo = LazyPojo.forFactory(iface, factoryBean, annotationInitializer);
		Object proxy = LazyJdkProxy.newProxy(lazyPojo);
		Set> types = new PojoInjectionDescriptor(iface).getDeclaredTypes();
		types.forEach((type) -> addElement(type, name, proxy));
		beans.put(name, proxy);
	}

	@Override
	public  S getService(Class iface) {
		Optional optional = getOptionalService(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) {
		return getServices(iface).stream().findFirst();
	}
	
	@Override
	public  Optional getOptionalService(Class iface, String name) {
		S service = iface.cast(beans.get(name));
		return Optional.ofNullable(service);
	}

	private void addElement(Class registrable, String name, Object proxy) {
		Map map = services.computeIfAbsent(registrable, r -> new ConcurrentHashMap<>());
		map.put(name, proxy);
	}

	@SuppressWarnings("unchecked")
	@Override
	public  Collection getServices(Class registrable) {
		Preconditions.checkNotNull(registrable);
		Map map = (Map) services.getOrDefault(registrable, Collections.emptyMap());
		if (map != null) {
			return ImmutableList.copyOf(map.values());
		
		} else {
			return ImmutableList.of();
		}
	}
	
	@Override
	public  void addSingleton(T serviceBean) {
		addSingleton(Objects.toString(serviceBean), serviceBean);
	}
	
	@Override
	public  void addSingleton(String name, T serviceBean) {
		Set> types = new PojoInjectionDescriptor(serviceBean.getClass()).getDeclaredTypes();
		types.forEach((type) -> addElement(type, name, serviceBean));
		beans.put(name, serviceBean);
	}
	
	public Collection> getBeans() {
		Builder> builder = ImmutableList.builder();
		builder.addAll(services.keySet());
//		builder.addAll(providers.values())
		return builder.build();
	}
	
	public Collection getBeanNames() {
		return beans.keySet();
	}
	
	@SuppressWarnings("unchecked")
	public  Map getBeansOfType(Class type) {
		return (Map) services.getOrDefault(type, Collections.emptyMap());
	}
	
	public Object getBean(String name) {
		return beans.get(name);
	}
	
	private final IPropertyResolver resolver = new IPropertyResolver() {
		
		private static final long serialVersionUID = 746185406164849945L;

		@Override
		public Object resolve(Object pojo, IType type) {
			
			if (delegateResolver != null) {
				Object resolve = delegateResolver.resolve(pojo, type);
				if (resolve != null) {
					return resolve;
				}
			}

			Optional optionalNamed = getOptionalService(type.getRawType(), type.getName());
			if (optionalNamed.isPresent()) {
				return optionalNamed.get();
			}
			
			if (type.isParametrized()) {
				Class rawType = type.getRawType();
				Class paramClass = type.getFirstParamClass();

				if (Optional.class.isAssignableFrom(rawType)) {
					Optional optional = getOptionalService(paramClass);
					return optional;
				}
				
				if (Collection.class.isAssignableFrom(rawType)) {
					Collection providers = getServices(paramClass);
					
					if (Set.class.isAssignableFrom(rawType)) {
						return ImmutableSet.copyOf(providers);
					} else {
						return ImmutableList.copyOf(providers);
					}
				}
			}
			
			Optional optionalTyped = getOptionalService(type.getRawType());
			if (optionalTyped.isPresent()) {
				return optionalTyped.get();
			}
			
			return null;
		}
	};
	
	
	public IPropertyResolver toResolver() {
		return resolver;
	}
	
	private final IPojoInitializer annotationInitializer = AnnotationPojoInitializer.withResolver(resolver);

}