1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.grouplens.grapht.reflect.internal;
22
23 import org.apache.commons.lang3.reflect.MethodUtils;
24 import org.grouplens.grapht.ConstructionException;
25 import org.grouplens.grapht.Instantiator;
26 import org.grouplens.grapht.LifecycleManager;
27 import org.grouplens.grapht.NullDependencyException;
28 import org.grouplens.grapht.reflect.Desire;
29 import org.grouplens.grapht.reflect.InjectionPoint;
30 import org.grouplens.grapht.util.LogContext;
31 import org.grouplens.grapht.util.Preconditions;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 import javax.annotation.PostConstruct;
36 import java.lang.reflect.Constructor;
37 import java.lang.reflect.InvocationTargetException;
38 import java.lang.reflect.Method;
39 import java.util.HashMap;
40 import java.util.List;
41 import java.util.Map;
42
43
44
45
46
47
48 public class ClassInstantiator implements Instantiator {
49 private static final Logger logger = LoggerFactory.getLogger(ClassInstantiator.class);
50
51 private final Class<?> type;
52 private final List<Desire> desires;
53 private final Map<Desire, Instantiator> providers;
54 private final LifecycleManager manager;
55
56
57
58
59
60
61
62
63
64
65 public ClassInstantiator(Class<?> type, List<Desire> desires,
66 Map<Desire,Instantiator> providers,
67 LifecycleManager manager) {
68 Preconditions.notNull("type", type);
69 Preconditions.notNull("desires", desires);
70 Preconditions.notNull("providers", providers);
71
72 this.type = type;
73 this.desires = desires;
74 this.providers = providers;
75 this.manager = manager;
76 }
77
78 @Override
79 public Class getType() {
80 return type;
81 }
82
83 @Override
84 public Object instantiate() throws ConstructionException {
85
86
87 Constructor<?> ctor = getConstructor();
88 LogContext globalLogContext = LogContext.create();
89 Object instance = null;
90 Method[] methods;
91
92 try {
93
94 try {
95 globalLogContext.put("org.grouplens.grapht.class", ctor.getClass().toString());
96 Object[] ctorArgs = new Object[ctor.getParameterTypes().length];
97 for (Desire d : desires) {
98 LogContext ipContext = LogContext.create();
99 if (d.getInjectionPoint() instanceof ConstructorParameterInjectionPoint) {
100
101 Instantiator provider = providers.get(d);
102 ConstructorParameterInjectionPoint cd = (ConstructorParameterInjectionPoint) d.getInjectionPoint();
103 logger.trace("Injection point satisfactions in progress {}", cd);
104 try {
105 ipContext.put("org.grouplens.grapht.injectionPoint", cd.toString());
106 } finally {
107 ipContext.finish();
108 }
109 ctorArgs[cd.getParameterIndex()] = checkNull(cd, provider.instantiate());
110 }
111 }
112 logger.trace("Invoking constructor {} with arguments {}", ctor, ctorArgs);
113 ctor.setAccessible(true);
114 instance = ctor.newInstance(ctorArgs);
115 } catch (InvocationTargetException e) {
116 throw new ConstructionException(ctor, "Constructor " + ctor + " failed", e);
117 } catch (InstantiationException e) {
118 throw new ConstructionException(ctor, "Could not instantiate " + type, e);
119 } catch (IllegalAccessException e) {
120 throw new ConstructionException(ctor, "Access violation on " + ctor, e);
121 }
122
123
124
125 Map<Method, InjectionArgs> settersAndArguments = new HashMap<Method, InjectionArgs>();
126 for (Desire d : desires) {
127 LogContext ipContext = LogContext.create();
128 try {
129 final InjectionStrategy injectionStrategy = InjectionStrategy.forInjectionPoint(d.getInjectionPoint());
130 ipContext.put("org.grouplens.grapht.injectionPoint", d.getInjectionPoint().toString());
131 injectionStrategy.inject(d.getInjectionPoint(), instance, providers.get(d), settersAndArguments);
132 } finally {
133 ipContext.finish();
134 }
135 }
136 } finally {
137 globalLogContext.finish();
138 }
139 if (manager != null) {
140 manager.registerComponent(instance);
141 }
142
143 methods = MethodUtils.getMethodsWithAnnotation(type, PostConstruct.class);
144 for(Method method:methods){
145 method.setAccessible(true);
146 try {
147 method.invoke(instance);
148 } catch (InvocationTargetException e) {
149 throw new ConstructionException("Exception throw by " + method, e);
150 } catch (IllegalAccessException e) {
151 throw new ConstructionException("Access violation invoking " + method, e);
152 }
153 }
154
155
156 return instance;
157 }
158
159 @SuppressWarnings("unchecked")
160 private Constructor<?> getConstructor() {
161 for (Desire d: desires) {
162 if (d.getInjectionPoint() instanceof ConstructorParameterInjectionPoint) {
163
164
165 Constructor<?> ctor = ((ConstructorParameterInjectionPoint) d.getInjectionPoint()).getMember();
166 logger.debug("Using constructor annotated with @Inject: {}", ctor);
167 return ctor;
168 }
169 }
170
171 try {
172 logger.debug("Using default constructor for {}", type);
173 return type.getDeclaredConstructor();
174 } catch (NoSuchMethodException e) {
175
176
177
178 throw new RuntimeException("Unexpected exception", e);
179 }
180 }
181
182 static Object checkNull(InjectionPoint injectPoint, Object value) throws NullDependencyException {
183 if (value == null && !injectPoint.isNullable()) {
184 throw new NullDependencyException(injectPoint);
185 } else {
186 return value;
187 }
188 }
189
190 static class InjectionArgs {
191 public final Object[] arguments;
192 private final boolean[] injected;
193
194 public InjectionArgs(int num) {
195 arguments = new Object[num];
196 injected = new boolean[num];
197 }
198
199 public void set(int i, Object o) {
200 arguments[i] =o;
201 injected[i] = true;
202 }
203
204 public boolean isCompleted() {
205 for (int i = 0; i < injected.length; i++) {
206 if (!injected[i]) {
207 return false;
208 }
209 }
210 return true;
211 }
212 }
213 }