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.Set;
20
21 import net.sf.jguiraffe.di.Dependency;
22 import net.sf.jguiraffe.di.DependencyProvider;
23 import net.sf.jguiraffe.di.impl.ClassDescription;
24 import net.sf.jguiraffe.di.impl.MethodInvocation;
25
26 /**
27 * <p>
28 * A specialized <code>BeanProvider</code> that creates beans by invoking a
29 * method.
30 * </p>
31 * <p>
32 * This <code>BeanProvider</code> implementation is initialized with a
33 * {@link MethodInvocation} object describing the method to be invoked. Optional
34 * a {@link Dependency} can be provided for the instance, on which the method is
35 * to be invoked (if no such dependency is specified, the method to be invoked
36 * must be static). This class is intended for the following use cases:
37 * <ul>
38 * <li>It can be used for obtaining instances that are not created using a
39 * constructor, but by invoking a static factory method. In this case the passed
40 * in <code>MethodInvocation</code> must refer to this factory method and must
41 * provide the parameters for this invocation.</li>
42 * <li>Another use case is the handling of <em>factory classes</em>: Some
43 * objects are not directly created, but a method on a specific factory class is
44 * used for this purpose. An example could be a <code>Connection</code> object
45 * that is obtained from a <code>DataSource</code>. In this case the factory is
46 * defined as a separate bean, and a dependency to this bean is specified to
47 * this bean provider class. The <code>MethodInvocation</code> defines the
48 * method of this factory bean that has to be called for obtaining an instance.</li>
49 * </ul>
50 * </p>
51 * <p>
52 * As is true for other <code>SimpleBeanProvider</code>s, this provider is
53 * intended to be used together with a life-cycle-aware
54 * <code>BeanProvider</code>. It does not provide any life-cycle support on its
55 * own.
56 * </p>
57 *
58 * @author Oliver Heger
59 * @version $Id: MethodInvocationBeanProvider.java 205 2012-01-29 18:29:57Z oheger $
60 */
61 public class MethodInvocationBeanProvider extends SimpleBeanProvider
62 {
63 /** Stores the method invocation object. */
64 private MethodInvocation invocation;
65
66 /** Stores the target dependency. */
67 private Dependency targetDependency;
68
69 /** Stores a class description for the managed bean. */
70 private ClassDescription beanClassDescription;
71
72 /**
73 * Creates a new instance of <code>MethodInvocationBeanProvider</code> and
74 * initializes it with the dependency to the target object (on which the
75 * method is to be invoked) and the description of the method invocation.
76 *
77 * @param targetBean the dependency to the target bean (can be <b>null</b>)
78 * @param methodInv the description of the method to invoke (must not be
79 * <b>null</b>)
80 * @throws IllegalArgumentException if the <code>MethodInvocation</code>
81 * is undefined
82 */
83 public MethodInvocationBeanProvider(Dependency targetBean,
84 MethodInvocation methodInv)
85 {
86 this(targetBean, methodInv, null);
87 }
88
89 /**
90 * Creates a new instance of <code>MethodInvocationBeanProvider</code> and
91 * initializes it with the description of the method to invoke. No
92 * dependency for the target instance is provided, so a static method must
93 * be specified.
94 *
95 * @param methodInv the description of the method to invoke (must not be
96 * <b>null</b>)
97 * @throws IllegalArgumentException if the <code>MethodInvocation</code>
98 * is undefined
99 */
100 public MethodInvocationBeanProvider(MethodInvocation methodInv)
101 {
102 this(null, methodInv);
103 }
104
105 /**
106 * Creates a new instance of <code>MethodInvocationBeanProvider</code> and
107 * initializes it with the dependency to the target object (on which the
108 * method is to be invoked), the description of the method invocation, and
109 * the class of the managed bean. Depending on the used dependency and/or
110 * the method invocation, it is not always possible to determine the class
111 * of the managed bean. With this constructor it can be explicitly set.
112 *
113 * @param targetBean the dependency to the target bean (can be <b>null</b>)
114 * @param methodInv the description of the method to invoke (must not be
115 * <b>null</b>)
116 * @param beanClsDsc a description of the class of the managed bean (can be
117 * <b>null</b>)
118 * @throws IllegalArgumentException if the <code>MethodInvocation</code>
119 * is undefined
120 */
121 public MethodInvocationBeanProvider(Dependency targetBean,
122 MethodInvocation methodInv, ClassDescription beanClsDsc)
123 {
124 if (methodInv == null)
125 {
126 throw new IllegalArgumentException(
127 "Method invocation must not be null!");
128 }
129 invocation = methodInv;
130 targetDependency = targetBean;
131 beanClassDescription = beanClsDsc;
132 }
133
134 /**
135 * Returns the dependency to the target bean. This can be <b>null</b> if
136 * none is provided.
137 *
138 * @return the dependency to the target bean
139 */
140 public Dependency getTargetDependency()
141 {
142 return targetDependency;
143 }
144
145 /**
146 * Returns the <code>MethodInvocation</code> for the method to be invoked.
147 *
148 * @return the method invocation
149 */
150 public MethodInvocation getInvocation()
151 {
152 return invocation;
153 }
154
155 /**
156 * Returns the description of the class of the managed bean. If a class
157 * description was explicitly set in the constructor, this description is
158 * returned. Otherwise this implementation tries to obtain the bean class
159 * from the {@link MethodInvocation} object owned by this provider. Because
160 * a class is optional for a method invocation, result can be <b>null</b>.
161 * To avoid this, a valid {@link ClassDescription} should always be set
162 * either on the <code>MethodInvocation</code> or when an instance of this
163 * class is constructed.
164 *
165 * @return a class description of the managed bean
166 */
167 public ClassDescription getBeanClassDescription()
168 {
169 return (beanClassDescription != null) ? beanClassDescription
170 : getInvocation().getTargetClass();
171 }
172
173 /**
174 * Returns the bean managed by this provider. If a target dependency is set,
175 * the corresponding bean will be fetched from the specified dependency
176 * provider and used as target instance for the method invocation. Otherwise
177 * the method is invoked on a <b>null</b> instance, so this has to be a
178 * static method.
179 *
180 * @param dependencyProvider the dependency provider
181 * @return the bean managed by this provider
182 */
183 public Object getBean(DependencyProvider dependencyProvider)
184 {
185 Object target = (getTargetDependency() != null) ? dependencyProvider
186 .getDependentBean(getTargetDependency()) : null;
187 Object result = getInvocation().invoke(dependencyProvider, target);
188 return (target != null) ? target : result;
189 }
190
191 /**
192 * Returns the bean class of the bean managed by this provider. This
193 * implementation delegates to <code>getBeanClassDescription()</code> for
194 * obtaining the description of the bean class. If this is successful, the
195 * class is resolved; otherwise result is <b>null</b>.
196 *
197 * @param dependencyProvider the dependency provider
198 * @return the class of the managed bean
199 * @see #getBeanClassDescription()
200 */
201 public Class<?> getBeanClass(DependencyProvider dependencyProvider)
202 {
203 ClassDescription cdesc = getBeanClassDescription();
204 return (cdesc != null) ? cdesc.getTargetClass(dependencyProvider)
205 : null;
206 }
207
208 /**
209 * Returns the dependencies of this bean provider. These are the parameter
210 * dependencies of the <code>MethodInvocation</code> object. If a
211 * dependency for the target instance is provided, it will also be contained
212 * in the returned set.
213 *
214 * @return a set with the dependencies of this provider
215 */
216 @Override
217 public Set<Dependency> getDependencies()
218 {
219 Set<Dependency> result = new HashSet<Dependency>(getInvocation()
220 .getParameterDependencies());
221 if (getTargetDependency() != null)
222 {
223 result.add(getTargetDependency());
224 }
225 return result;
226 }
227
228 /**
229 * Returns a string representation for this object. This string will contain
230 * information about the method invoked by this bean provider. If a target
231 * dependency is provided, it will also be output.
232 *
233 * @return a string for this object
234 */
235 @Override
236 public String toString()
237 {
238 StringBuilder buf = new StringBuilder(getClass().getName());
239 buf.append('@').append(System.identityHashCode(this));
240 buf.append("[ method = ").append(getInvocation());
241 if (getTargetDependency() != null)
242 {
243 buf.append(" target = ").append(getTargetDependency());
244 }
245 return buf.toString();
246 }
247 }