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

net.sf.jguiraffe.di.impl.providers.LifeCycleBeanProvider Maven / Gradle / Ivy

There is a newer version: 1.4.1
Show newest version
/*
 * Copyright 2006-2010 The JGUIraffe Team.
 *
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.sf.jguiraffe.di.impl.providers;

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

import net.sf.jguiraffe.di.BeanContext;
import net.sf.jguiraffe.di.BeanInitializer;
import net.sf.jguiraffe.di.BeanProvider;
import net.sf.jguiraffe.di.Dependency;
import net.sf.jguiraffe.di.DependencyProvider;
import net.sf.jguiraffe.di.InjectionException;
import net.sf.jguiraffe.di.InvocationHelper;
import net.sf.jguiraffe.di.impl.Invokable;
import net.sf.jguiraffe.di.impl.NullInvocation;

/**
 * 

* An abstract base class for BeanProvider implementations with * life-cycle support. *

*

* A LifeCycleBeanProvider has the following properties: *

    *
  • A (usually simple) {@link BeanProvider} for actually * creating an instance of the managed bean.
  • *
  • An optional {@link Invokable} object for initializing the * newly created bean instance.
  • *
*

*

* This base class provides basic functionality for the creation and * initialization of beans. It also implements the methods related to life-cycle * support of the BeanProvider interface in a meaningful way. * There are two methods that are intended to be called by concrete sub classes: * createBean() and fetchBean(). *

*

* createBean(), as its name implies, creates a new instance of * the managed bean class. This is done by invoking the * BeanProvider for creating new beans. After that the * Invokable object is called on the newly created bean. The * creation of a bean through the bean provider may cause an endless loop if * there are cyclic dependencies (e.g. bean A needs bean B as a constructor * argument and vice verse). Such cycles are detected and lead to a * InjectionException exception being thrown. *

*

* The fetchBean() method checks whether a bean instance has * already been created. If this is the case, it is directly returned. Otherwise * createBean() is called for creating a new instance. Depending * on their semantics derived classes decide, which of these methods to call. * This decision must be implemented in the getBean() method; all * other methods defined by the BeanProvider interface are * already implemented by this base class. *

*

* Implementation note: This class is intended to be used together with a * correct implementation of the BeanContext interface. It is not * thread-safe by itself, but if the bean context handles transactions properly, * it can be used in an environment with multiple threads accessing the bean * context concurrently. *

* * @author Oliver Heger * @version $Id: LifeCycleBeanProvider.java 195 2010-08-30 19:54:41Z oheger $ */ public abstract class LifeCycleBeanProvider implements BeanProvider, BeanInitializer { /** Stores the bean provider for creating a bean. */ private final BeanProvider beanCreator; /** Stores the invocation object for initializing the bean. */ private final Invokable beanInitializer; /** * Stores the currently processed dependency. This field is used for * producing meaningful error messages in case of cyclic dependencies, which * cannot be resolved. */ private Dependency currentDependency; /** Stores the latest created bean. */ private Object bean; /** Stores the lock ID of the current transaction. */ private Long lockID; /** A flag whether a bean is currently initialized. */ private boolean initializing; /** A flag whether a bean is currently created. */ private boolean creating; /** A flag whether a bean instance has been successfully created. */ private volatile boolean instanceCreated; /** * Creates a new instance of LifeCycleBeanProvider and * initializes it with the BeanProvider for creating the bean * instance and an Invokable for initializing it. * * @param createProvider the bean provider for creating a bean instance * (must not be null) * @param initinv an optional invocation object with initialization code for * the newly created bean * @throws IllegalArgumentException if the bean provider is null */ protected LifeCycleBeanProvider(BeanProvider createProvider, Invokable initinv) { if (createProvider == null) { throw new IllegalArgumentException( "Creation bean provider must not be null!"); } beanCreator = createProvider; beanInitializer = (initinv != null) ? initinv : NullInvocation.INSTANCE; } /** * Creates a new instance of LifeCycleBeanProvider and * initializes it with the BeanProvider for creating the bean * instance. * * @param createProvider the bean provider for creating a bean instance * (must not be null) * @throws IllegalArgumentException if the bean provider is null */ protected LifeCycleBeanProvider(BeanProvider createProvider) { this(createProvider, null); } /** * Returns the BeanProvider that is responsible for creating * a new bean instance. * * @return the bean provider for creating new bean instances */ public BeanProvider getBeanCreator() { return beanCreator; } /** * Returns the Invokable object responsible for initializing * the newly created bean. This method never returns null. If no * initializer was set, a default initializer object (that does not have any * effect) is returned. * * @return the invocation object used for initialization */ public Invokable getBeanInitializer() { return beanInitializer; } /** * Returns the class of the bean managed by this provider. This class is * determined by the BeanProvider for creating new bean * instances. * * @param dependencyProvider the dependency provider * @return the class of the managed bean * @throws InjectionException if an error occurs determining the class */ public Class getBeanClass(DependencyProvider dependencyProvider) { return getBeanCreator().getBeanClass(dependencyProvider); } /** * Returns the dependencies of this bean provider. This implementation * obtains the dependencies of the creation bean provider and the invocation * object for initialization and returns a union. * * @return the dependencies of this bean provider */ public Set getDependencies() { List initDeps = getBeanInitializer().getParameterDependencies(); if (initDeps.isEmpty()) { return getBeanCreator().getDependencies(); } else { Set result = new HashSet(); result.addAll(getBeanCreator().getDependencies()); result.addAll(initDeps); return result; } } /** * Returns the ID of the locking transaction. If this method returns a non * null value, this bean provider must not be used by a concurrent * transaction. * * @return the ID of the locking transaction */ public Long getLockID() { return lockID; } /** * Sets the ID of the locking transaction. This indicates that this bean * provider is blocked by the specified transaction. * * @param lid the ID of the locking transaction */ public void setLockID(Long lid) { lockID = lid; } /** * Returns a flag whether the bean managed by this provider is available. * This implementation checks whether the bean is currently created. If this * is the case, it is not available. * * @return a flag whether the managed bean is available */ public boolean isBeanAvailable() { return !creating; } /** * Notifies this {@code BeanProvider} that it is no longer needed. This is * just an empty dummy implementation. Derived classes that support shutdown * handling have to override it. * * @param depProvider the {@code DependencyProvider} */ public void shutdown(DependencyProvider depProvider) { } /** * Performs initialization. This method is called by the dependency provider * if initialization has to be delayed because of cyclic dependencies. It * invokes the initializer. * * @param dependencyProvider the dependency provider */ public void initialize(DependencyProvider dependencyProvider) { try { initBean(bean, dependencyProvider); instanceCreated = true; } finally { initializing = false; } } /** * Returns a string representation of this object. This string also contains * information about the bean provider used for creating the bean and the * invocation object for the initialization. * * @return a string for this object */ @Override public String toString() { StringBuilder buf = new StringBuilder(); buf.append(getClass()); buf.append('@').append(System.identityHashCode(this)); buf.append("[ creator = ").append(getBeanCreator()); buf.append(" initializer = ").append(getBeanInitializer()); buf.append(']'); return buf.toString(); } /** * Creates and initializes a new bean instance. This method is reentrant, * which is necessary if there are cycles in the dependencies. In this case * a bean that is only partly initialized may be returned. It is also * possible that the cycles cannot be resolved, then an exception is thrown. * * @param dependencyProvider the dependency provider * @return the newly created bean instance * @throws InjectionException if an error occurs */ protected Object createBean(DependencyProvider dependencyProvider) { if (creating) { // a disallowed reentrant call throw new InjectionException("Unresolvable cyclic dependency " + currentDependency + " in bean provider " + this); } if (!initializing) { // no reentrant call creating = true; initializing = true; boolean canInit = true; try { bean = doCreateBean(createDiagnosticDependencyProvider(dependencyProvider)); creating = false; // Can initialization be directly performed? canInit = canInitialize(dependencyProvider); if (canInit) { initBean(bean, dependencyProvider); } else { // No, postpone it dependencyProvider.addInitializer(this); } if (canInit) { instanceCreated = true; } } finally { creating = false; if (canInit) { initializing = false; } } } return bean; } /** * Returns the bean instance created by this provider. If no instance has * been created yet, this is done now by invoking createBean(). * Otherwise the bean instance is directly returned. If the dependencies * contain cyclic references, it is possible that a bean instance is * returned, which has not yet been fully initialized. Cycles that cannot be * resolved cause an exception. * * @param dependencyProvider the dependency provider * @return the bean instance managed by this provider * @throws InjectionException if an error occurs */ protected Object fetchBean(DependencyProvider dependencyProvider) { return (bean != null) ? bean : createBean(dependencyProvider); } /** * Checks whether initialization of the bean is now possible. This method * tests whether all dependencies required for the bean's initialization are * currently available. * * @param dependencyProvider the dependency provider * @return a flag whether initialization can now be performed */ protected boolean canInitialize(DependencyProvider dependencyProvider) { List initDeps = getBeanInitializer() .getParameterDependencies(); if (initDeps != null) { for (Dependency d : initDeps) { if (!dependencyProvider.isBeanAvailable(d)) { return false; } } } return true; } /** * Returns a flag whether a bean instance has already been created. This can * be used by derived classes for adapting their behavior. This * implementation returns true if and only if a bean instance has * been created and fully initialized. * * @return a flag whether a bean instance has been created */ protected boolean hasBean() { return instanceCreated; } /** * Resets any so far created bean. This method can be called by derived * classes to reset this bean provider. In a following call to * fetchBean() a completely new bean will be created. */ protected void resetBean() { bean = null; instanceCreated = false; } /** * Creates a new bean instance. This method is called by * createBean() for actually creating the bean. * * @param dependencyProvider the dependency provider * @return the new bean instance * @throws InjectionException if an error occurs */ protected Object doCreateBean(DependencyProvider dependencyProvider) { return getBeanCreator().getBean( new DiagnosticDependencyProvider(dependencyProvider)); } /** * Initializes a newly created bean instance. This method is called by * {@code createBean()} for each new bean instance. This implementation * invokes the initializer and notifies the {@code DependencyProvider} about * the creation of a new bean. * * @param bean the bean to initialize * @param dependencyProvider the dependency provider * @throws InjectionException if an error occurs */ protected void initBean(Object bean, DependencyProvider dependencyProvider) { getBeanInitializer().invoke(dependencyProvider, bean); dependencyProvider.beanCreated(bean, this); } /** * Creates a specialized dependency provider with the ability of generating * meaningful error messages in case of cyclic dependencies. This method is * called by createBean() before the creator is invoked. * * @param wrappedProvider the dependency provider to be wrapped * @return the diagnostic dependency provider */ DependencyProvider createDiagnosticDependencyProvider( DependencyProvider wrappedProvider) { return new DiagnosticDependencyProvider(wrappedProvider); } /** * A specialized DependencyProvider implementation that is * used for generating meaningful error messages for cyclic dependencies. * This implementation stores the currently processed dependency in a member * field. If later a reentrant call to createBean() happens, * we are able to determine, which dependency caused the problem. */ private class DiagnosticDependencyProvider implements DependencyProvider { /** Stores the wrapped dependency provider. */ private final DependencyProvider wrappedProvider; /** * Creates a new instance of DiagnosticDependencyProvider * and sets the dependency provider to be wrapped. * * @param d the wrapped dependency provider */ public DiagnosticDependencyProvider(DependencyProvider d) { wrappedProvider = d; } /** * Returns the bean for the given dependency. This implementation saves * the dependency in an internal field and then delegates to the wrapped * provider. * * @param dependency the dependency * @return the dependent bean * @throws InjectionException if an error occurs */ public Object getDependentBean(Dependency dependency) { currentDependency = dependency; return wrappedProvider.getDependentBean(dependency); } /** * Loads the specified class. This implementation just delegates to the * wrapped provider. * * @param name the name of the class * @param loaderRef the name of the class loader to use * @return the resolved class * @throws InjectionException if an error occurs */ public Class loadClass(String name, String loaderRef) { return wrappedProvider.loadClass(name, loaderRef); } /** * Adds an initializer. This implementation just delegates to the * wrapped provider. * * @param initializer the initializer to add */ public void addInitializer(BeanInitializer initializer) { wrappedProvider.addInitializer(initializer); } /** * Checks whether a dependency is currently available. This * implementation just delegates to the wrapped provider. * * @param dependency the dependency in question * @return a flag whether this dependency is available */ public boolean isBeanAvailable(Dependency dependency) { return wrappedProvider.isBeanAvailable(dependency); } /** * Returns a set with the names of the registered class loaders. This * implementation just delegates to the wrapped provider. * * @return a set with the names of the registered class loaders */ public Set classLoaderNames() { return wrappedProvider.classLoaderNames(); } /** * Returns the class loader that was registered under the given symbolic * name. This implementation just delegates to the wrapped provider. * * @param name the name of the class loader * @return the class loader for this name * @throws InjectionException if no such class loader can be found */ public ClassLoader getClassLoader(String name) { return wrappedProvider.getClassLoader(name); } /** * Returns the default class loader name. This implementation just * delegates to the wrapped provider. * * @return the name of the default class loader */ public String getDefaultClassLoaderName() { return wrappedProvider.getDefaultClassLoaderName(); } /** * Registers a class loader under a symbolic name. This implementation * just delegates to the wrapped provider. * * @param name the symbolic name * @param loader the class loader */ public void registerClassLoader(String name, ClassLoader loader) { wrappedProvider.registerClassLoader(name, loader); } /** * Sets the name of the default class loader. This implementation just * delegates to the wrapped provider. * * @param loaderName the new default class loader name */ public void setDefaultClassLoaderName(String loaderName) { wrappedProvider.setDefaultClassLoaderName(loaderName); } /** * A new bean was created. This implementation just delegates to the * wrapped provider. * * @param bean the new bean * @param provider the responsible bean provider */ public void beanCreated(Object bean, BeanProvider provider) { wrappedProvider.beanCreated(bean, provider); } /** * Sets the bean context responsible for a bean creation. This * implementation just delegates to the wrapped provider. * * @param context the responsible bean context */ public void setCreationBeanContext(BeanContext context) { wrappedProvider.setCreationBeanContext(context); } /** * Returns the current {@code InvocationHelper} object. This * implementation just delegates to the wrapped provider. * * @return the {@code InvocationHelper} */ public InvocationHelper getInvocationHelper() { return wrappedProvider.getInvocationHelper(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy