View Javadoc

1   /*
2    * Copyright 2006-2016 The JGUIraffe Team.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License")
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package net.sf.jguiraffe.di.impl.providers;
17  
18  import java.util.HashSet;
19  import java.util.List;
20  import java.util.Set;
21  
22  import net.sf.jguiraffe.di.BeanContext;
23  import net.sf.jguiraffe.di.BeanInitializer;
24  import net.sf.jguiraffe.di.BeanProvider;
25  import net.sf.jguiraffe.di.Dependency;
26  import net.sf.jguiraffe.di.DependencyProvider;
27  import net.sf.jguiraffe.di.InjectionException;
28  import net.sf.jguiraffe.di.InvocationHelper;
29  import net.sf.jguiraffe.di.impl.HelperInvocations;
30  import net.sf.jguiraffe.di.impl.Invokable;
31  
32  /**
33   * <p>
34   * An abstract base class for {@code BeanProvider} implementations with
35   * life-cycle support.
36   * </p>
37   * <p>
38   * A {@code LifeCycleBeanProvider} has the following properties:
39   * <ul>
40   * <li>A (usually simple) {@link BeanProvider} for actually
41   * creating an instance of the managed bean.</li>
42   * <li>An optional {@link Invokable} object for initializing the
43   * newly created bean instance.</li>
44   * </ul>
45   * </p>
46   * <p>
47   * This base class provides basic functionality for the creation and
48   * initialization of beans. It also implements the methods related to life-cycle
49   * support of the {@code BeanProvider} interface in a meaningful way.
50   * There are two methods that are intended to be called by concrete sub classes:
51   * {@code createBean()} and {@code fetchBean()}.
52   * </p>
53   * <p>
54   * {@code createBean()}, as its name implies, creates a new instance of
55   * the managed bean class. This is done by invoking the
56   * {@code BeanProvider} for creating new beans. After that the
57   * {@code Invokable} object is called on the newly created bean. The
58   * creation of a bean through the bean provider may cause an endless loop if
59   * there are cyclic dependencies (e.g. bean A needs bean B as a constructor
60   * argument and vice verse). Such cycles are detected and lead to a
61   * {@code InjectionException} exception being thrown.
62   * </p>
63   * <p>
64   * The {@code fetchBean()} method checks whether a bean instance has
65   * already been created. If this is the case, it is directly returned. Otherwise
66   * {@code createBean()} is called for creating a new instance. Depending
67   * on their semantics derived classes decide, which of these methods to call.
68   * This decision must be implemented in the {@code getBean()} method; all
69   * other methods defined by the {@code BeanProvider} interface are
70   * already implemented by this base class.
71   * </p>
72   * <p>
73   * Implementation note: This class is intended to be used together with a
74   * correct implementation of the {@code BeanContext} interface. It is not
75   * thread-safe by itself, but if the bean context handles transactions properly,
76   * it can be used in an environment with multiple threads accessing the bean
77   * context concurrently.
78   * </p>
79   *
80   * @author Oliver Heger
81   * @version $Id: LifeCycleBeanProvider.java 208 2012-02-11 20:57:33Z oheger $
82   */
83  public abstract class LifeCycleBeanProvider implements BeanProvider,
84          BeanInitializer
85  {
86      /** Stores the bean provider for creating a bean. */
87      private final BeanProvider beanCreator;
88  
89      /** Stores the invocation object for initializing the bean. */
90      private final Invokable beanInitializer;
91  
92      /**
93       * Stores the currently processed dependency. This field is used for
94       * producing meaningful error messages in case of cyclic dependencies, which
95       * cannot be resolved.
96       */
97      private Dependency currentDependency;
98  
99      /** Stores the latest created bean. */
100     private Object bean;
101 
102     /** Stores the lock ID of the current transaction. */
103     private Long lockID;
104 
105     /** A flag whether a bean is currently initialized. */
106     private boolean initializing;
107 
108     /** A flag whether a bean is currently created. */
109     private boolean creating;
110 
111     /** A flag whether a bean instance has been successfully created. */
112     private volatile boolean instanceCreated;
113 
114     /**
115      * Creates a new instance of {@code LifeCycleBeanProvider} and
116      * initializes it with the {@code BeanProvider} for creating the bean
117      * instance and an {@code Invokable} for initializing it.
118      *
119      * @param createProvider the bean provider for creating a bean instance
120      * (must not be <b>null</b>)
121      * @param initinv an optional invocation object with initialization code for
122      * the newly created bean
123      * @throws IllegalArgumentException if the bean provider is <b>null</b>
124      */
125     protected LifeCycleBeanProvider(BeanProvider createProvider,
126             Invokable initinv)
127     {
128         if (createProvider == null)
129         {
130             throw new IllegalArgumentException(
131                     "Creation bean provider must not be null!");
132         }
133         beanCreator = createProvider;
134         beanInitializer =
135                 (initinv != null) ? initinv
136                         : HelperInvocations.IDENTITY_INVOCATION;
137     }
138 
139     /**
140      * Creates a new instance of {@code LifeCycleBeanProvider} and
141      * initializes it with the {@code BeanProvider} for creating the bean
142      * instance.
143      *
144      * @param createProvider the bean provider for creating a bean instance
145      * (must not be <b>null</b>)
146      * @throws IllegalArgumentException if the bean provider is <b>null</b>
147      */
148     protected LifeCycleBeanProvider(BeanProvider createProvider)
149     {
150         this(createProvider, null);
151     }
152 
153     /**
154      * Returns the {@code BeanProvider} that is responsible for creating
155      * a new bean instance.
156      *
157      * @return the bean provider for creating new bean instances
158      */
159     public BeanProvider getBeanCreator()
160     {
161         return beanCreator;
162     }
163 
164     /**
165      * Returns the {@code Invokable} object responsible for initializing
166      * the newly created bean. This method never returns <b>null</b>. If no
167      * initializer was set, a default initializer object (that does not have any
168      * effect) is returned.
169      *
170      * @return the invocation object used for initialization
171      */
172     public Invokable getBeanInitializer()
173     {
174         return beanInitializer;
175     }
176 
177     /**
178      * Returns the class of the bean managed by this provider. This class is
179      * determined by the {@code BeanProvider} for creating new bean
180      * instances.
181      *
182      * @param dependencyProvider the dependency provider
183      * @return the class of the managed bean
184      * @throws InjectionException if an error occurs determining the class
185      */
186     public Class<?> getBeanClass(DependencyProvider dependencyProvider)
187     {
188         return getBeanCreator().getBeanClass(dependencyProvider);
189     }
190 
191     /**
192      * Returns the dependencies of this bean provider. This implementation
193      * obtains the dependencies of the creation bean provider and the invocation
194      * object for initialization and returns a union.
195      *
196      * @return the dependencies of this bean provider
197      */
198     public Set<Dependency> getDependencies()
199     {
200         List<Dependency> initDeps =
201                 getBeanInitializer().getParameterDependencies();
202         Set<Dependency> creatorDeps = getBeanCreator().getDependencies();
203 
204         if (initDeps.isEmpty())
205         {
206             return creatorDeps;
207         }
208         else
209         {
210             Set<Dependency> result = new HashSet<Dependency>();
211             if (creatorDeps != null)
212             {
213                 result.addAll(creatorDeps);
214             }
215             result.addAll(initDeps);
216             return result;
217         }
218     }
219 
220     /**
221      * Returns the ID of the locking transaction. If this method returns a non
222      * <b>null</b> value, this bean provider must not be used by a concurrent
223      * transaction.
224      *
225      * @return the ID of the locking transaction
226      */
227     public Long getLockID()
228     {
229         return lockID;
230     }
231 
232     /**
233      * Sets the ID of the locking transaction. This indicates that this bean
234      * provider is blocked by the specified transaction.
235      *
236      * @param lid the ID of the locking transaction
237      */
238     public void setLockID(Long lid)
239     {
240         lockID = lid;
241     }
242 
243     /**
244      * Returns a flag whether the bean managed by this provider is available.
245      * This implementation checks whether the bean is currently created. If this
246      * is the case, it is not available.
247      *
248      * @return a flag whether the managed bean is available
249      */
250     public boolean isBeanAvailable()
251     {
252         return !creating;
253     }
254 
255     /**
256      * Notifies this {@code BeanProvider} that it is no longer needed. This is
257      * just an empty dummy implementation. Derived classes that support shutdown
258      * handling have to override it.
259      *
260      * @param depProvider the {@code DependencyProvider}
261      */
262     public void shutdown(DependencyProvider depProvider)
263     {
264     }
265 
266     /**
267      * Performs initialization. This method is called by the dependency provider
268      * if initialization has to be delayed because of cyclic dependencies. It
269      * invokes the initializer.
270      *
271      * @param dependencyProvider the dependency provider
272      */
273     public void initialize(DependencyProvider dependencyProvider)
274     {
275         try
276         {
277             Object initBean =
278                     fetchInitializedBeanInstance(bean, dependencyProvider);
279             bean = initBean;
280             instanceCreated = true;
281         }
282         finally
283         {
284             initializing = false;
285         }
286     }
287 
288     /**
289      * Returns a string representation of this object. This string also contains
290      * information about the bean provider used for creating the bean and the
291      * invocation object for the initialization.
292      *
293      * @return a string for this object
294      */
295     @Override
296     public String toString()
297     {
298         StringBuilder buf = new StringBuilder();
299         buf.append(getClass());
300         buf.append('@').append(System.identityHashCode(this));
301         buf.append("[ creator = ").append(getBeanCreator());
302         buf.append(" initializer = ").append(getBeanInitializer());
303         buf.append(']');
304         return buf.toString();
305     }
306 
307     /**
308      * Creates and initializes a new bean instance. This method is reentrant,
309      * which is necessary if there are cycles in the dependencies. In this case
310      * a bean that is only partly initialized may be returned. It is also
311      * possible that the cycles cannot be resolved, then an exception is thrown.
312      *
313      * @param dependencyProvider the dependency provider
314      * @return the newly created bean instance
315      * @throws InjectionException if an error occurs
316      */
317     protected Object createBean(DependencyProvider dependencyProvider)
318     {
319         if (creating)
320         {
321             // a disallowed reentrant call
322             throw new InjectionException("Unresolvable cyclic dependency "
323                     + currentDependency + " in bean provider " + this);
324         }
325 
326         if (!initializing)
327         {
328             // no reentrant call
329             creating = true;
330             initializing = true;
331             boolean canInit = true;
332             try
333             {
334                 bean = doCreateBean(createDiagnosticDependencyProvider(dependencyProvider));
335                 creating = false;
336 
337                 // Can initialization be directly performed?
338                 canInit = canInitialize(dependencyProvider);
339                 if (canInit)
340                 {
341                     bean =
342                             fetchInitializedBeanInstance(bean,
343                                     dependencyProvider);
344                 }
345                 else
346                 {
347                     // No, postpone it
348                     dependencyProvider.addInitializer(this);
349                 }
350 
351                 if (canInit)
352                 {
353                     instanceCreated = true;
354                 }
355             }
356             finally
357             {
358                 creating = false;
359                 if (canInit)
360                 {
361                     initializing = false;
362                 }
363             }
364         }
365 
366         return bean;
367     }
368 
369     /**
370      * Returns the bean instance created by this provider. If no instance has
371      * been created yet, this is done now by invoking {@code createBean()}.
372      * Otherwise the bean instance is directly returned. If the dependencies
373      * contain cyclic references, it is possible that a bean instance is
374      * returned, which has not yet been fully initialized. Cycles that cannot be
375      * resolved cause an exception.
376      *
377      * @param dependencyProvider the dependency provider
378      * @return the bean instance managed by this provider
379      * @throws InjectionException if an error occurs
380      */
381     protected Object fetchBean(DependencyProvider dependencyProvider)
382     {
383         return (bean != null) ? bean : createBean(dependencyProvider);
384     }
385 
386     /**
387      * Checks whether initialization of the bean is now possible. This method
388      * tests whether all dependencies required for the bean's initialization are
389      * currently available.
390      *
391      * @param dependencyProvider the dependency provider
392      * @return a flag whether initialization can now be performed
393      */
394     protected boolean canInitialize(DependencyProvider dependencyProvider)
395     {
396         List<Dependency> initDeps = getBeanInitializer()
397                 .getParameterDependencies();
398         if (initDeps != null)
399         {
400             for (Dependency d : initDeps)
401             {
402                 if (!dependencyProvider.isBeanAvailable(d))
403                 {
404                     return false;
405                 }
406             }
407         }
408         return true;
409     }
410 
411     /**
412      * Returns a flag whether a bean instance has already been created. This can
413      * be used by derived classes for adapting their behavior. This
414      * implementation returns <b>true</b> if and only if a bean instance has
415      * been created and fully initialized.
416      *
417      * @return a flag whether a bean instance has been created
418      */
419     protected boolean hasBean()
420     {
421         return instanceCreated;
422     }
423 
424     /**
425      * Resets any so far created bean. This method can be called by derived
426      * classes to reset this bean provider. In a following call to
427      * {@code fetchBean()} a completely new bean will be created.
428      */
429     protected void resetBean()
430     {
431         bean = null;
432         instanceCreated = false;
433     }
434 
435     /**
436      * Creates a new bean instance. This method is called by
437      * {@code createBean()} for actually creating the bean.
438      *
439      * @param dependencyProvider the dependency provider
440      * @return the new bean instance
441      * @throws InjectionException if an error occurs
442      */
443     protected Object doCreateBean(DependencyProvider dependencyProvider)
444     {
445         return getBeanCreator().getBean(
446                 new DiagnosticDependencyProvider(dependencyProvider));
447     }
448 
449     /**
450      * Initializes a newly created bean instance. This method is called by
451      * {@code createBean()} for each new bean instance. This implementation
452      * invokes the initializer and notifies the {@code DependencyProvider} about
453      * the creation of a new bean.
454      *
455      * @param bean the bean to initialize
456      * @param dependencyProvider the dependency provider
457      * @throws InjectionException if an error occurs
458      * @deprecated This method is not called any more during bean creation;
459      * instead {@link #fetchInitializedBeanInstance(Object, DependencyProvider)}
460      * is invoked
461      */
462     @Deprecated
463     protected void initBean(Object bean, DependencyProvider dependencyProvider)
464     {
465         getBeanInitializer().invoke(dependencyProvider, bean);
466         dependencyProvider.beanCreated(bean, this);
467     }
468 
469     /**
470      * Returns the initialized bean instance. This method is called by
471      * {@code createBean()} for each new bean instance. Its purpose is to
472      * execute the initializer script on the bean. This implementation invokes
473      * the initializer and notifies the {@code DependencyProvider} about the
474      * creation of a new bean. Note that the bean returned by the initializer
475      * script is the result of this method. Thus it is possible that a different
476      * bean than passed to the method becomes the managed bean of this provider.
477      * However, if the initializer returns <b>null</b>, the passed in bean is
478      * returned.
479      *
480      * @param bean the bean to initialize
481      * @param dependencyProvider the dependency provider
482      * @return the bean instance
483      * @throws InjectionException if an error occurs
484      * @since 1.1
485      */
486     protected Object fetchInitializedBeanInstance(Object bean,
487             DependencyProvider dependencyProvider)
488     {
489         Object initBean = getBeanInitializer().invoke(dependencyProvider, bean);
490         dependencyProvider.beanCreated(initBean, this);
491         return (initBean != null) ? initBean : bean;
492     }
493 
494     /**
495      * Creates a specialized dependency provider with the ability of generating
496      * meaningful error messages in case of cyclic dependencies. This method is
497      * called by {@code createBean()} before the creator is invoked.
498      *
499      * @param wrappedProvider the dependency provider to be wrapped
500      * @return the diagnostic dependency provider
501      */
502     DependencyProvider createDiagnosticDependencyProvider(
503             DependencyProvider wrappedProvider)
504     {
505         return new DiagnosticDependencyProvider(wrappedProvider);
506     }
507 
508     /**
509      * A specialized {@code DependencyProvider} implementation that is
510      * used for generating meaningful error messages for cyclic dependencies.
511      * This implementation stores the currently processed dependency in a member
512      * field. If later a reentrant call to {@code createBean()} happens,
513      * we are able to determine, which dependency caused the problem.
514      */
515     private class DiagnosticDependencyProvider implements DependencyProvider
516     {
517         /** Stores the wrapped dependency provider. */
518         private final DependencyProvider wrappedProvider;
519 
520         /**
521          * Creates a new instance of {@code DiagnosticDependencyProvider}
522          * and sets the dependency provider to be wrapped.
523          *
524          * @param d the wrapped dependency provider
525          */
526         public DiagnosticDependencyProvider(DependencyProvider d)
527         {
528             wrappedProvider = d;
529         }
530 
531         /**
532          * Returns the bean for the given dependency. This implementation saves
533          * the dependency in an internal field and then delegates to the wrapped
534          * provider.
535          *
536          * @param dependency the dependency
537          * @return the dependent bean
538          * @throws InjectionException if an error occurs
539          */
540         public Object getDependentBean(Dependency dependency)
541         {
542             currentDependency = dependency;
543             return wrappedProvider.getDependentBean(dependency);
544         }
545 
546         /**
547          * Loads the specified class. This implementation just delegates to the
548          * wrapped provider.
549          *
550          * @param name the name of the class
551          * @param loaderRef the name of the class loader to use
552          * @return the resolved class
553          * @throws InjectionException if an error occurs
554          */
555         public Class<?> loadClass(String name, String loaderRef)
556         {
557             return wrappedProvider.loadClass(name, loaderRef);
558         }
559 
560         /**
561          * Adds an initializer. This implementation just delegates to the
562          * wrapped provider.
563          *
564          * @param initializer the initializer to add
565          */
566         public void addInitializer(BeanInitializer initializer)
567         {
568             wrappedProvider.addInitializer(initializer);
569         }
570 
571         /**
572          * Checks whether a dependency is currently available. This
573          * implementation just delegates to the wrapped provider.
574          *
575          * @param dependency the dependency in question
576          * @return a flag whether this dependency is available
577          */
578         public boolean isBeanAvailable(Dependency dependency)
579         {
580             return wrappedProvider.isBeanAvailable(dependency);
581         }
582 
583         /**
584          * Returns a set with the names of the registered class loaders. This
585          * implementation just delegates to the wrapped provider.
586          *
587          * @return a set with the names of the registered class loaders
588          */
589         public Set<String> classLoaderNames()
590         {
591             return wrappedProvider.classLoaderNames();
592         }
593 
594         /**
595          * Returns the class loader that was registered under the given symbolic
596          * name. This implementation just delegates to the wrapped provider.
597          *
598          * @param name the name of the class loader
599          * @return the class loader for this name
600          * @throws InjectionException if no such class loader can be found
601          */
602         public ClassLoader getClassLoader(String name)
603         {
604             return wrappedProvider.getClassLoader(name);
605         }
606 
607         /**
608          * Returns the default class loader name. This implementation just
609          * delegates to the wrapped provider.
610          *
611          * @return the name of the default class loader
612          */
613         public String getDefaultClassLoaderName()
614         {
615             return wrappedProvider.getDefaultClassLoaderName();
616         }
617 
618         /**
619          * Registers a class loader under a symbolic name. This implementation
620          * just delegates to the wrapped provider.
621          *
622          * @param name the symbolic name
623          * @param loader the class loader
624          */
625         public void registerClassLoader(String name, ClassLoader loader)
626         {
627             wrappedProvider.registerClassLoader(name, loader);
628         }
629 
630         /**
631          * Sets the name of the default class loader. This implementation just
632          * delegates to the wrapped provider.
633          *
634          * @param loaderName the new default class loader name
635          */
636         public void setDefaultClassLoaderName(String loaderName)
637         {
638             wrappedProvider.setDefaultClassLoaderName(loaderName);
639         }
640 
641         /**
642          * A new bean was created. This implementation just delegates to the
643          * wrapped provider.
644          *
645          * @param bean the new bean
646          * @param provider the responsible bean provider
647          */
648         public void beanCreated(Object bean, BeanProvider provider)
649         {
650             wrappedProvider.beanCreated(bean, provider);
651         }
652 
653         /**
654          * Sets the bean context responsible for a bean creation. This
655          * implementation just delegates to the wrapped provider.
656          *
657          * @param context the responsible bean context
658          */
659         public void setCreationBeanContext(BeanContext context)
660         {
661             wrappedProvider.setCreationBeanContext(context);
662         }
663 
664         /**
665          * Returns the current {@code InvocationHelper} object. This
666          * implementation just delegates to the wrapped provider.
667          *
668          * @return the {@code InvocationHelper}
669          */
670         public InvocationHelper getInvocationHelper()
671         {
672             return wrappedProvider.getInvocationHelper();
673         }
674     }
675 }