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 }