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

io.katharsis.module.ModuleRegistry Maven / Gradle / Ivy

There is a newer version: 2.6.3
Show newest version
package io.katharsis.module;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.fasterxml.jackson.databind.ObjectMapper;

import io.katharsis.dispatcher.filter.Filter;
import io.katharsis.errorhandling.mapper.ExceptionMapper;
import io.katharsis.errorhandling.mapper.ExceptionMapperLookup;
import io.katharsis.errorhandling.mapper.JsonApiExceptionMapper;
import io.katharsis.module.SimpleModule.RelationshipRepositoryRegistration;
import io.katharsis.module.SimpleModule.ResourceRepositoryRegistration;
import io.katharsis.repository.RelationshipRepository;
import io.katharsis.repository.RepositoryInstanceBuilder;
import io.katharsis.repository.ResourceRepository;
import io.katharsis.resource.information.ResourceInformation;
import io.katharsis.resource.information.ResourceInformationBuilder;
import io.katharsis.resource.registry.RegistryEntry;
import io.katharsis.resource.registry.ResourceLookup;
import io.katharsis.resource.registry.ResourceRegistry;
import io.katharsis.resource.registry.repository.DirectResponseRelationshipEntry;
import io.katharsis.resource.registry.repository.DirectResponseResourceEntry;
import io.katharsis.resource.registry.repository.ResponseRelationshipEntry;

/**
 * Container for setting up and holding {@link Module} instances;
 */
public class ModuleRegistry {

	private ObjectMapper objectMapper;
	private ResourceRegistry resourceRegistry;

	private List modules = new ArrayList();

	private SimpleModule aggregatedModule = new SimpleModule(null);
	private volatile boolean initialized;

	public ModuleRegistry() {
	}

	/**
	 * Register an new module to this registry and setup the module.
	 * 
	 * @param module module
	 */
	public void addModule(Module module) {
		module.setupModule(new ModuleContextImpl());
		modules.add(module);
	}

	class ModuleContextImpl implements Module.ModuleContext {

		@Override
		public void addResourceInformationBuilder(ResourceInformationBuilder resourceInformationBuilder) {
			checkNotInitialized();
			aggregatedModule.addResourceInformationBuilder(resourceInformationBuilder);
		}

		@Override
		public void addResourceLookup(ResourceLookup resourceLookup) {
			checkNotInitialized();
			aggregatedModule.addResourceLookup(resourceLookup);
		}

		@Override
		public void addJacksonModule(com.fasterxml.jackson.databind.Module module) {
			checkNotInitialized();
			aggregatedModule.addJacksonModule(module);
			if (objectMapper != null) {
				objectMapper.registerModule(module);
			}
		}

		@Override
		public ResourceRegistry getResourceRegistry() {
			if (resourceRegistry == null)
				throw new IllegalStateException("resourceRegistry not yet available");
			return resourceRegistry;
		}

		@Override
		public void addRepository(Class type, ResourceRepository repository) {
			checkNotInitialized();
			aggregatedModule.addRepository(type, repository);
		}

		@Override
		public void addRepository(Class sourceType, Class targetType,
				RelationshipRepository repository) {
			checkNotInitialized();
			aggregatedModule.addRepository(sourceType, targetType, repository);
		}

		@Override
		public void addFilter(Filter filter) {
			checkNotInitialized();
			aggregatedModule.addFilter(filter);			
		}

		@Override
		public void addExceptionMapperLookup(ExceptionMapperLookup exceptionMapperLookup) {
			checkNotInitialized();
			aggregatedModule.addExceptionMapperLookup(exceptionMapperLookup);			
		}

		@Override
		public void addExceptionMapper(ExceptionMapper exceptionMapper) {
			checkNotInitialized();
			aggregatedModule.addExceptionMapper(exceptionMapper);			
		}
	}

	/**
	 *
	 * @return all Jackson modules registered by modules.
	 */
	public List getJacksonModules() {
		return aggregatedModule.getJacksonModules();
	}

	/**
	 * Ensures the {@link ModuleRegistry#init(ObjectMapper, ResourceRegistry)} has not yet been called.
	 */
	protected void checkNotInitialized() {
		if (initialized) {
			throw new IllegalStateException("already initialized, cannot be changed anymore");
		}
	}

	/**
	 * Returns a {@link ResourceInformationBuilder} instance that combines all
	 * instances registered by modules.
	 *
	 * @return resource information builder
	 */
	public ResourceInformationBuilder getResourceInformationBuilder() {
		return new CombinedResourceInformationBuilder(aggregatedModule.getResourceInformationBuilders());
	}

	/**
	 * Returns a {@link ResourceLookup} instance that combines all
	 * instances registered by modules.
	 *
	 * @return resource lookup
	 */
	public ResourceLookup getResourceLookup() {
		return new CombinedResourceLookup(aggregatedModule.getResourceLookups());
	}

	/**
	 * Combines all {@link ResourceInformationBuilder} instances provided by the registered
	 * {@link Module}.
	 */
	static class CombinedResourceInformationBuilder implements ResourceInformationBuilder {

		private Collection builders;

		public CombinedResourceInformationBuilder(List builders) {
			this.builders = builders;
		}

		@Override
		public boolean accept(Class resourceClass) {
			for (ResourceInformationBuilder builder : builders) {
				if (builder.accept(resourceClass)) {
					return true;
				}
			}
			return false;
		}

		@Override
		public ResourceInformation build(Class resourceClass) {
			for (ResourceInformationBuilder builder : builders) {
				if (builder.accept(resourceClass)) {
					return builder.build(resourceClass);
				}
			}
			throw new UnsupportedOperationException(
					"no ResourceInformationBuilder for " + resourceClass.getName() + " available");
		}
	}

	
	/**
	 * Combines all {@link ExceptionMapperLookup} instances provided by the registered
	 * {@link Module}.
	 */
	static class CombinedExceptionMapperLookup implements ExceptionMapperLookup {

		private Collection lookups;

		public CombinedExceptionMapperLookup(List lookups) {
			this.lookups = lookups;
		}

		@SuppressWarnings("rawtypes")
		@Override
		public  Set getExceptionMappers() {
			Set set = new HashSet();
			for(ExceptionMapperLookup lookup : lookups){
				set.addAll(lookup.getExceptionMappers());
			}
			return set;
		}
	}

	/**
	 * Combines all {@link ResourceLookup} instances provided by the registered
	 * {@link Module}.
	 */
	static class CombinedResourceLookup implements ResourceLookup {

		private Collection lookups;

		public CombinedResourceLookup(List lookups) {
			this.lookups = lookups;
		}

		@Override
		public Set> getResourceClasses() {
			Set> set = new HashSet>();
			for (ResourceLookup lookup : lookups) {
				set.addAll(lookup.getResourceClasses());
			}
			return set;
		}

		@Override
		public Set> getResourceRepositoryClasses() {
			Set> set = new HashSet>();
			for (ResourceLookup lookup : lookups) {
				set.addAll(lookup.getResourceRepositoryClasses());
			}
			return set;
		}
	}

	/**
	 * Initializes the {@link ModuleRegistry} and applies all pending changes. After the initialization
	 * completed, it is not possible to add any further modules.
	 * 
	 * @param objectMapper object mapper
	 * @param resourceRegistry resource registry
	 */
	public synchronized void init(ObjectMapper objectMapper, ResourceRegistry resourceRegistry) {
		if (!initialized) {
			this.initialized = true;
			this.objectMapper = objectMapper;
			this.resourceRegistry = resourceRegistry;
			this.objectMapper.registerModules(getJacksonModules());

			applyRepositoryRegistration(resourceRegistry);
		}
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private void applyRepositoryRegistration(ResourceRegistry resourceRegistry) {
		List relationshipRepositoryRegistrations = aggregatedModule
				.getRelationshipRepositoryRegistrations();
		List resourceRepositoryRegistrations = aggregatedModule
				.getResourceRepositoryRegistrations();

		// TODO this needs to be merged with ResourceRegistryBuilder
		for (final ResourceRepositoryRegistration resourceRepositoryRegistration : resourceRepositoryRegistrations) {
			Class resourceClass = resourceRepositoryRegistration.getResourceClass();
			RepositoryInstanceBuilder> repositoryInstanceBuilder = new RepositoryInstanceBuilder(
					null, null) {
				public Object buildRepository() {
					return resourceRepositoryRegistration.getRepository();
				}
			};
			DirectResponseResourceEntry resourceEntry = new DirectResponseResourceEntry(repositoryInstanceBuilder);
			ResourceInformation resourceInformation = getResourceInformationBuilder().build(resourceClass);
			List relationshipEntries = new ArrayList();
			for (final RelationshipRepositoryRegistration relationshipRepositoryRegistration : relationshipRepositoryRegistrations) {
				if (relationshipRepositoryRegistration.getSourceType() == resourceClass) {
					RepositoryInstanceBuilder relationshipInstanceBuilder = new RepositoryInstanceBuilder(
							null, null) {
						public RelationshipRepository buildRepository() {
							return relationshipRepositoryRegistration.getRepository();
						}
					};
					ResponseRelationshipEntry relationshipEntry = new DirectResponseRelationshipEntry(
							relationshipInstanceBuilder);
					relationshipEntries.add(relationshipEntry);
				}
			}
			// TODO get also relations from resource lookup
			RegistryEntry registryEntry = new RegistryEntry(resourceInformation, resourceEntry, relationshipEntries);
			resourceRegistry.addEntry(resourceClass, registryEntry);
		}
	}

	/**
	 * @return {@link Filter} added by all modules
	 */
	public List getFilters() {
		return aggregatedModule.getFilters();
	}

	/**
	 * @return combined {@link ExceptionMapperLookup} added by all modules
	 */
	public ExceptionMapperLookup getExceptionMapperLookup() {
		return new CombinedExceptionMapperLookup(aggregatedModule.getExceptionMapperLookups());
	}
	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy