1 | // Copyright 2004-2007 Jean-Francois Poilpret |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | // you may not use this file except in compliance with the License. |
5 | // You may obtain a copy of the License at |
6 | // |
7 | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | // |
9 | // Unless required by applicable law or agreed to in writing, software |
10 | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | // See the License for the specific language governing permissions and |
13 | // limitations under the License. |
14 | |
15 | package net.sourceforge.hiveutils.service.impl; |
16 | |
17 | import java.util.ArrayList; |
18 | import java.util.HashMap; |
19 | import java.util.Iterator; |
20 | import java.util.List; |
21 | import java.util.Map; |
22 | |
23 | import org.apache.commons.logging.Log; |
24 | import org.apache.hivemind.ApplicationRuntimeException; |
25 | import org.apache.hivemind.schema.Translator; |
26 | import org.apache.hivemind.service.EventLinker; |
27 | import org.apache.hivemind.util.ConstructorUtils; |
28 | import org.apache.hivemind.util.PropertyUtils; |
29 | |
30 | import net.sourceforge.hiveutils.service.ObjectBuilder; |
31 | import net.sourceforge.hiveutils.service.ObjectBuilderListener; |
32 | |
33 | /** |
34 | * Implementation of the <code>ObjectBuilder</code> service. |
35 | * |
36 | * @author Jean-Francois Poilpret |
37 | */ |
38 | public class ObjectBuilderImpl implements ObjectBuilder |
39 | { |
40 | public ObjectBuilderImpl(Log logger, |
41 | Map<String, ObjectContribution> config, |
42 | Translator objectTranslator, |
43 | EventLinker linker) |
44 | { |
45 | _logger = logger; |
46 | _linker = linker; |
47 | _objectTranslator = objectTranslator; |
48 | _config = config; |
49 | |
50 | _cache = new HashMap<CachedObjectKey, Object>(); |
51 | _listeners = new ArrayList<ObjectBuilderListener>(); |
52 | |
53 | /* |
54 | List objectsToLoad = new ArrayList(); |
55 | Iterator i = config.values().iterator(); |
56 | while (i.hasNext()) |
57 | { |
58 | ObjectContribution contrib = (ObjectContribution) i.next(); |
59 | if (contrib.isEagerLoad()) |
60 | { |
61 | contrib.setCached(true); |
62 | objectsToLoad.add(contrib.getName()); |
63 | } |
64 | } |
65 | // Create eager-loaded objects (#### bug here? why? temporarily comment out) |
66 | i = objectsToLoad.iterator(); |
67 | while (i.hasNext()) |
68 | { |
69 | //#### Should we catch all exceptions here? |
70 | create((String) i.next()); |
71 | } |
72 | */ |
73 | } |
74 | |
75 | synchronized public void addObjectBuilderListener(ObjectBuilderListener listener) |
76 | { |
77 | _listeners.add(listener); |
78 | } |
79 | |
80 | synchronized public void removeObjectBuilderListener(ObjectBuilderListener listener) |
81 | { |
82 | _listeners.remove(listener); |
83 | } |
84 | |
85 | synchronized private void fireObjectCreated(String name, Object object) |
86 | { |
87 | for (ObjectBuilderListener listener: _listeners) |
88 | { |
89 | listener.objectCreated(name, object); |
90 | } |
91 | } |
92 | |
93 | @SuppressWarnings("unchecked") |
94 | public<T> T get(String name, Object... args) |
95 | { |
96 | return (T) _cache.get(new CachedObjectKey(name, args)); |
97 | } |
98 | |
99 | @SuppressWarnings("unchecked") |
100 | public<T> T create(String name, Object... userArgs) |
101 | { |
102 | // First find if the object is in the cache |
103 | T object = (T) _cache.get(new CachedObjectKey(name, userArgs)); |
104 | if (object != null) |
105 | { |
106 | return object; |
107 | } |
108 | |
109 | // Now find if there is a configuration for it |
110 | ObjectContribution contrib = _config.get(name); |
111 | if (contrib == null) |
112 | { |
113 | _logger.info("create() has no contribution for object <" + name + ">"); |
114 | return null; |
115 | } |
116 | |
117 | // Build the object with constructor injection first |
118 | object = (T) injectConstructor(contrib, userArgs); |
119 | |
120 | // Then add setter injection |
121 | injectSetters(object, contrib); |
122 | |
123 | // Finally add listeners |
124 | injectListeners(object, contrib); |
125 | |
126 | if (contrib.isCached()) |
127 | { |
128 | _cache.put(new CachedObjectKey(name, userArgs), object); |
129 | } |
130 | |
131 | _logger.debug("create() just created object: " + name); |
132 | fireObjectCreated(name, object); |
133 | return object; |
134 | } |
135 | |
136 | private Object injectConstructor(ObjectContribution contrib, Object[] userArgs) |
137 | { |
138 | // Build the object with constructor injection first |
139 | String name = contrib.getName(); |
140 | List ctorArgs = contrib.getConstructorInjections(); |
141 | Object[] args = new Object[ctorArgs.size()]; |
142 | Iterator i = ctorArgs.iterator(); |
143 | int j = 0; |
144 | int k = 0; |
145 | while (i.hasNext()) |
146 | { |
147 | InjectObjectInformation inject = (InjectObjectInformation) i.next(); |
148 | if (inject != null) |
149 | { |
150 | args[j++] = getObject(inject); |
151 | } |
152 | else if (k < userArgs.length) |
153 | { |
154 | args[j++] = userArgs[k++]; |
155 | } |
156 | else |
157 | { |
158 | _logger.error("create() has not enough user arguments for object <" + |
159 | name + ">"); |
160 | throw new ApplicationRuntimeException( |
161 | "create() has not enough user arguments for object <" + name + ">"); |
162 | } |
163 | } |
164 | if (k != userArgs.length) |
165 | { |
166 | _logger.info("Too many user arguments passed to create() for object <" + |
167 | name + ">"); |
168 | } |
169 | return ConstructorUtils.invokeConstructor(contrib.getObjectClass(), args); |
170 | } |
171 | |
172 | private void injectSetters(Object object, ObjectContribution contrib) |
173 | { |
174 | // Then add setter injection |
175 | Map<String, InjectObjectInformation> setters = contrib.getSetterInjections(); |
176 | for (Map.Entry<String, InjectObjectInformation> entry: setters.entrySet()) |
177 | { |
178 | PropertyUtils.write(object, entry.getKey(), getObject(entry.getValue())); |
179 | } |
180 | } |
181 | |
182 | public void injectListeners(Object object, ObjectContribution contrib) |
183 | { |
184 | for (ListenerContribution listener: contrib.getListeners()) |
185 | { |
186 | addEventListener(object, listener.getObject(), listener.getEvent()); |
187 | } |
188 | } |
189 | |
190 | private void addEventListener(Object producer, |
191 | InjectObjectInformation listener, |
192 | String event) |
193 | { |
194 | //#### Bad location, no? |
195 | _linker.addEventListener(producer, |
196 | event, |
197 | getObject(listener), |
198 | listener.getLocation()); |
199 | } |
200 | |
201 | private Object getObject(InjectObjectInformation inject) |
202 | { |
203 | return _objectTranslator.translate( inject.getModule(), |
204 | inject.getType(), |
205 | inject.getObject(), |
206 | inject.getLocation()); |
207 | } |
208 | |
209 | // Class to hold the key of a cached object (name + args) |
210 | static private class CachedObjectKey |
211 | { |
212 | public CachedObjectKey(String name, Object[] args) |
213 | { |
214 | // CSOFF: MagicNumberCheck |
215 | _name = name; |
216 | _args = args; |
217 | int hash = _name.hashCode() * 4321; |
218 | for (int i = 0; i < _args.length; i++) |
219 | { |
220 | if (_args[i] != null) |
221 | { |
222 | hash += _args[i].hashCode() * (567 + i); |
223 | } |
224 | } |
225 | _hash = hash; |
226 | // CSON: MagicNumberCheck |
227 | } |
228 | |
229 | public int hashCode() |
230 | { |
231 | return _hash; |
232 | } |
233 | |
234 | public boolean equals(Object o) |
235 | { |
236 | if (!(o instanceof CachedObjectKey)) |
237 | { |
238 | return false; |
239 | } |
240 | CachedObjectKey that = (CachedObjectKey) o; |
241 | if (!that._name.equals(this._name)) |
242 | { |
243 | return false; |
244 | } |
245 | if (that._args.length != this._args.length) |
246 | { |
247 | return false; |
248 | } |
249 | for (int i = 0; i < _args.length; i++) |
250 | { |
251 | if (that._args[i] == this._args[i]) |
252 | { |
253 | continue; |
254 | } |
255 | if ( that._args[i] != null |
256 | && !that._args[i].equals(this._args[i])) |
257 | { |
258 | return false; |
259 | } |
260 | } |
261 | return true; |
262 | } |
263 | |
264 | private final String _name; |
265 | private final Object[] _args; |
266 | private final int _hash; |
267 | } |
268 | |
269 | private final Log _logger; |
270 | private final EventLinker _linker; |
271 | private final Map<String, ObjectContribution> _config; |
272 | private final Map<CachedObjectKey, Object> _cache; |
273 | private final Translator _objectTranslator; |
274 | private final List<ObjectBuilderListener> _listeners; |
275 | } |