package js.container;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.TreeSet;

import js.annotation.Asynchronous;
import js.annotation.Inject;
import js.annotation.Intercepted;
import js.annotation.Private;
import js.annotation.Public;
import js.annotation.Remote;
import js.converter.Converter;
import js.converter.ConverterException;
import js.converter.ConverterRegistry;
import js.core.App;
import js.core.AppContext;
import js.core.AppFactory;
import js.lang.BugError;
import js.lang.Config;
import js.lang.ConfigException;
import js.lang.Configurable;
import js.lang.InstanceInvocationHandler;
import js.lang.InvocationException;
import js.lang.ManagedLifeCycle;
import js.lang.ManagedPreDestroy;
import js.lang.NoProviderException;
import js.log.Log;
import js.log.LogFactory;
import js.rmi.RemoteFactory;
import js.transaction.Transaction;
import js.util.Classes;
import js.util.Params;
import js.util.Types;

/**
 * Container creates managed classes and handle managed instances life cycle, that is, instances creation and caching. Basic
 * container functionality is managed methods invocation, methods that implement application defined logic for dynamic resources
 * generation and services. One can view container as a bridge between servlets and application defined resources and services
 * or as an environment in which an application runs.
 *

 * At is core container deals with managed classes. Container has a pool of managed classes and every managed class has a pool
 * of managed methods. Beside managed life cycle container provides a host of declarative services to managed instances. Anyway,
 * Container class does not implement managed instances services by itself but delegates factories and processors, implemented
 * by js.container package or as plug-ins.
 *
 *

 *     +-------------------------------------------------------------------+
 *     | Tiny Container                                                    |
 *     |    +------------+                              +---------------+  |
 *     |    | Interfaces |                          +---> ManagedMethod |  |
 *     |    +-----^------+       +--------------+   |   +---------------+  |
 *     |          |          +---> ManagedClass +---+                      |
 *     |          |          |   +--------------+   |   +---------------+  |
 *     |    +-----+------+   |                      +---> ManagedMethod |  |
 *  ---O----> Container  +---+                          +---------------+  |
 *     |    +------------+   |                                             |
 *     |                     ~                      +---...                |
 *     |                     |   +--------------+   |                      |
 *     |                     +---> ManagedClass +---+                      |
 *     |                         +--------------+   |                      |
 *     +-------------------------------------------------------------------+
 *

 *

 * Here is a quick list of services provided, actually orchestrated, by container:
 *

 *
Declarative implementation binding for managed classes.

 *
Managed class has interface used by caller to invoke methods on managed instance. Managed instance is create by factory
 * instead of new operator. The actual implementation is declared into class descriptor and bound at container creation.
 * Implementation bind cannot be hot changed.

 *
Life span management for managed instances, e.g. application, thread or local scope.

 *
A new managed instance can be created at every request or can be reused from a cache, depending on managed class scope.
 * Instance scope is declared on managed class descriptor. New managed instances are created by {@link InstanceFactory} whereas
 * life span is controlled by {@link ScopeFactory} class.

 *
External configuration of managed instances from application descriptor.

 *
If a managed instance implements {@link js.lang.Configurable} interface container takes care to configure it from managed
 * class configuration section. Every managed class has an alias into application descriptor that identify configurable section.
 *

 *
Managed life cycle for managed instances.

 *
For managed instances implementing {@link ManagedLifeCycle} interface container invokes post-create and pre-destroy
 * methods after managed instance creation, respective before destroying.

 *
Declarative transactions, both mutable and immutable.

 *
A managed class can be marked as transactional, see {@link Transaction} annotation. Container creates a Java Proxy
 * handler and execute managed method inside transaction boundaries.

 *
Dependency injection for fields and constructor parameters.

 *
Current container supports two types of dependency injection: field injection using {@link Inject} annotation and
 * constructor injection. Injected instance should be managed instance on its turn or instantiable plain Java class.

 *
Managed classes static fields initialization from application descriptor.

 *
Inject value objects declared in application descriptor into static fields from managed class implementation. Container
 * uses {@link Converter} to convert string to value type. Of course static field should not be final.

 *
Authenticated remote access to managed methods; authentication occurs at method invocation.

 *
A method annotated with, or owned by a class annotated with {@link Remote} is named net method and is accessible from
 * remote. Remote access is controller by {@link Public} and {@link Private} annotations. A private net method can be accessed
 * only after authentication.

 *
Declarative asynchronous execution mode for long running logic, executed in separated thread.

 *
If a managed method is annotated with {@link Asynchronous} container creates a separated thread and execute method
 * asynchronously. Asynchronous method should return void.

 *
Method invocation listeners. There are interceptors for before, after and around method invocation.

 *
Invocation listeners provide a naive, but still useful AOP. There is {@link Intercepted} annotation for tagging methods -
 * declaring join points, and related interface to be implemented by interceptors, aka AOP advice. See {@link Interceptor}
 * interface for sample usage.

 *
Method instrumentation. Uses {@link InvocationMeter} to monitor method invocations.

 *
Every managed method has a meter that updates internal counters about execution time, invocation and exceptions count.
 * Invocation meter interface is used to collect counter values. Instrumentation manager, {@link Observer}, collects
 * periodically all managed methods counters and create report on server logger.

 *

 *
 *
Descriptors

 *

 * Every application has a descriptor for declarative services and state initialization. Application descriptor is a XML file,
 * with root child elements named sections. Tiny Container uses these sections to group related
 * configurations and here we will focus only on container specific ones.
 *

 * Container adds supplementary semantics to application descriptor: a section element name is known as alias and
 * this alias can be use to refer other section, known as linked section. There are predefined sections, like
 * managed-class and user defined sections, that need to be declared as linked sections into a predefined section,
 * for example processor section.
 *

 * Here are predefined container descriptor sections.
 *

 *

 * Name
 * Description
 * Linked Section
 *

 * managed-classes
 * Managed class descriptors.
 * Managed class configuration object.
 *

 * pojo-classes
 * Plain Java classes static field initialization.
 * List of static fields and respective intial values.
 *

 * converters
 * Declarative converters.
 * N/A
 *

 *

 * In sample below there is a predefined section managed-class that has element processor. Element is
 * a class descriptor and its name is related to linked section processor, section that is the managed class
 * configuration object. For details about managed classes descriptors see {@link ManagedClass}.
 *
 *

 * <managed-classes>
 * 	<processor interface="js.email.Processor" class="js.email.ProcessorImpl" scope="APPLICATION" />
 * 	...
 * </managed-classes>
 *
 * <pojo-classes>
 * 	<email-reader class="com.mobile.EmailReader" />
 * 	...
 * </pojo-classes>
 *
 * <processor repository="/var/www/vhosts/kids-cademy.com/email" files-pattern="*.htm" >
 * 	<property name="mail.transport.protocol" value="smtp" />
 * 	...
 * </processor>
 *
 * <email-reader>
 * 	<static-field name="ACCOUNT" value="john.doe@email.com" />
 * 	...
 * </email-reader>
 *
 * <converters>
 * 	<type class="js.email.MessageID" converter="js.email.MessageIDConverter" />
 * 	...
 * </converters>
 *

 *
 * For pojo-classes and converters predefined sections see {@link #pojoStaticInitialization(Config)},
 * respective {@link #convertersInitialization(Config)}.
 *
 *
Life Cycle

 *

 * Container has a managed life cycle on its own. This means it is created automatically, initialized and configured,
 * post-constructed and just before ending its life, pre-destroyed. Accordingly, container's life passes five phases:
 *

 *
Construct container - create factories and processors for instance management but leave classes pool loading for next
 * step, see {@link #Container()},
 *
Create managed classes pool - scans application and class descriptors and creates all managed classes described there,
 * see {@link #config(Config)},
 *
Start managed classes with managed life cycle - this step should occur after all managed classes were loaded and
 * container is fully initialized, see {@link #start()}; this is because a managed instance may have dependencies on other
 * managed classes for injection,
 *
Container is running - this stage has no single entry point; it is the sum of all services container provide while is up
 * and running,
 *
Destroy container - destroy and release caches, factories and processors; this is container global clean-up invoked at
 * application unload, see {@link #destroy()}.
 *

 *
 *
Instance Retrieval Algorithm
 Instance retrieval is the process of obtaining a managed
 * instance, be it from scope factories caches or fresh created by specialized instance factories.
 *

 *
obtain an instance key based on {@link ManagedClassSPI#getKey()} or instance name,
 *
use interface class to retrieve managed class from container classes pool,
 *
if managed class not found exit with bug error; if {@link #getOptionalInstance(Class, Object...)} returns null,
 *
get scope and instance factories for managed class instance scope and type; if managed class instance scope is local
 * there is no scope factory,
 *
if scope factory is null execute local instance factory, that creates a new instance, and returns it; applies arguments
 * processing on local instance but not instance processors,
 *
if there is scope factory do next steps into synchronized block,
 *
try to retrieve instance from scope factory cache,
 *
if no cached instance pre-process arguments, create a new instance using instance factory and persist instance on scope
 * factory,
 *
end synchronized block,
 *
arguments pre-processing takes care to inject constructor dependencies,
 *
if new instance is created execute instance post-processors into registration order.
 *

 *
 * @author Iulian Rotaru
 * @version draft
 */
public abstract class Container implements ContainerSPI, Configurable {
	/** Class logger. */
	private static final Log log = LogFactory.getLog(Container.class);

	/**
	 * Scope specific factories mapped to related {@link InstanceScope}. A scope factory takes care of managed instance life
	 * span and reuse it if instance is retrieved in the same scope. It is part of Instance
	 * Retrieval Algorithm.
	 *

	 * There are a number of built-in scope factories created by constructor but subclass may register new ones via
	 * {@link #registerScopeFactory(ScopeFactory)}.
	 */
	private final Map<InstanceScope, ScopeFactory> scopeFactories = new HashMap<>();

	/**
	 * Mutex for scope factory access. Instance retrieval algorithm uses scope factories in two steps: first try to retrieve an
	 * instance already existing on scope and if null persist a newly created one. These steps are synchronized using this
	 * mutex.
	 */
	private final Object scopeMutex = new Object();

	/**
	 * Instance factories mapped to related {@link InstanceType}. An instance factory deals with actual instance creation and is
	 * enacted by Instance Retrieval Algorithm only if on current scope there is not an
	 * already created managed instance.
	 *

	 * There are a number of built-in instance factories created by constructor but subclass may register new ones via
	 * {@link #registerInstanceFactory(InstanceType, InstanceFactory)}.
	 */
	private final Map<InstanceType, InstanceFactory> instanceFactories = new HashMap<>();

	/**
	 * Class post-processors are executed after {@link ManagedClass} creation and generally deals with managed implementation
	 * static fields initialization, but is not limited to.
	 *

	 * These processors are registered by {@link #registerClassProcessor(ClassProcessor)}. Note that these processors are global
	 * and executed for ALL managed classes.
	 */
	private final List<ClassProcessor> classProcessors = new ArrayList<>();

	/**
	 * Instance post-processors are executed only on newly created managed instances. If instance is reused from scope cache
	 * this processors are not executed. They add instance specific services. This list contains processors in execution order.
	 *

	 * There are a number of built-in processor created by constructor but subclass may register new ones via
	 * {@link #registerInstanceProcessor(InstanceProcessor)}.
	 */
	private final List<InstanceProcessor> instanceProcessors = new ArrayList<>();

	/**
	 * Processor for managed constructor and method invocation arguments. Takes care of dependency injection on arguments and
	 * some insanity checks.
	 */
	private final ArgumentsProcessor argumentsProcessor = new ArgumentsProcessor();

	/**
	 * Master cache for all managed classes registered to container. Since an application has one and only one container
	 * instance, managed classes pool is unique per application. This pool is initialized by {@link #config(Config)} method.
	 */
	private final Map<Class<?>, ManagedClassSPI> classesPool = new HashMap<>();

	private final CronManager cronManager;

	// --------------------------------------------------------------------------------------------
	// CONTAINER LIFE CYCLE

	/**
	 * Create factories and processors for instance retrieval but leave classes pool loading for {@link #config(Config)}. This
	 * constructor creates built-in factories and processors but subclass may add its own.
	 */
	public Container() {
		log.trace("Container()");

		cronManager = new CronManager();

		// first register plug-in scope factories in order to avoid overriding built-in factories
		for (ScopeFactory scopeFactory : ServiceLoader.load(ScopeFactory.class)) {
			registerScopeFactory(scopeFactory);
		}
		registerScopeFactory(new ApplicationScopeFactory());
		registerScopeFactory(new ThreadScopeFactory());
		// local instances have no managed scope; simple creates a new instance for every call
		registerScopeFactory(null);

		// first register plug-in instance factories in order to avoid overriding built-in factories
		for (InstanceFactory instanceFactory : ServiceLoader.load(InstanceFactory.class)) {
			registerInstanceFactory(instanceFactory.getInstanceType(), instanceFactory);
		}

		InstanceFactory localFactory = new LocalInstanceFactory();
		registerInstanceFactory(InstanceType.POJO, localFactory);
		registerInstanceFactory(InstanceType.PROXY, localFactory);

		registerInstanceFactory(InstanceType.SERVICE, new ServiceInstanceFactory());
		registerInstanceFactory(InstanceType.REMOTE, new RemoteInstanceFactory());

		registerInstanceProcessor();
		registerClassProcessor();
	}

	/**
	 * Register all instance post-processors.
	 */
	protected void registerInstanceProcessor() {
		registerInstanceProcessor(new InstanceFieldsInjectionProcessor());
		registerInstanceProcessor(new InstanceFieldsInitializationProcessor());
		registerInstanceProcessor(new ConfigurableInstanceProcessor());
		registerInstanceProcessor(new PostConstructInstanceProcessor());
		registerInstanceProcessor(new CronMethodsProcessor(cronManager));
		registerInstanceProcessor(new LoggerInstanceProcessor());
	}

	/**
	 * Register all managed class post-processors.
	 */
	protected void registerClassProcessor() {
	}

	/**
	 * Register scope factory for the instance scope returned by {@link ScopeFactory#getInstanceScope()}.
	 *
	 * @param scopeFactory scope factory instance, possible null for local scope.
	 * @throws BugError if instance scope is already registered.
	 */
	protected void registerScopeFactory(ScopeFactory scopeFactory) {
		if (scopeFactory == null) {
			log.debug("Register null scope factory to |%s|.", InstanceScope.LOCAL);
			scopeFactories.put(InstanceScope.LOCAL, null);
			return;
		}

		final InstanceScope instanceScope = scopeFactory.getInstanceScope();
		log.debug("Register scope factory |%s| to |%s|.", scopeFactory.getClass(), instanceScope);
		if (scopeFactories.put(instanceScope, scopeFactory) != null) {
			throw new BugError("Attempt to override instance scope |%s|.", instanceScope);
		}
	}

	/**
	 * Register instance factory to requested instance type.
	 *
	 * @param instanceType instance type used as key for instance factory,
	 * @param instanceFactory instance factory.
	 * @throws BugError if instance type is already registered.
	 */
	protected void registerInstanceFactory(InstanceType instanceType, InstanceFactory instanceFactory) {
		log.debug("Register instance factory |%s| to |%s|.", instanceFactory.getClass(), instanceType);
		if (instanceFactories.put(instanceType, instanceFactory) != null) {
			throw new BugError("Attempt to override instance type |%s|.", instanceType);
		}
	}

	/**
	 * Register instance processor. Only a single instance per processor class is allowed.
	 *
	 * @param instanceProcessor instance processor.
	 * @throws BugError if instance processor class is already registered.
	 * @see #instanceProcessors
	 */
	protected void registerInstanceProcessor(InstanceProcessor instanceProcessor) {
		for (InstanceProcessor existingInstanceProcessoor : instanceProcessors) {
			if (existingInstanceProcessoor.getClass().equals(instanceProcessor.getClass())) {
				throw new BugError("Attempt to override instance processor |%s|.", instanceProcessor.getClass());
			}
		}
		log.debug("Register instance processor |%s|.", instanceProcessor.getClass());
		instanceProcessors.add(instanceProcessor);
	}

	/**
	 * Register global processors for managed classes. Post-processors are singletons and only one post-processor instance of a
	 * type is allowed.
	 *
	 * @param classProcessor managed class post-processor.
	 * @throws BugError if class processor class is already registered.
	 * @see #classProcessors
	 */
	protected void registerClassProcessor(ClassProcessor classProcessor) {
		for (ClassProcessor existingClassProcessoor : classProcessors) {
			if (existingClassProcessoor.getClass().equals(classProcessor.getClass())) {
				throw new BugError("Attempt to override class processor |%s|.", classProcessor.getClass());
			}
		}
		log.debug("Register class processor |%s|.", classProcessor.getClass());
		classProcessors.add(classProcessor);
	}

	/**
	 * Create all managed instances registered to this container via external application descriptor. See
	 * Descriptors for details about application and class descriptors.
	 *
	 * @param config container configuration object.
	 * @throws ConfigException if container configuration fails.
	 * @throws BugError for insane condition that prevent managed classes initialization.
	 */
	@Override
	public void config(Config config) throws ConfigException {
		log.trace("config(Config)");

		// normalized class descriptors with overrides resolves; should preserve order from external files

		// it is legal for application descriptor to override already defined class descriptor
		// for example user defined ro.gnotis.Fax2MailApp overrides library declared js.core.App
		// <app interface='js.core.App' class='ro.gnotis.Fax2MailApp' />

		// when found an overridden class descriptor replace it with most recent version
		// only class descriptors with single interface can be overridden

		List<Config> classDescriptors = new ArrayList<>();

		for (Config descriptorsSection : config.findChildren("managed-classes", "web-sockets")) {
			CLASS_DESCRIPTORS: for (Config classDescriptor : descriptorsSection.getChildren()) {
				if (!classDescriptor.hasChildren()) {
					if (!classDescriptor.hasAttribute("interface")) {
						classDescriptor.setAttribute("interface", classDescriptor.getAttribute("class"));
					}
					String interfaceClass = classDescriptor.getAttribute("interface");
					for (int i = 0; i < classDescriptors.size(); ++i) {
						if (classDescriptors.get(i).hasAttribute("interface", interfaceClass)) {
							log.debug("Override class descriptor for interface |%s|.", interfaceClass);
							classDescriptors.set(i, classDescriptor);
							continue CLASS_DESCRIPTORS;
						}
					}
				}
				classDescriptors.add(classDescriptor);
			}
		}

		// second step is to actually populate the classes pool from normalized class descriptors list

		for (Config classDescriptor : classDescriptors) {
			// create managed class, a single one per class descriptor, even if there are multiple interfaces
			// if multiple interfaces register the same managed class multiple times, once per interface
			// this way, no mater which interface is used to retrieve the instance it uses in the end the same managed class
			ManagedClass managedClass = new ManagedClass(this, classDescriptor);
			log.debug("Register managed class |%s|.", managedClass);
			for (Class<?> interfaceClass : managedClass.getInterfaceClasses()) {
				classesPool.put(interfaceClass, managedClass);
			}

			for (ClassProcessor classProcessor : classProcessors) {
				classProcessor.postProcessClass(managedClass);
			}
		}

		convertersInitialization(config);
		pojoStaticInitialization(config);

		// special handling for this container instance accessed via application context
		// need to ensure this container instance is reused and not to create a new one
		ManagedClassSPI appContext = classesPool.get(AppContext.class);
		// application context can be null on tests
		// also on tests application context interface can be implemented by mock class not in container hierarchy
		if (appContext != null && Types.isKindOf(appContext.getImplementationClass(), ContainerSPI.class)) {
			log.debug("Persist container instance on application scope.");
			scopeFactories.get(InstanceScope.APPLICATION).persistInstance(new InstanceKey(appContext.getKey()), this);
		}
	}

	/**
	 * Ensure all managed classes with managed life cycle are instantiated. Invoked at a final stage of container initialization
	 * this method checks every managed class that implements {@link ManagedLifeCycle} interface and ensure is instantiated.
	 *

	 * Takes care to instantiate, configure if the case, and execute post-construct in the order from application descriptor.
	 * This is critical for assuring that {@link App} is created first; {@link App} class descriptor is declared first into
	 * application descriptor.
	 *

	 * Note that this method does not explicitly execute {@link ManagedLifeCycle#postConstruct()} hooks; this hooks are actually
	 * executed by instance processor, see {link {@link PostConstructInstanceProcessor}.
	 */
	@SuppressWarnings("unchecked")
	public void start() {
		log.trace("start()");

		// classes pool is not sorted; it is a hash map for performance reasons
		// also, a managed class may appear multiple times if have multiple interfaces
		// bellow sorted set is used to ensure ascending order on managed classes instantiation
		// comparison is based on managed class key that is created incrementally

		Set<ManagedClassSPI> sortedClasses = new TreeSet<>(new Comparator<ManagedClassSPI>() {
			@Override
			public int compare(ManagedClassSPI o1, ManagedClassSPI o2) {
				// compare first with second to ensure ascending sorting
				return o1.getKey().compareTo(o2.getKey());
			}
		});

		for (ManagedClassSPI managedClass : classesPool.values()) {
			if (managedClass.isAutoInstanceCreation()) {
				sortedClasses.add(managedClass);
			}
			// process only implementations of managed life cycle interface
			// if (Types.isKindOf(managedClass.getImplementationClass(), ManagedLifeCycle.class)) {
			// sortedClasses.add(managedClass);
			// }
		}

		for (ManagedClassSPI managedClass : sortedClasses) {
			// call getInstance to ensure managed instance with managed life cycle is started
			// if there are more than one single interface peek one, no matter which; the simple way is to peek the first
			// getInstance() will create instance only if not already exist; returned value is ignored

			log.debug("Create managed instance with managed life cycle |%s|.", managedClass.getInterfaceClass());
			getInstance((Class<? super Object>) managedClass.getInterfaceClass());
		}
	}

	/**
	 * Destroy container and release caches, factories and processors. This is container global clean-up invoked at application
	 * unload; after executing this method no managed instance can be created or reused. Attempting to use {@link AppFactory}
	 * after container destroy may lead to not predictable behavior.
	 *

	 * Before caches clean-up, this method invokes {@link ManagedPreDestroy} on all managed instances that are active, that is,
	 * cached in all scope factories. Takes care to execute pre-destroy in reverse order from application descriptor
	 * declarations. This ensure {@link App} is destroyed last since App class descriptor is declared first into application
	 * descriptor.
	 */
	public void destroy() {
		log.trace("destroy()");

		// cron manager should be destroyed first to avoid invoking cron methods on cleaned managed instance
		cronManager.destroy();

		// classes pool is not sorted; it is a hash map for performance reasons
		// also, a managed class may appear multiple times if have multiple interfaces
		// bellow sorted set is used to ensure reverse order on managed classes destruction
		// comparison is based on managed class key that is created incrementally

		Set<ManagedClassSPI> sortedClasses = new TreeSet<>(new Comparator<ManagedClassSPI>() {
			@Override
			public int compare(ManagedClassSPI o1, ManagedClassSPI o2) {
				// compare second with first to ensure descending sorting
				return o2.getKey().compareTo(o1.getKey());
			}
		});

		for (ManagedClassSPI managedClass : classesPool.values()) {
			// process only implementations of managed pre-destroy interface
			if (Types.isKindOf(managedClass.getImplementationClass(), ManagedPreDestroy.class)) {
				sortedClasses.add(managedClass);
			}
		}

		for (ManagedClassSPI managedClass : sortedClasses) {
			ScopeFactory scopeFactory = scopeFactories.get(managedClass.getInstanceScope());
			InstanceKey instanceKey = new InstanceKey(managedClass.getKey());
			Object instance = scopeFactory.getInstance(instanceKey);
			if (instance == null) {
				continue;
			}

			// sorted managed classes contains only implementations of pre-destroy interface
			// in case instance is a Java Proxy takes care to execute pre-destroy hook on wrapped instance
			// in order to avoid adding container services to this finalization hook
			ManagedPreDestroy managedInstance = (ManagedPreDestroy) Classes.unproxy(instance);
			log.debug("Pre-destroy managed instance |%s|.", managedInstance.getClass());
			try {
				managedInstance.preDestroy();
			} catch (Throwable t) {
				log.dump(String.format("Managed instance |%s| pre-destroy fail:", instance.getClass()), t);
			}
		}

		for (ScopeFactory scopeFactory : scopeFactories.values()) {
			// local scope has null factory
			if (scopeFactory != null) {
				scopeFactory.clear();
			}
		}

		classesPool.clear();
		instanceProcessors.clear();
		scopeFactories.clear();
		instanceFactories.clear();
	}

	// --------------------------------------------------------------------------------------------
	// INSTANCE RETRIEVAL ALGORITHM

	@Override
	public <T> T getInstance(Class<? super T> interfaceClass, Object... args) {
		Params.notNull(interfaceClass, "Interface class");

		ManagedClassSPI managedClass = classesPool.get(interfaceClass);
		if (managedClass == null) {
			throw new BugError("No managed class associated with interface class |%s|.", interfaceClass);
		}

		InstanceKey instanceKey = new InstanceKey(managedClass.getKey());
		return getInstance(managedClass, instanceKey, args);
	}

	@Override
	public <T> T getInstance(String instanceName, Class<? super T> interfaceClass, Object... args) {
		Params.notNullOrEmpty(instanceName, "Instance name");
		Params.notNull(interfaceClass, "Interface class");

		ManagedClassSPI managedClass = classesPool.get(interfaceClass);
		if (managedClass == null) {
			throw new BugError("No managed class associated with interface class |%s|.", interfaceClass);
		}

		// instance name should be unique and can be used as uniqueness indicator
		InstanceKey key = new InstanceKey(instanceName);
		return getInstance(managedClass, key, args);
	}

	@Override
	public <T> T getOptionalInstance(Class<? super T> interfaceClass, Object... args) {
		Params.notNull(interfaceClass, "Interface class");

		ManagedClassSPI managedClass = classesPool.get(interfaceClass);
		if (managedClass == null) {
			return null;
		}

		// here is a piece of code that uses exception for normal logic flow but I do not see alternative
		// ServiceInstanceFactory should throw exception that propagates to AppFactory and application code
		// on the other hand this getOptionalInstance() should return null for missing service provider

		InstanceKey instanceKey = new InstanceKey(managedClass.getKey());
		try {
			return getInstance(managedClass, instanceKey, args);
		} catch (NoProviderException e) {
			// is not an error since application code is prepared for missing provider
			log.debug(e);
			return null;
		}
	}

	@Override
	public <T> T getInstance(ManagedClassSPI managedClass, Object... args) {
		InstanceKey instanceKey = new InstanceKey(managedClass.getKey());
		return getInstance(managedClass, instanceKey, args);
	}

	/**
	 * Utility method that implements the core of managed instances retrieval algorithm. This method gets existing managed
	 * instance from container caches or creates a new instance. If instance is fresh created add instance services via
	 * registered instance processors.
	 *

	 * Here is managed instance retrieval algorithm part implemented by this method.
	 *

	 *
get scope and instance factories for managed class instance scope and type; if managed class instance scope is local
	 * there is no scope factory,
	 *
if scope factory is null execute local instance factory, that creates a new instance, and returns it; applies
	 * arguments processing on local instance but not instance processors,
	 *
if there is scope factory do next steps into synchronized block,
	 *
try to retrieve instance from scope factory cache,
	 *
if no cached instance pre-process arguments, create a new instance using instance factory and persist instance on
	 * scope factory,
	 *
end synchronized block,
	 *
arguments pre-processing takes care to inject constructor dependencies,
	 *
if new instance is created execute instance post-processors into registration order.
	 *

	 *
	 * @param managedClass managed class for which instance is to be retrieved,
	 * @param instanceKey managed instance key,
	 * @param args optional constructor arguments, used only if new local instance is created.
	 * @param <T> managed instance type.
	 * @return managed instance, created on the fly or reused from caches, but never null.
	 *
	 * @throws InvocationException if instance is local and constructor fails.
	 * @throws NoProviderException if interface is a service and no provider found on run-time.
	 * @throws ConverterException if attempt to initialize a field with a type for which there is no converter,
	 * @throws BugError if dependency value cannot be created or circular dependency is detected.
	 * @throws BugError if instance configuration fails either due to bad configuration object or fail on configuration user
	 *             defined logic.
	 * @throws BugError if instance post-construction fails due to exception of user defined logic.
	 * @throws BugError if attempt to assign field to not POJO type.
	 */
	@SuppressWarnings("unchecked")
	private <T> T getInstance(ManagedClassSPI managedClass, InstanceKey instanceKey, Object... args) {
		// if(managedClass.getInstanceType().isREMOTE()) {
		// return getRemoteInstance(new URL(managedClass.getImplementationURL()), (Class<? super T>)
		// managedClass.getInterfaceClass());
		// }

		ScopeFactory scopeFactory = scopeFactories.get(managedClass.getInstanceScope());
		InstanceFactory instanceFactory = instanceFactories.get(managedClass.getInstanceType());

		if (scopeFactory == null) {
			args = argumentsProcessor.preProcessArguments(managedClass, args);
			return instanceFactory.newInstance(managedClass, args);
		}

		boolean postProcessingEnabled = false;
		Object instance = null;
		synchronized (scopeMutex) {
			instance = scopeFactory.getInstance(instanceKey);
			if (instance == null) {
				postProcessingEnabled = true;
				args = argumentsProcessor.preProcessArguments(managedClass, args);
				instance = instanceFactory.newInstance(managedClass, args);
				scopeFactory.persistInstance(instanceKey, instance);
			}
		}

		if (!postProcessingEnabled) {
			return (T) instance;
		}

		// post-processors operate on bare POJO instances but is possible for instance factory to return a Java Proxy
		// if instance is a Java Proxy that uses InstanceInvocationHandler extract wrapped POJO instance
		// if instance is a Java Proxy that does not use InstanceInvocationHandler post-processing is not performed at all
		// if instance is not a Java Proxy execute post-processing on it

		Object pojoInstance = null;
		if (instance instanceof Proxy) {
			if (Proxy.getInvocationHandler(instance) instanceof InstanceInvocationHandler) {
				InstanceInvocationHandler<T> handler = (InstanceInvocationHandler<T>) Proxy.getInvocationHandler(instance);
				pojoInstance = handler.getWrappedInstance();
			}
		} else {
			pojoInstance = instance;
		}

		if (pojoInstance != null) {
			for (InstanceProcessor instanceProcessor : instanceProcessors) {
				instanceProcessor.postProcessInstance(managedClass, pojoInstance);
			}
		}

		return (T) instance;
	}

	@Override
	public <T> T getRemoteInstance(String implementationURL, Class<? super T> interfaceClass) {
		Params.notNull(implementationURL, "Implementation URL");
		Params.notNull(interfaceClass, "Interface class");
		Params.isTrue(interfaceClass.isInterface(), "Interface class argument is not actually an interface.");

		InstanceFactory instanceFactory = instanceFactories.get(InstanceType.REMOTE);
		if (instanceFactory == null) {
			throw new BugError("No REMOTE instance factory registered.");
		}
		if (!(instanceFactory instanceof RemoteFactory)) {
			throw new BugError("Invalid REMOTE instance factory provider. Does not implement %s interface.", RemoteFactory.class);
		}
		return ((RemoteFactory) instanceFactory).getRemoteInstance(implementationURL, interfaceClass);
	}

	// ----------------------------------------------------
	// CONTAINER SPI

	@Override
	public Iterable<ManagedClassSPI> getManagedClasses() {
		return classesPool.values();
	}

	@Override
	public Iterable<ManagedMethodSPI> getManagedMethods() {
		return new Iterable<ManagedMethodSPI>() {
			@Override
			public Iterator<ManagedMethodSPI> iterator() {
				return new ManagedMethodsIterator();
			}
		};
	}

	@Override
	public boolean isManagedClass(Class<?> interfaceClass) {
		return classesPool.containsKey(interfaceClass);
	}

	@Override
	public ManagedClassSPI getManagedClass(Class<?> interfaceClass) {
		return classesPool.get(interfaceClass);
	}

	// --------------------------------------------------------------------------------------------
	// SECURITY CONTEXT INTERFACE

	@Override
	public boolean isAuthenticated() {
		return true;
	}

	// --------------------------------------------------------------------------------------------
	// PACKAGE LEVEL METHODS

	/**
	 * Test if instance scope has a {@link ScopeFactory} registered on this container.
	 *
	 * @param instanceScope instance scope to test for factory registration.
	 * @return true if instance scope has factory.
	 */
	boolean hasScopeFactory(InstanceScope instanceScope) {
		return scopeFactories.containsKey(instanceScope);
	}

	/**
	 * Test if instance type has a {@link InstanceFactory} registered on this container.
	 *
	 * @param instanceType instance type to test for factory registration.
	 * @return true if instance type has factory.
	 */
	boolean hasInstanceFactory(InstanceType instanceType) {
		return instanceFactories.containsKey(instanceType);
	}

	// --------------------------------------------------------------------------------------------
	// INNER CLASSES

	/**
	 * Iterator for all managed methods defined by this container. Traverses all managed methods from all managed classes from
	 * {@link Container#classesPool}. There is no guarantee for a particular order.
	 *
	 * @author Iulian Rotaru
	 * @version final
	 */
	private class ManagedMethodsIterator implements Iterator<ManagedMethodSPI> {
		/** Managed classes iterator. */
		private Iterator<ManagedClassSPI> classesIterator;
		/** Iterator on managed methods from current managed class. */
		private Iterator<ManagedMethodSPI> currentMethodsIterator;

		/**
		 * Initialize iterators for managed classes and current class methods.
		 */
		public ManagedMethodsIterator() {
			classesIterator = classesPool.values().iterator();
			if (!classesIterator.hasNext()) {
				throw new BugError("Empty classes pool.");
			}
			currentMethodsIterator = classesIterator.next().getManagedMethods().iterator();
		}

		@Override
		public boolean hasNext() {
			while (!currentMethodsIterator.hasNext()) {
				if (!classesIterator.hasNext()) {
					return false;
				}
				currentMethodsIterator = classesIterator.next().getManagedMethods().iterator();
			}
			return true;
		}

		@Override
		public ManagedMethodSPI next() {
			return currentMethodsIterator.next();
		}
	}

	// --------------------------------------------------------------------------------------------
	// UTILITY METHODS

	/**
	 * Declarative converters registration. This utility scans for converters section into given configuration
	 * object and register converters via {@link ConverterRegistry#registerConverter(Class, Class)}.
	 *

	 * Configuration converter section should respect below syntax.
	 *
	 *

	 *  <converters>
	 *      <type class="java.io.File" converter="js.converter.FileConverter" />
	 *      <type class="java.net.URL" converter="js.converter.UrlConverter" />
	 *      . . .
	 *      <type class="java.util.TimeZone" converter="js.converter.TimeZoneConverter" />
	 *  </converters>
	 *

	 *
	 * @param config configuration object.
	 * @throws ConfigException if declared class or converter not found on run-time class path.
	 */
	private static void convertersInitialization(Config config) throws ConfigException {
		Config section = config.getChild("converters");
		if (section == null) {
			return;
		}
		for (Config el : section.findChildren("type")) {
			String className = el.getAttribute("class");
			Class<?> valueType = Classes.forOptionalName(className);
			if (valueType == null) {
				throw new ConfigException("Invalid converter configuration. Value type class |%s| not found.", className);
			}

			String converterName = el.getAttribute("converter");
			Class<? extends Converter> converterClass = Classes.forOptionalName(converterName);
			if (converterClass == null) {
				throw new ConfigException("Invalid converter configuration. Converter class |%s| not found.", converterName);
			}

			ConverterRegistry.getInstance().registerConverter(valueType, converterClass);
		}
	}

	/**
	 * Plain Java objects static initialization. Inject static fields value into arbitrary Java classes. Note that this
	 * mechanism is not related to managed classes; it acts on regular Java classes and only on static fields.
	 *

	 * Here is a sample configuration for Java objects static injection. There is pojo-classes section that list
	 * all involved classes, with aliases. For every Java class alias there is a section with the same name that has
	 * static name / value elements related by name to class static fields.
	 *
	 *

	 * <pojo-classes>
	 * 	<email-reader class="com.mobile.EmailReader" />
	 * </pojo-classes>
	 * ...
	 * <email-reader>
	 * 	<static-field name="USER_NAME" value="johndoe" />
	 * 	<static-field name="PASSWORD" value="secret" />
	 * </email-reader>
	 *

	 *

	 * Static values from above configuration sample are injected into class static fields with the same name. String value from
	 * configuration object is converted to field type.
	 *
	 *

	 * public class EmailReader {
	 * 	private static String USER_NAME;
	 * 	private static String PASSWORD;
	 * 	...
	 * }
	 *

	 *
	 * This initializer has side effects: it loads configured Java classes at container startup.
	 *
	 * @param config configuration object.
	 * @throws ConfigException if configuration object is not valid or class or field not found.
	 * @throws ConverterException if there is no converter registered for a specific field type.
	 */
	private static void pojoStaticInitialization(Config config) throws ConfigException {
		Config pojoClassesSection = config.getChild("pojo-classes");
		if (pojoClassesSection == null) {
			return;
		}

		for (Config pojoClassElement : pojoClassesSection.getChildren()) {
			String pojoClassName = pojoClassElement.getAttribute("class");
			if (pojoClassName == null) {
				throw new ConfigException("Invalid POJO class element. Missing <class> attribute.");
			}
			Config configSection = config.getChild(pojoClassElement.getName());
			Class<?> pojoClass = Classes.forOptionalName(pojoClassName);
			if (pojoClass == null) {
				throw new ConfigException("Missing configured POJO class |%s|.", pojoClassName);
			}
			if (configSection == null) {
				continue;
			}

			for (Config staticElement : configSection.findChildren("static-field")) {
				String fieldName = staticElement.getAttribute("name");
				if (fieldName == null) {
					throw new ConfigException("Missing <name> attribute from static field initialization |%s|.", configSection);
				}
				if (!staticElement.hasAttribute("value")) {
					throw new ConfigException("Missing <value> attribute from static field initialization |%s|.", configSection);
				}

				Field staticField = Classes.getOptionalField(pojoClass, fieldName);
				if (staticField == null) {
					throw new ConfigException("Missing POJO static field |%s#%s|.", pojoClassName, fieldName);
				}
				int modifiers = staticField.getModifiers();
				if (!Modifier.isStatic(modifiers)) {
					throw new ConfigException("Attempt to execute POJO |%s| static initialization on instance field |%s|.", pojoClassName, fieldName);
				}

				Object value = staticElement.getAttribute("value", staticField.getType());
				log.debug("Intialize static field |%s#%s| |%s|", pojoClassName, fieldName, value);
				Classes.setFieldValue(null, staticField, value);
			}
		}
	}
}


/**
 * Copyright (C) 2015 Michael Schnell. All rights reserved.
 * http://www.fuin.org/
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 3 of the License, or (at your option) any
 * later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library. If not, see http://www.gnu.org/licenses/.
 */
package org.fuin.srcgen4j.core.velocity;

import java.io.File;
import java.io.Reader;
import java.io.Serializable;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

import org.fuin.objects4j.common.Contract;
import org.fuin.objects4j.vo.TrimmedNotEmpty;
import org.fuin.srcgen4j.commons.JaxbHelper;
import org.fuin.srcgen4j.commons.MarshalObjectException;
import org.fuin.srcgen4j.commons.SrcGen4JContext;
import org.fuin.srcgen4j.commons.UnmarshalObjectException;
import org.fuin.srcgen4j.core.base.Producer;
import org.fuin.utils4j.Utils4J;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Velocity template that has a number of parameters. Every target file defines concrete values for those parameters. The list of target
 * files is statically defined or dynamically created by another class that generates the list.
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "parameterized-template")
@XmlType(propOrder = { "template", "arguments", "targetFiles", "tflProducerConfig" })
@TargetArgsMatchTemplateArgs
@TargetFilesMustBeAvailable
public class ParameterizedTemplateModel implements Serializable, Comparable<ParameterizedTemplateModel>, Producer {

    private static final long serialVersionUID = 1L;

    private static final Logger LOG = LoggerFactory.getLogger(ParameterizedTemplateModel.class);

    @XmlAttribute
    @TrimmedNotEmpty
    private String template;

    @Valid
    @XmlElementWrapper(name = "arguments")
    @XmlElement(name = "argument")
    private List<Argument> arguments;

    @Valid
    @XmlElement(name = "target-file")
    private List<TargetFile> targetFiles;

    @Valid
    @XmlElement(name = "target-file-list-producer")
    private TargetFileListProducerConfig tflProducerConfig;

    private transient File file;

    /**
     * Default constructor.
     */
    public ParameterizedTemplateModel() {
        super();
    }

    /**
     * Constructor with file list.
     *
     * @param template
     *            Relative path and name of the template - Cannot be NULL.
     * @param targetFiles
     *            Array of target files - Cannot be NULL and should have at least one entry.
     */
    public ParameterizedTemplateModel(final String template, final TargetFile... targetFiles) {
        super();

        this.template = template;
        this.targetFiles = new ArrayList<TargetFile>();
        this.tflProducerConfig = null;

        if (targetFiles != null) {
            for (final TargetFile targetFile : targetFiles) {
                this.targetFiles.add(targetFile);
            }
        }

    }

    /**
     * Constructor with target file list producer.
     *
     * @param template
     *            Relative path and name of the template - Cannot be NULL.
     * @param tflProducerConfig
     *            Configuration - Cannot be NULL.
     */
    public ParameterizedTemplateModel(final String template, final TargetFileListProducerConfig tflProducerConfig) {
        super();

        this.template = template;
        this.targetFiles = null;
        this.tflProducerConfig = tflProducerConfig;

    }

    /**
     * Returns the path and name of the template.
     *
     * @return Relative path and name.
     */
    public final String getTemplate() {
        return template;
    }

    /**
     * Sets the path and name of the template.
     *
     * @param template
     *            Relative path and name.
     */
    public final void setTemplate(final String template) {
        this.template = template;
    }

    /**
     * Returns the list of arguments defined by the template. All values serve as default for the parameters defined in a target file.
     *
     * @return Arguments and their default values defined for the template.
     */
    public final List<Argument> getArguments() {
        return arguments;
    }

    /**
     * Sets the list of arguments defined by the template. All values serve as default for the parameters defined in a target file.
     *
     * @param arguments
     *            Arguments and their default values defined for the template or NULL.
     */
    public final void setArguments(final List<Argument> arguments) {
        this.arguments = arguments;
    }

    /**
     * Adds an argument to the template. If the list of arguments does not exist, it will be created.
     *
     * @param argument
     *            Arguments to add.
     */
    public final void addArgument(@NotNull final Argument argument) {
        if (arguments == null) {
            arguments = new ArrayList<Argument>();
        }
        arguments.add(argument);
    }

    /**
     * Returns the list of target files to produce.
     *
     * @return Target files.
     */
    public final List<TargetFile> getTargetFiles() {
        return targetFiles;
    }

    /**
     * Sets the list of target files to produce.
     *
     * @param targetFiles
     *            Target file list to set or NULL.
     */
    public final void setTargetFiles(final List<TargetFile> targetFiles) {
        this.targetFiles = targetFiles;
    }

    /**
     * Returns the list of target files. Either by using the producer or by simply returning the internal list.
     *
     * @return Target files - Never NULL and has at least one entry.
     */
    public final List<TargetFile> createTargetFileList() {
        if (tflProducerConfig == null) {
            LOG.info("Using target file list: {} elements", targetFiles.size());
            return targetFiles;
        }
        final TargetFileListProducer producer = tflProducerConfig.getTargetFileListProducer();
        LOG.info("Using target file list producer: {}", producer.getClass().getName());
        return producer.createTargetFiles();
    }

    /**
     * Returns the target file producer configuration.
     *
     * @return Configuration.
     */
    public final TargetFileListProducerConfig getTargetFileListProducerConfig() {
        return tflProducerConfig;
    }

    // CHECKSTYLE:OFF Generated code
    @Override
    public final int hashCode() {
        final int prime = 31;
        int result = 1;
        result = (prime * result) + ((template == null) ? 0 : template.hashCode());
        return result;
    }

    @Override
    public final boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final ParameterizedTemplateModel other = (ParameterizedTemplateModel) obj;
        if (template == null) {
            if (other.template != null) {
                return false;
            }
        } else if (!template.equals(other.template)) {
            return false;
        }
        return true;
    }

    // CHECKSTYLE:ON

    @Override
    public final int compareTo(final ParameterizedTemplateModel other) {
        return template.compareTo(other.template);
    }

    /**
     * Marshals the object to XML.
     *
     * @param file
     *            File to write the instance to.
     */
    public final void writeToXml(final File file) {
        try {
            final JaxbHelper helper = new JaxbHelper();
            helper.write(this, file, createJaxbContext());
        } catch (final MarshalObjectException ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * Marshals the object to an XML String.
     *
     * @return XML String.
     */
    public final String toXml() {
        try {
            final JaxbHelper helper = new JaxbHelper();
            return helper.write(this, createJaxbContext());
        } catch (final MarshalObjectException ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * Marshals the object to XML.
     *
     * @param writer
     *            Writer to write the instance to.
     */
    public final void writeToXml(final Writer writer) {
        try {
            final JaxbHelper helper = new JaxbHelper();
            helper.write(this, writer, createJaxbContext());
        } catch (final MarshalObjectException ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * Returns the file the template was parsed from.
     *
     * @return Template file or NULL.
     */
    public final File getFile() {
        return file;
    }

    /**
     * Sets the file the template was parsed from.
     *
     * @param file
     *            Template file or NULL.
     */
    public final void setFile(final File file) {
        this.file = file;
    }

    /**
     * Initializes the model.
     *
     * @param context
     *            Current context.
     * @param vars
     *            Variables to use.
     */
    public final void init(final SrcGen4JContext context, final Map<String, String> vars) {

        if (template != null) {
            template = Utils4J.replaceVars(template, vars);
        }
        if (arguments != null) {
            for (final Argument argument : arguments) {
                argument.init(vars);
            }
        }
        if (targetFiles != null) {
            for (final TargetFile targetFile : targetFiles) {
                targetFile.init(vars);
            }
        }
        if (tflProducerConfig != null) {
            tflProducerConfig.init(context, this, vars);
        }
    }

    /**
     * Checks if this model has a reference to the given template file.
     *
     * @param templateDir
     *            Directory where all template files are located - Cannot be NULL.
     * @param templateFile
     *            Template file to check - Cannot be NULL.
     *
     * @return If the template is referenced TRUE else FALSE.
     */
    public final boolean hasReferenceTo(final File templateDir, final File templateFile) {
        final String p1 = Utils4J.getCanonicalPath(new File(templateDir, template));
        final String p2 = Utils4J.getCanonicalPath(templateFile);
        return p1.equals(p2);
    }

    private static JAXBContext createJaxbContext() {
        try {
            return JAXBContext.newInstance(ParameterizedTemplateModel.class, TargetFile.class, Argument.class);
        } catch (final JAXBException ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * Creates an instance by reading the XML from a reader.
     *
     * @param reader
     *            Reader to use.
     *
     * @return New instance.
     */
    public static ParameterizedTemplateModel create(final Reader reader) {
        try {
            final JaxbHelper helper = new JaxbHelper();
            final ParameterizedTemplateModel pc = helper.create(reader, createJaxbContext());
            Contract.requireValid(pc);
            return pc;
        } catch (final UnmarshalObjectException ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * Creates an instance by reading the XML from a file.
     *
     * @param file
     *            File to read.
     *
     * @return New instance.
     */
    public static ParameterizedTemplateModel create(final File file) {
        try {
            final JaxbHelper helper = new JaxbHelper();
            final ParameterizedTemplateModel pc = helper.create(file, createJaxbContext());
            pc.setFile(file);
            Contract.requireValid(pc);
            return pc;
        } catch (final UnmarshalObjectException ex) {
            throw new RuntimeException(ex);
        }
    }

}


package js.container;

import java.lang.reflect.Field;

import js.annotation.Inject;
import js.lang.BugError;
import js.util.Classes;

/**
 * Post processor for instance fields injection. Fields are discovered by managed class based on {@link Inject} annotation and
 * provided to this processor by {@link ManagedClassSPI#getDependencies()}. This class inherits dependency processor and
 * delegates {@link DependencyProcessor#getDependencyValue(ManagedClassSPI, Class)} for dependency value processing.
 * <p>
 * In essence this processor scans all dependencies detected by managed class and for every field retrieve its dependency value
 * and inject it reflexively.
 *
 * @author Iulian Rotaru
 * @version final
 */
final class InstanceFieldsInjectionProcessor extends DependencyProcessor implements InstanceProcessor {
	/**
	 * Inject dependencies described by given managed class into related managed instance. For every dependency field retrieve
	 * its value using {@link DependencyProcessor#getDependencyValue(ManagedClassSPI, Class)} and inject it reflexively.
	 *
	 * @param managedClass managed class,
	 * @param instance instance of given managed class.
	 * @throws BugError if a field dependency cannot be resolved.
	 */
	@Override
	public void postProcessInstance(ManagedClassSPI managedClass, Object instance) {
		if (instance == null) {
			// null instance is silently ignored since container ensure not null instance argument
			return;
		}
		for (Field dependency : managedClass.getDependencies()) {
			if (dependency.isSynthetic()) {
				// it seems there can be injected fields, created via byte code manipulation, when run with test coverage active
				// not clear why and how but was consistently observed on mock object from unit test run with coverage
				continue;
			}
			Classes.setFieldValue(instance, dependency, getDependencyValue(managedClass, dependency.getType()));
		}
	}
}


package js.container;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

import js.annotation.Intercepted;
import js.annotation.RequestPath;
import js.annotation.Remote;
import js.core.SecurityContext;
import js.lang.AsyncTask;
import js.lang.BugError;
import js.lang.InvocationException;
import js.log.Log;
import js.log.LogFactory;
import js.util.Classes;
import js.util.Strings;
import js.util.Types;

/**
 * Managed method provides method level services. A managed method is a thin wrapper around Java reflective method. It has
 * methods to retrieve internal state and miscellaneous flags but the real added piece of functionality is
 * {@link #invoke(Object, Object...)} method. It is in charge, of course beside executing wrapped Java method, with interceptors
 * and invocation meters handling. For details please see {@link Interceptor} and {@link InvocationMeter} interfaces.
 *
 * @author Iulian Rotaru
 * @version final
 */
final class ManagedMethod implements ManagedMethodSPI {
	/** Class logger. */
	private static final Log log = LogFactory.getLog(ManagedMethodSPI.class);

	/** Format string for managed method simple name, without class name. */
	private static final String SIMPLE_NAME_FORMAT = "%s(%s)";

	/** Format string for managed method fully qualified name. */
	private static final String QUALIFIED_NAME_FORMAT = "%s#" + SIMPLE_NAME_FORMAT;

	/** Back reference to parent container. */
	private final ContainerSPI container;

	/** The managed class declaring this managed method. */
	private final ManagedClassSPI declaringClass;

	/**
	 * Wrapped Java reflective method. This method instance reflects the method declared by managed class interface, see
	 * {@link ManagedClassSPI#getInterfaceClass()}.
	 */
	private final Method method;

	/**
	 * Request URI path for this managed method configured by {@link RequestPath} annotation. If annotation is missing this
	 * request path is initialized with method name converted to dashed case. Initialization is performed by
	 * {@link #setRequestPath(String)}.
	 */
	private String requestPath;

	/** Flag indicating if method should be executed into transactional context. */
	private boolean transactional;

	/**
	 * Transactional method marked as immutable, that is, read-only. By default method is mutable till explicitly set by
	 * {@link #setImmutable(boolean)}.
	 */
	private boolean immutable;

	/**
	 * A managed method is remotely accessible, also known as net method, if is annotated with {@link Remote} or declaring class
	 * is remotely accessible.
	 */
	private boolean remotelyAccessible;

	/** Flag for asynchronous method execution. If true, method invocation is delegated to {@link AsyncInvoker}. */
	private boolean asynchronous;

	/**
	 * Remote access authorization, default to private. This means that by default this method cannot be invoked from remote
	 * outside an authorized context. This field can be changed by {@link #setAccess(Access)}.
	 */
	private Access access = Access.PRIVATE;

	/**
	 * Invoker strategy, initialized at this managed method construction. If managed method is created without interceptor
	 * class, which is the commons case, this invoker is initialized to {@link DefaultInvoker}; if managed method is created
	 * with interceptor class uses {@link InterceptedInvoker}.
	 */
	private Invoker invoker;

	/** Managed method signature, mainly for debugging. */
	private final String signature;

	/** Method invocation arguments processor. */
	private final ArgumentsProcessor argumentsProcessor;

	/**
	 * Optional invocation meter used to record invocations and errors count and processing time. By default meter is not used
	 * and is null. It is created on the fly by {@link #getMeter()}, supposedly called only if application instrumentation is
	 * active.
	 */
	private Meter meter;

	private String cronExpression;

	/**
	 * Construct a managed method. This is a convenient constructor that just delegates
	 * {@link #ManagedMethod(ManagedClassSPI, Class, Method)} with null interceptor class.
	 *
	 * @param declaringClass declaring managed class,
	 * @param method Java reflective method wrapped by this managed method.
	 */
	public ManagedMethod(ManagedClassSPI declaringClass, Method method) {
		this(declaringClass, null, method);
	}

	/**
	 * Construct a managed method with optional invocation interceptor. Store declaring class and Java reflective method,
	 * initialize this managed method signature and arguments processor.
	 *

	 * If method invocation interceptor is present initialize {@link #invoker} field with {@link InterceptedInvoker}; otherwise
	 * uses {@link DefaultInvoker}.
	 *
	 * @param declaringClass declaring managed class,
	 * @param methodInterceptor optional method invocation interceptor, possible null,
	 * @param method Java reflective method wrapped by this managed method.
	 */
	public ManagedMethod(ManagedClassSPI declaringClass, Class<? extends Interceptor> methodInterceptor, Method method) {
		this.container = declaringClass.getContainer();
		this.declaringClass = declaringClass;
		this.method = method;
		this.method.setAccessible(true);

		invoker = methodInterceptor == null ? new DefaultInvoker() : new InterceptedInvoker(methodInterceptor);

		List<String> formalParameters = new ArrayList<String>();
		for (Class<?> formalParameter : method.getParameterTypes()) {
			formalParameters.add(formalParameter.getSimpleName());
		}
		signature = String.format(QUALIFIED_NAME_FORMAT, method.getDeclaringClass().getName(), method.getName(), Strings.join(formalParameters, ','));

		argumentsProcessor = new ArgumentsProcessor();
	}

	// --------------------------------------------------------------------------------------------
	// PACKAGE METHODS

	/**
	 * Set this method request URI path, that is, the path component by which this method is referred into request URI. If given
	 * request URI path is null uses method name converted to dashed case.
	 *
	 * @param requestPath request URI path for this method, possible null.
	 */
	void setRequestPath(String requestPath) {
		this.requestPath = requestPath != null ? requestPath : Strings.toDashCase(method.getName());
	}

	/**
	 * Set remote accessibility flag.
	 *
	 * @param remotelyAccessible remote accessibility flag.
	 */
	void setRemotelyAccessible(boolean remotelyAccessible) {
		this.remotelyAccessible = remotelyAccessible;
	}

	/**
	 * Set remote accessibility to public or private.
	 *
	 * @param access access type.
	 */
	void setAccess(Access access) {
		this.access = access;
	}

	/**
	 * Set this managed method transactional state.
	 *
	 * @param transactional transactional flag.
	 */
	void setTransactional(boolean transactional) {
		this.transactional = transactional;
	}

	/**
	 * Set transaction immutable state.
	 *
	 * @param immutable immutable flag.
	 */
	void setImmutable(boolean immutable) {
		this.immutable = immutable;
	}

	/**
	 * Set asynchronous mode. If asynchronous arguments is true, internal {@link #invoker} is decorated with
	 * {@link AsyncInvoker}; this process is irreversible.
	 *
	 * @param asynchronous asynchronous mode flag.
	 */
	void setAsynchronous(boolean asynchronous) {
		this.asynchronous = asynchronous;
		if (asynchronous) {
			invoker = new AsyncInvoker(invoker);
		}
	}

	void setCronExpression(String cronExpression) {
		this.cronExpression = cronExpression;
	}

	/**
	 * Enable instrumentation on the fly and return this method invocation meter.
	 *
	 * @return this method invocation meter.
	 * @see meter
	 */
	Meter getMeter() {
		// creating meter on the fly is not dangerous and need not be synchronized
		// this assumption is related to #invoke(Object, Object...) method logic
		// if change invoke logic it may need to add synchronization here too

		if (meter == null) {
			meter = new Meter(method);
		}
		return meter;
	}

	// --------------------------------------------------------------------------------------------
	// MANAGED METHOD SPI

	@Override
	public ManagedClassSPI getDeclaringClass() {
		return declaringClass;
	}

	@Override
	public Method getMethod() {
		return method;
	}

	@Override
	public Type[] getParameterTypes() {
		// there are rumors that generic type information is not preserved in byte code but not confirmed
		// this library does believe documentation and relies on next excerpt from Method.getGenericParameterTypes API:

		// If a formal parameter type is a parameterized type, the Type object returned for it must accurately reflect the
		// actual type parameters used in the source code.

		return method.getGenericParameterTypes();
	}

	@Override
	public Type getReturnType() {
		return method.getGenericReturnType();
	}

	/**
	 * Invoke managed method and applies method level services. Delegates this managed method {@link #invoker}; accordingly
	 * selected strategy invoker can be {@link DefaultInvoker} or {@link InterceptedInvoker}, if this managed method is
	 * annotated with {@link Interceptor}. Also takes care to update {@link #meter}.
	 *
	 * @param object managed instance against which method is executed,
	 * @param args optional managed method invocation arguments.
	 * @param <T> returned value type.
	 * @return value returned by method or null for void.
	 * @throws AuthorizationException if method is private and {@link SecurityContext} is not authenticated.
	 * @throws IllegalArgumentException if invocation arguments does not match method signature.
	 * @throws InvocationException if method execution fails for whatever reason.
	 */
	@SuppressWarnings("unchecked")
	@Override
	public <T> T invoke(Object object, Object... args) throws AuthorizationException, IllegalArgumentException, InvocationException {
		if (remotelyAccessible && !isPublic() && !container.isAuthenticated()) {
			log.info("Reject not authenticated access to |%s|.", method);
			throw new AuthorizationException();
		}

		// arguments processor converts <args> to empty array if it is null
		// it can be null if on invocation chain there is Proxy invoked with no arguments
		args = argumentsProcessor.preProcessArguments(this, args);

		if (object instanceof Proxy) {
			// if object is a Java Proxy does not apply method services implemented by below block
			// instead directly invoke Java method on the Proxy instance
			// container will call again this method but with the real object instance, in which case executes the next logic
			try {
				return (T) method.invoke(object, args);
			} catch (InvocationTargetException e) {
				throw new InvocationException(e.getTargetException());
			} catch (IllegalAccessException e) {
				throw new BugError("Illegal access on method with accessibility set true.");
			}
		}

		if (meter == null) {
			try {
				return (T) invoker.invoke(object, args);
			} catch (InvocationTargetException e) {
				throw new InvocationException(e.getTargetException());
			} catch (IllegalAccessException e) {
				throw new BugError("Illegal access on method with accessibility set true.");
			}
		}

		meter.incrementInvocationsCount();
		meter.startProcessing();
		T returnValue = null;
		try {
			returnValue = (T) invoker.invoke(object, args);
		} catch (InvocationTargetException e) {
			meter.incrementExceptionsCount();
			throw new InvocationException(e.getTargetException());
		} catch (IllegalAccessException e) {
			// this condition is a bug; do not increment exceptions count
			throw new BugError("Illegal access on method with accessibility set true.");
		}
		meter.stopProcessing();
		return returnValue;
	}

	@Override
	public String getRequestPath() {
		if (!remotelyAccessible) {
			throw new BugError("Attempt to retrieve request URI path from local managed method |%s|.", this);
		}
		return requestPath;
	}

	@Override
	public boolean isVoid() {
		return Types.isVoid(method.getReturnType());
	}

	@Override
	public boolean isRemotelyAccessible() {
		return remotelyAccessible;
	}

	@Override
	public boolean isPublic() {
		return access == Access.PUBLIC;
	}

	@Override
	public boolean isTransactional() {
		return transactional;
	}

	@Override
	public boolean isImmutable() {
		return immutable;
	}

	@Override
	public boolean isAsynchronous() {
		return asynchronous;
	}

	@Override
	public String getCronExpression() {
		return cronExpression;
	}

	/**
	 * Returns a string describing this managed method.
	 */
	@Override
	public String toString() {
		return signature;
	}

	// --------------------------------------------------------------------------------------------
	// INVOKER CLASSES

	/**
	 * Nested utility interface used to invoke outer managed method while adding method level services. This interface follows a
	 * strategy pattern and is used to define behaviors implemented by inner classes:
	 *

	 *
{@link DefaultInvoker} default implementation used when no additional services are defined,
	 *
{@link InterceptedInvoker} implements the actual intercepted invocation for managed methods tagged with
	 * {@link Intercepted} annotation,
	 *
{@link AsyncInvoker} execute method in a separated thread of execution.
	 *

	 *
	 * @author Iulian Rotaru
	 * @version final
	 */
	private static interface Invoker {
		/**
		 * Invoke managed method adding specific services. Services are implementation specific.
		 *
		 * @param object instance on which managed method is invoked,
		 * @param arguments invocation arguments.
		 * @return value returned by managed method execution.
		 * @throws IllegalArgumentException if invocation arguments does not match method formal parameters,
		 * @throws IllegalAccessException never happen but needed by Java method signature,
		 * @throws InvocationTargetException if method invocation fails.
		 */
		Object invoke(Object object, Object[] arguments) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
	}

	/**
	 * Default invoker used when no additional services are defined on outer managed method. This is the default {@link Invoker}
	 * implementation.
	 *
	 * @author Iulian Rotaru
	 * @version final
	 */
	private final class DefaultInvoker implements Invoker {
		/**
		 * Invoke Java reflective method from outer managed method without any fuss.
		 *
		 * @param object instance on which method is invoked,
		 * @param args invocation arguments.
		 * @return value returned by method execution.
		 * @throws IllegalArgumentException if invocation arguments does not match method formal parameters,
		 * @throws IllegalAccessException never happen but needed by Java method signature,
		 * @throws InvocationTargetException if method invocation fails.
		 */
		@Override
		public Object invoke(Object object, Object[] args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
			return ManagedMethod.this.method.invoke(object, args);
		}
	};

	/**
	 * Intercepted invoker used for managed method tagged as intercepted. This invoker constructor has an interceptor class as
	 * argument that is used to initialize interceptor instance, {@link #interceptor}. Interceptor instance can be fresh created
	 * or reused, if interceptor is a managed class with non local scope.
	 *

	 * Interceptor execution occurs on {@link #invoke(Object, Object[])} execution either before or after actual method
	 * invocation, depending on interface interceptor is implementing. It is legal for interceptor to implement both
	 * {@link PreInvokeInterceptor} and {@link PostInvokeInterceptor} interfaces.
	 *
	 * @version final
	 */
	private final class InterceptedInvoker implements Invoker {
		/**
		 * Interceptor instance. If interceptor class is managed this instance can be reused depending on managed class scope.
		 * Otherwise it is created at every invoker construction.
		 */
		private final Interceptor interceptor;

		/**
		 * Initialize internal interceptor instance for given interceptor class. If interceptor class is managed, instance can
		 * be reused if instance scope is not {@link InstanceScope#LOCAL}. Otherwise a new interceptor instance is created.
		 *
		 * @param interceptorClass interceptor class, managed or plain Java class.
		 */
		@SuppressWarnings("unchecked")
		public InterceptedInvoker(Class<? extends Interceptor> interceptorClass) {
			if (container.isManagedClass(interceptorClass)) {
				interceptor = container.getInstance((Class<? super Interceptor>) interceptorClass);
			} else {
				interceptor = Classes.newInstance(interceptorClass);
			}
		}

		/**
		 * Invoke outer managed method and returns execution value. Takes care to execute interceptor instance before, after or
		 * both before and after method execution, depending on interceptor implemented interfaces.
		 *
		 * @param object instance on which method is invoked,
		 * @param args invocation arguments.
		 * @return value returned by method execution.
		 * @throws IllegalArgumentException if invocation arguments does not match method formal parameters,
		 * @throws IllegalAccessException never happen but needed by Java method signature,
		 * @throws InvocationTargetException if method invocation or interceptor execution fails. Exception target value is
		 *             execution fail root cause.
		 */
		@Override
		public Object invoke(Object object, Object[] args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
			final ManagedMethodSPI managedMethod = ManagedMethod.this;

			if (interceptor instanceof PreInvokeInterceptor) {
				log.debug("Execute pre-invoke interceptor for method |%s|.", managedMethod);
				PreInvokeInterceptor preInvokeInterceptor = (PreInvokeInterceptor) interceptor;
				try {
					preInvokeInterceptor.preInvoke(managedMethod, args);
				} catch (Exception e) {
					log.error("Exception on pre-invoke interceptor for method |%s|: %s", managedMethod, e);
					throw new InvocationTargetException(e);
				}
			}

			Object returnValue = ManagedMethod.this.method.invoke(object, args);

			if (interceptor instanceof PostInvokeInterceptor) {
				log.debug("Execute post-invoke interceptor for method |%s|.", managedMethod);
				PostInvokeInterceptor postInvokeInterceptor = (PostInvokeInterceptor) interceptor;
				try {
					postInvokeInterceptor.postInvoke(managedMethod, args, returnValue);
				} catch (Exception e) {
					log.error("Exception on post-invoke interceptor for method |%s|: %s", managedMethod, e);
					throw new InvocationTargetException(e);
				}
			}
			return returnValue;
		}
	}

	/**
	 * Execute {@link Invoker} instance in a separated thread of execution. This invoker implementation is a decorator; it gets
	 * an externally created invoker and adds asynchronous execution.
	 *

	 * Current implementation is based on {@link AsyncTask} and has no means to join after starting asynchronous
	 * tasks. If invoker executed asynchronously fails the only option to be notified is application logger.
	 *
	 * @version final
	 */
	private final class AsyncInvoker implements Invoker {
		/** Reference to externally created invoker to be executed asynchronously. */
		private Invoker invoker;

		/**
		 * Create asynchronous invoker for given invoker instance.
		 *
		 * @param invoker invoker instance to be executed asynchronously.
		 */
		public AsyncInvoker(Invoker invoker) {
			this.invoker = invoker;
		}

		/**
		 * Create and start a new asynchronous task to execute {@link #invoker}. This method returns immediately whit always
		 * null value and does not throw any exception. If underlying invoker execution fail for any reason the only option to
		 * be notified is application logger.
		 *
		 * @param object instance on which method is invoked,
		 * @param args invocation arguments.
		 * @return always returns null.
		 */
		@Override
		public Object invoke(final Object object, final Object[] args) {
			AsyncTask<Void> asyncTask = new AsyncTask<Void>() {
				@Override
				protected Void execute() throws Throwable {
					AsyncInvoker.this.invoker.invoke(object, args);
					return null;
				}
			};
			asyncTask.start();
			return null;
		}
	}

	// --------------------------------------------------------------------------------------------
	// INVOCATION METER

	/**
	 * Invocation meters implementation for managed methods. Standard usage pattern is to update meter state on
	 * {@link ManagedMethod#invoke(Object, Object...)} execution, like in sample code below. Also, not present in pseudo-code,
	 * increment exceptions count if method logic fails.
	 *
	 *

	 * class ManagedMethod {
	 * 	void invoke() {
	 * 		this.meter.incrementInvocationsCount();
	 * 		this.meter.startProcessing();
	 * 		// process method logic
	 * 		this.meter.stopProcessing();
	 * 	}
	 * }
	 *

	 *
	 * Managed method meter class implements {@link InvocationMeter} interface for meter counters reading.
	 *
	 * @author Iulian Rotaru
	 * @version final
	 */
	private static class Meter implements InvocationMeter {
		/** Declaring class for instrumented managed method. */
		private Class<?> declaringClass;

		/** Instrumented method signature. */
		private String methodSignature;

		/** Method invocations count. Updated by {@link #incrementInvocationsCount()}. */
		private long invocationsCount;

		/** Method exceptions count. Updated by {@link #incrementExceptionsCount()}. */
		private long exceptionsCount;

		/** Total processing time. */
		private long totalProcessingTime;

		/** Maximum value of processing time. */
		private long maxProcessingTime;

		/** Timestamp for processing time recording start. */
		private long startProcessingTimestamp;

		/**
		 * Construct meter instance. Store declaring class and initialize method signature.
		 *
		 * @param method instrumented method.
		 */
		Meter(Method method) {
			this.declaringClass = method.getDeclaringClass();

			List<String> formalParameters = new ArrayList<String>();
			for (Class<?> formalParameter : method.getParameterTypes()) {
				formalParameters.add(formalParameter.getSimpleName());
			}

			this.methodSignature = String.format(SIMPLE_NAME_FORMAT, method.getName(), Strings.join(formalParameters, ','));
		}

		@Override
		public Class<?> getMethodDeclaringClass() {
			return declaringClass;
		}

		@Override
		public String getMethodSignature() {
			return methodSignature;
		}

		@Override
		public long getInvocationsCount() {
			return invocationsCount;
		}

		@Override
		public long getExceptionsCount() {
			return exceptionsCount;
		}

		@Override
		public long getTotalProcessingTime() {
			return totalProcessingTime;
		}

		@Override
		public long getMaxProcessingTime() {
			return maxProcessingTime;
		}

		@Override
		public void reset() {
			invocationsCount = 0;
			exceptionsCount = 0;
			totalProcessingTime = 0;
			maxProcessingTime = 0;
		}

		@Override
		public String toExternalForm() {
			long totalProcessingTime = this.totalProcessingTime / 1000000;
			long averageProcessingTime = invocationsCount != 0 ? totalProcessingTime / invocationsCount : 0;

			StringBuilder sb = new StringBuilder();
			sb.append(declaringClass.getCanonicalName());
			sb.append("#");
			sb.append(methodSignature);
			sb.append(": ");
			sb.append(invocationsCount);
			sb.append(": ");
			sb.append(exceptionsCount);
			sb.append(": ");
			sb.append(totalProcessingTime);
			sb.append(": ");
			sb.append(maxProcessingTime / 1000000);
			sb.append(": ");
			sb.append(averageProcessingTime);
			return sb.toString();
		}

		/** Increment invocation count on every method invocation, including those failed. */
		private void incrementInvocationsCount() {
			++invocationsCount;
		}

		/** Increment exceptions count for every failed method invocation. */
		private void incrementExceptionsCount() {
			++exceptionsCount;
		}

		/**
		 * Start recording of processing time. It is called just before method execution. Update internal
		 * {@link #startProcessingTimestamp} to current system time.
		 */
		private void startProcessing() {
			startProcessingTimestamp = System.nanoTime();
		}

		/** Stop recording of processing time and update total and maximum processing time. */
		private void stopProcessing() {
			long processingTime = System.nanoTime() - startProcessingTimestamp;
			totalProcessingTime += processingTime;
			if (maxProcessingTime < processingTime) {
				maxProcessingTime = processingTime;
			}
		}
	}
}


/*
 * Copyright (c) 2002 Cunningham & Cunningham, Inc.
 * Copyright (c) 2009-2015 by Jochen Wierum & Cologne Intelligence
 *
 * This file is part of FitGoodies.
 *
 * FitGoodies is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * FitGoodies is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with FitGoodies.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.cologneintelligence.fitgoodies.util;

import java.lang.reflect.Method;

/**
 * With this class is it possible to invoke a given method of a given class
 * by reflection until the method returns true or a given maxTime is reached.
 * <p>
 * The method must have the signature {@code boolean method()}.
 *
 * @author kmussawisade
 */
public class WaitForResult {
    private final SystemTime systemTime;

    private long lastElapsedTime;
    private boolean lastCallWasSuccessful;

    /**
     * Initializes a new object.
     *
     * @param systemTime SystemTime implementation to use
     */
    public WaitForResult(final SystemTime systemTime) {
        this.systemTime = systemTime;
    }

    /**
     * Initializes a new object. The object will use {@link SystemTime}.
     */
    public WaitForResult() {
        this(new SystemTime());
    }

    private boolean invokeMethod(Object target, Method method) {
        try {
            return (boolean) method.invoke(target);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Invokes the method multiple times, either until it returns true, or
     * until maxtime is over. After each invoke, the class waits
     * {@code sleepTime} milliseconds.
     *
     * @param target    Object which receives the call.
     * @param method    Method to invoke on {@code target}.
     * @param maxTime   Maximum time to wait before causing a failure.
     * @param sleepTime Time to sleep between method calls.
     */
    public void wait(Object target, Method method, long maxTime, long sleepTime) {
        long remaining = maxTime;

        lastCallWasSuccessful = invokeMethod(target, method);
        while (!lastCallWasSuccessful && remaining > 0) {
            systemTime.sleep(sleepTime);
            remaining -= sleepTime;
            lastCallWasSuccessful = invokeMethod(target, method);
        }

        lastElapsedTime = maxTime - remaining;
    }

    /**
     * Gets the elapsed time of the last call to {@link #wait(Object, Method, long, long)}.
     *
     * @return the elapsed time in milliseconds
     */
    public long getLastElapsedTime() {
        return lastElapsedTime;
    }

    public boolean lastCallWasSuccessful() {
        return lastCallWasSuccessful;
    }
}


package js.http.form;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;

import javax.servlet.http.HttpServletRequest;

import js.converter.ConverterRegistry;
import js.log.Log;
import js.log.LogFactory;
import js.util.Files;
import js.util.Params;
import js.util.Strings;

import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

/**
 * Multipart form iterator implementation. This class implements both {@link FormIterator} direct interface and inherited ones,
 * {@link Iterator} and {@link Iterable}. This implementation has side effects: it takes care to close form parts while
 * traversing form iterator so that application does not need to.
 *

 * Although form iterator implementation is public it is designed for framework internal usage; it helps injecting
 * {@link FormIterator} on resource or service methods. Container uses this class to create and initialize FormIterator then
 * inject it as managed method argument.
 *
 *

 * @Override
 * public void saveCustomer(FormIterator form) {
 * 	for (Part part : form) {
 * 		// handle form parts one by one, in sequence
 * 		...
 * 		// no need to close the form part
 * 	}
 * }
 *

 *
 * @author Iulian Rotaru
 * @version final
 */
public final class FormIteratorImpl implements FormIterator {
	/** Class logger. */
	private static final Log log = LogFactory.getLog(FormIterator.class);

	/** File item iterator. */
	private FileItemIterator fileItemIterator;

	/** Form currently processing part. */
	private PartImpl currentPart;

	/**
	 * Construct form iterator with data from given HTTP request. HTTP request Content-Type type should be
	 * multipart and boundary parameter should be defined.
	 *
	 * @param httpRequest HTTP request carrying the multipart form.
	 * @throws IOException if HTTP request read operation fails, content type has no boundary or form body parsing fails.
	 */
	public FormIteratorImpl(HttpServletRequest httpRequest) throws IOException {
		ServletFileUpload upload = new ServletFileUpload();
		try {
			this.fileItemIterator = upload.getItemIterator(httpRequest);
		} catch (FileUploadException e) {
			throw new IOException(e);
		}
	}

	/**
	 * Test constructor.
	 *
	 * @param fileItemIterator mock for underlying file item iterator.
	 */
	public FormIteratorImpl(FileItemIterator fileItemIterator) {
		this.fileItemIterator = fileItemIterator;
	}

	@Override
	public void forEach(FormHandler handler) throws IOException {
		for (Part part : this) {
			if (part instanceof UploadStream) {
				UploadStreamImpl uploadStream = (UploadStreamImpl) part;
				InputStream inputStream = uploadStream.openStream();
				try {
					handler.stream(uploadStream.getName(), uploadStream.getFileName(), uploadStream.getContentType(), inputStream);
				} catch (IOException e) {
					throw e;
				} catch (Throwable t) {
					throw new RuntimeException(t);
				} finally {
					Files.close(uploadStream);
				}
			} else {
				FormField formField = (FormField) part;
				try {
					handler.field(formField.getName(), formField.getValue());
				} catch (Throwable t) {
					throw new RuntimeException(t);
				}
			}
		}
	}

	/**
	 * Return form parts iterator.
	 *
	 * @return this form parts iterator.
	 */
	@Override
	public Iterator<Part> iterator() {
		return this;
	}

	/**
	 * Test if there are more parts on this form iterator. This implementation has side effects: it takes care to close form
	 * parts while traversing form iterator.
	 *
	 * @return true if there is at least one available form part.
	 */
	@Override
	public boolean hasNext() {
		try {
			if (currentPart != null) {
				currentPart.close();
			}
			if (!fileItemIterator.hasNext()) {
				return false;
			}
			FileItemStream fileItemStream = fileItemIterator.next();
			if (fileItemStream.isFormField()) {
				currentPart = new FormFieldImpl(fileItemStream);
			} else {
				currentPart = new UploadStreamImpl(fileItemStream);
			}
			return true;
		} catch (IOException | FileUploadException e) {
			log.error(e);
		}
		return false;
	}

	/**
	 * Get next part from this form iterator. This method should be called only if {@link #hasNext()} returns true. Otherwise
	 * behavior is not defined.
	 *
	 * @return form part, never null if {@link #hasNext()} was true.
	 */
	@Override
	public Part next() {
		return currentPart;
	}

	/** Unsupported operation. */
	@Override
	public void remove() {
		throw new UnsupportedOperationException();
	}

	// --------------------------------------------------------------------------------------------
	// INNER CLASSES

	/**
	 * Form part implementation.
	 *
	 * @author Iulian Rotaru
	 * @version final
	 */
	private abstract class PartImpl implements Part, Closeable {
		/** File item stream that facilitates access to form part stream. */
		protected final FileItemStream fileItemStream;
		/** Part name, normalized to Java member like syntax. */
		private final String name;

		public PartImpl(FileItemStream fileItemStream) {
			this.fileItemStream = fileItemStream;
			this.name = Strings.toMemberName(this.fileItemStream.getFieldName());
		}

		@Override
		public String getName() {
			return name;
		}

		@Override
		public boolean is(String name) {
			Params.notNull(name, "Name");
			return name.equals(Strings.toMemberName(name));
		}

		@Override
		public String getContentType() {
			return fileItemStream.getContentType();
		}
	}

	/**
	 * Form field implementation.
	 *
	 * @author Iulian Rotaru
	 * @version final
	 */
	private class FormFieldImpl extends PartImpl implements FormField {
		/** Form field string value. */
		private final String value;

		/**
		 * Create form filed instance and load its value from underlying form. Closes file item stream after loading this form
		 * field value.
		 *
		 * @param fileItemStream file item stream that facilitates access to form part stream.
		 * @throws IOException if fail to read form part stream.
		 */
		public FormFieldImpl(FileItemStream fileItemStream) throws IOException {
			super(fileItemStream);
			this.value = Strings.load(fileItemStream.openStream());
		}

		@Override
		public String getValue() {
			return value;
		}

		@Override
		public <T> T getValue(Class<T> type) {
			return ConverterRegistry.getConverter().asObject(getValue(), type);
		}

		/** This method does nothing since file item stream is closed by constructor after value loading. */
		@Override
		public void close() {
		}
	}

	/**
	 * Upload stream implementation.
	 *
	 * @author Iulian Rotaru
	 * @version final
	 */
	private class UploadStreamImpl extends PartImpl implements UploadStream {
		/** Form upload input stream. */
		private InputStream inputStream;

		/**
		 * Create form upload stream instance wrapping underlying form part stream.
		 *
		 * @param fileItemStream file item stream that facilitates access to form part stream.
		 * @throws IOException if fail to read form part stream.
		 */
		public UploadStreamImpl(FileItemStream fileItemStream) throws IOException {
			super(fileItemStream);
		}

		@Override
		public String getFileName() {
			String fileName = fileItemStream.getName();
			int i = fileName.lastIndexOf('/');
			if (i == -1) {
				i = fileName.lastIndexOf('\\');
			}
			return i != -1 ? fileName.substring(i + 1) : fileName;
		}

		@Override
		public InputStream openStream() throws IOException {
			inputStream = fileItemStream.openStream();
			return inputStream;
		}

		/**
		 * Close underlying input stream, if opened by {@link #openStream()}. Otherwise this method does nothing.
		 *
		 * @throws IOException if input stream close operation fails.
		 */
		@Override
		public void close() throws IOException {
			if (inputStream != null) {
				inputStream.close();
			}
		}
	}
}