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;
17
18 import java.util.HashMap;
19 import java.util.LinkedList;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Queue;
23 import java.util.Set;
24
25 import net.sf.jguiraffe.di.BeanContext;
26 import net.sf.jguiraffe.di.BeanInitializer;
27 import net.sf.jguiraffe.di.BeanProvider;
28 import net.sf.jguiraffe.di.BeanStore;
29 import net.sf.jguiraffe.di.ClassLoaderProvider;
30 import net.sf.jguiraffe.di.Dependency;
31 import net.sf.jguiraffe.di.DependencyProvider;
32 import net.sf.jguiraffe.di.InjectionException;
33 import net.sf.jguiraffe.di.InvocationHelper;
34
35 /**
36 * <p>
37 * An internally used implementation of the <code>DependencyProvider</code>
38 * interface.
39 * </p>
40 * <p>
41 * An instance of this class is created by the default {@link BeanContext}
42 * implementation at the beginning of a transaction. Its task is to obtain and
43 * cache all dependencies of the currently requested bean. This can fail if a
44 * dependency cannot be resolved or one of the dependent bean providers is
45 * already locked by another transaction. In this case the transaction has to be
46 * suspended.
47 * </p>
48 * <p>
49 * If an instance could be successfully initialized, it allows access to all
50 * dependent bean providers - and no more. So the
51 * <code>getDependentBean()</code> method can be implemented in a meaningful
52 * way.
53 * </p>
54 * <p>
55 * This class works closely together with the default bean context
56 * implementation. From there it also obtains a map with the registered class
57 * loaders. Instances are confined to a single thread, so there is no need of
58 * being thread-safe.
59 * </p>
60 *
61 * @author Oliver Heger
62 * @version $Id: DefaultDependencyProvider.java 205 2012-01-29 18:29:57Z oheger $
63 */
64 class DefaultDependencyProvider implements DependencyProvider
65 {
66 /**
67 * Holds a reference to the associated bean context implementation.
68 */
69 private final DefaultBeanContext beanContext;
70
71 /** Stores the resolved dependencies. */
72 private Map<Dependency, BeanProvider> dependencyMap;
73
74 /** A list with the registered bean initializers. */
75 private List<BeanInitializer> initializers;
76
77 /** The context that is responsible for a bean creation event. */
78 private BeanContext creationBeanContext;
79
80 /** The invocation helper used by this instance. */
81 private InvocationHelper invocationHelper;
82
83 /**
84 * Creates a new instance of {@code DefaultDependencyProvider} and
85 * initializes it with the bean context implementation it assists.
86 *
87 * @param context the associated bean context
88 */
89 public DefaultDependencyProvider(DefaultBeanContext context)
90 {
91 beanContext = context;
92 }
93
94 /**
95 * Returns the {@code BeanContext} that is responsible for a bean creation.
96 *
97 * @return the {@code BeanContext} responsible for a bean creation
98 */
99 public BeanContext getCreationBeanContext()
100 {
101 return (creationBeanContext != null) ? creationBeanContext
102 : getBeanContext();
103 }
104
105 /**
106 * Sets the {@code BeanContext} that is responsible for a bean creation.
107 * This method is intended to be called by a bean creation listener to set
108 * the correct context when there is a complex structure of combined and
109 * wrapped bean contexts.
110 *
111 * @param creationBeanContext the creation bean context
112 */
113 public void setCreationBeanContext(BeanContext creationBeanContext)
114 {
115 this.creationBeanContext = creationBeanContext;
116 }
117
118 /**
119 * Initializes this object. This method resolves the specified dependency
120 * and, recursively, all dependencies it depends on. The found bean
121 * providers are stored in an internal data structure. If a dependency
122 * cannot be resolved, a <code>InjectionException</code> exception is
123 * thrown. The return value indicates whether all found bean providers are
124 * unlocked. If a locked provider is found, the method is aborted, and
125 * <b>false</b> is returned.
126 *
127 * @param dependency the initial dependency to resolve
128 * @param store the starting bean store
129 * @return a flag whether all dependencies can be locked
130 * @throws InjectionException if a dependency cannot be resolved
131 */
132 public boolean initialize(Dependency dependency, BeanStore store)
133 {
134 Map<Dependency, BeanProvider> depMap = new HashMap<Dependency, BeanProvider>();
135 Queue<Dependency> q = new LinkedList<Dependency>();
136 q.add(dependency);
137
138 while (!q.isEmpty())
139 {
140 Dependency d = q.remove();
141 if (!depMap.containsKey(d))
142 {
143 // not yet processed => resolve this dependency
144 BeanProvider provider = d.resolve(store, this);
145 if (provider.getLockID() != null)
146 {
147 // already locked, initialization fails
148 return false;
149 }
150 depMap.put(d, provider);
151
152 // Process the dependencies of this provider
153 Set<Dependency> dependencies = provider.getDependencies();
154 if (dependencies != null)
155 {
156 q.addAll(dependencies);
157 }
158 }
159 }
160
161 dependencyMap = depMap;
162 initInvocationHelper(store);
163 return true;
164 }
165
166 /**
167 * Returns the associated bean context implementation.
168 *
169 * @return the bean context
170 */
171 public DefaultBeanContext getBeanContext()
172 {
173 return beanContext;
174 }
175
176 /**
177 * Marks all resolved dependencies as locked. This method can be called
178 * after a successful invocation of <code>initialize()</code>. It sets the
179 * lock IDs for all involved <code>BeanProvider</code>s, so that they cannot
180 * take part in another concurrent transaction. A second call to this method
181 * with the argument <b>null</b> releases all locks.
182 *
183 * @param lockID the lock ID
184 */
185 public void lock(Long lockID)
186 {
187 for (BeanProvider p : getDependencyMap().values())
188 {
189 p.setLockID(lockID);
190 }
191 }
192
193 /**
194 * Returns the bean provider specified by the given dependency. This
195 * dependency must belong to the set of dependencies fetched during the
196 * <code>initialize()</code> method.
197 *
198 * @param dependency the dependency
199 * @return the bean provider specified by this dependency
200 * @throws InjectionException if the dependency cannot be resolved
201 */
202 public BeanProvider getDependentProvider(Dependency dependency)
203 {
204 BeanProvider result = dependencyMap.get(dependency);
205 if (result == null)
206 {
207 throw new InjectionException(
208 "Invalid dependency! This dependency does not belong to "
209 + "the current transaction: " + dependency);
210 }
211 return result;
212 }
213
214 /**
215 * Returns the bean of the bean provider specified by the given dependency.
216 * This dependency must belong to the set of dependencies fetched during the
217 * <code>initialize()</code> method.
218 *
219 * @param dependency the dependency
220 * @return the bean from the bean provider specified by this dependency
221 * @throws InjectionException if the dependency cannot be resolved
222 */
223 public Object getDependentBean(Dependency dependency)
224 {
225 return getDependentProvider(dependency).getBean(this);
226 }
227
228 /**
229 * Returns a set with the names of all registered class loaders. This
230 * implementation delegates to the internal <code>ClassLoaderProvider</code>
231 * .
232 *
233 * @return a set with the names of the registered class loaders
234 */
235 public Set<String> classLoaderNames()
236 {
237 return getCLP().classLoaderNames();
238 }
239
240 /**
241 * Returns the class loader with the given symbolic name. This
242 * implementation delegates to the internal <code>ClassLoaderProvider</code>
243 * .
244 *
245 * @param name the name of the class loader
246 * @return the class loader with this name
247 * @throws InjectionException if the class loader cannot be resolved
248 */
249 public ClassLoader getClassLoader(String name)
250 {
251 return getCLP().getClassLoader(name);
252 }
253
254 /**
255 * Returns the default class loader name. This implementation delegates to
256 * the internal <code>ClassLoaderProvider</code>.
257 *
258 * @return the default class loader name
259 */
260 public String getDefaultClassLoaderName()
261 {
262 return getCLP().getDefaultClassLoaderName();
263 }
264
265 /**
266 * Registers a class loader under a symbolic name. This implementation
267 * delegates to the internal <code>ClassLoaderProvider</code>.
268 *
269 * @param name the symbolic name for the class loader
270 * @param loader the class loader to register
271 */
272 public void registerClassLoader(String name, ClassLoader loader)
273 {
274 getCLP().registerClassLoader(name, loader);
275 }
276
277 /**
278 * Sets the name of the default class loader. This implementation delegates
279 * to the internal <code>ClassLoaderProvider</code>.
280 *
281 * @param loaderName the new default class loader name
282 */
283 public void setDefaultClassLoaderName(String loaderName)
284 {
285 getCLP().setDefaultClassLoaderName(loaderName);
286 }
287
288 /**
289 * Loads the class with the specified name using the class loader identified
290 * by the given symbolic reference. This implementation delegates to the
291 * internal <code>ClassLoaderProvider</code>.
292 *
293 * @param name the class of the name to be loaded
294 * @param loaderRef determines the class loader to be used
295 * @return the loaded class
296 * @throws InjectionException if the class cannot be loaded
297 */
298 public Class<?> loadClass(String name, String loaderRef)
299 {
300 return getCLP().loadClass(name, loaderRef);
301 }
302
303 /**
304 * Adds a bean initializer. This initializer will be invoked by the
305 * <code>invokeInitializers()</code> method.
306 *
307 * @param initializer the initializer to be added (may be <b>null</b>, then
308 * this operation has no effect)
309 */
310 public void addInitializer(BeanInitializer initializer)
311 {
312 if (initializer != null)
313 {
314 if (initializers == null)
315 {
316 initializers = new LinkedList<BeanInitializer>();
317 }
318 initializers.add(initializer);
319 }
320 }
321
322 /**
323 * Invokes all registered bean initializers. If an initializer throws an
324 * exception, it is caught and saved. The remaining initializers will be
325 * invoked. After that the exception is re-thrown.
326 *
327 * @throws InjectionException if an error occurs
328 */
329 public void invokeInitializers()
330 {
331 if (initializers != null)
332 {
333 InjectionException thrownEx = null;
334
335 for (BeanInitializer init : initializers)
336 {
337 try
338 {
339 init.initialize(this);
340 }
341 catch (InjectionException iex)
342 {
343 if (thrownEx == null)
344 {
345 thrownEx = iex;
346 }
347 }
348 catch (Exception ex)
349 {
350 if (thrownEx == null)
351 {
352 thrownEx = new InjectionException(ex);
353 }
354 }
355 }
356
357 if (thrownEx != null)
358 {
359 throw thrownEx;
360 }
361 }
362 }
363
364 /**
365 * Tests whether a bean is available.
366 *
367 * @param dependency the dependency
368 * @return a flag whether this dependency can be currently resolved
369 */
370 public boolean isBeanAvailable(Dependency dependency)
371 {
372 return getDependentProvider(dependency).isBeanAvailable();
373 }
374
375 /**
376 * Returns the {@code InvocationHelper} object. This implementation obtains
377 * the helper object from the associated bean context.
378 *
379 * @return the {@code InvocationHelper} object
380 */
381 public InvocationHelper getInvocationHelper()
382 {
383 return invocationHelper;
384 }
385
386 /**
387 * Notifies this dependency provider about the creation of a new bean. This
388 * method is called by a {@code BeanProvider} when it has to create a bean
389 * to satisfy the current request.
390 *
391 * @param bean the new bean
392 * @param provider the {@code BeanProvider} responsible for the creation
393 */
394 public void beanCreated(Object bean, BeanProvider provider)
395 {
396 getBeanContext().beanCreated(bean, provider, this);
397 }
398
399 /**
400 * Returns a map with the resolved dependencies.
401 *
402 * @return the internal dependency map
403 */
404 Map<Dependency, BeanProvider> getDependencyMap()
405 {
406 return dependencyMap;
407 }
408
409 /**
410 * Convenience method for obtaining the class loader provider from the
411 * associated bean context.
412 *
413 * @return the class loader provider
414 */
415 private ClassLoaderProvider getCLP()
416 {
417 return getBeanContext().getClassLoaderProvider();
418 }
419
420 /**
421 * Initializes the internal {@link InvocationHelper}. This method is called
422 * after a successful initialization.
423 *
424 * @param store the current bean store
425 */
426 private void initInvocationHelper(BeanStore store)
427 {
428 invocationHelper =
429 new InvocationHelper(DefaultBeanStore.fetchConversionHelper(
430 store, true));
431 }
432 }