001 // Copyright 2004, 2005 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.hivemind.impl;
016
017 import java.util.ArrayList;
018 import java.util.Collections;
019 import java.util.List;
020 import java.util.Map;
021
022 import org.apache.commons.logging.Log;
023 import org.apache.commons.logging.LogFactory;
024 import org.apache.hivemind.*;
025 import org.apache.hivemind.ApplicationRuntimeException;
026 import org.apache.hivemind.Occurances;
027 import org.apache.hivemind.internal.ConfigurationPoint;
028 import org.apache.hivemind.internal.Contribution;
029 import org.apache.hivemind.schema.Schema;
030 import org.apache.hivemind.util.ToStringBuilder;
031
032 /**
033 * Implementation of the {@link org.apache.hivemind.internal.ConfigurationPoint} interface; a
034 * container for {@link org.apache.hivemind.internal.Contribution}s.
035 *
036 * @author Howard Lewis Ship
037 */
038 public final class ConfigurationPointImpl extends AbstractExtensionPoint implements
039 ConfigurationPoint
040 {
041 private static final Log LOG = LogFactory.getLog(ConfigurationPointImpl.class);
042
043 /**
044 * The cached elements for the extension point (if caching is enabled).
045 */
046 private List _elements;
047
048 private List _elementsProxy;
049
050 private Map _mappedElements;
051
052 private Map _mappedElementsProxy;
053
054 private boolean _canElementsBeMapped = false;
055
056 private Occurances _expectedCount;
057
058 private List _contributions;
059
060 private boolean _building;
061
062 private Schema _contributionsSchema;
063
064 private ShutdownCoordinator _shutdownCoordinator;
065
066 protected void extendDescription(ToStringBuilder builder)
067 {
068 builder.append("expectedCount", _expectedCount);
069 builder.append("contributions", _contributions);
070 builder.append("schema", _contributionsSchema);
071 }
072
073 /**
074 * Returns the number of contributions; it is expected that each top-level
075 * {@link org.apache.hivemind.Element} in each {@link Contribution} will convert to one element
076 * instance; the value returned is the total number of top-level elements in all contributed
077 * Extensions.
078 */
079 public int getContributionCount()
080 {
081 if (_contributions == null)
082 return 0;
083
084 int total = 0;
085
086 int count = _contributions.size();
087 for (int i = 0; i < count; i++)
088 {
089 Contribution c = (Contribution) _contributions.get(i);
090 total += c.getElements().size();
091 }
092
093 return total;
094 }
095
096 public void addContribution(Contribution c)
097 {
098 if (_contributions == null)
099 _contributions = new ArrayList();
100
101 _contributions.add(c);
102 }
103
104 public Occurances getExpectedCount()
105 {
106 return _expectedCount;
107 }
108
109 public void setExpectedCount(Occurances occurances)
110 {
111 _expectedCount = occurances;
112 }
113
114 /**
115 * Returns the contributed elements as an unmodifiable {@link List}. Internally, a proxy to the
116 * real list is returned, such that the real list may not be constructed until actually needed.
117 */
118 public synchronized List getElements()
119 {
120 if (_elements != null)
121 return _elements;
122
123 if (_elementsProxy == null)
124 {
125 ElementsProxyList outerProxy = new ElementsProxyList();
126
127 new ElementsInnerProxyList(this, outerProxy);
128
129 _shutdownCoordinator.addRegistryShutdownListener(outerProxy);
130
131 _elementsProxy = outerProxy;
132 }
133
134 return _elementsProxy;
135 }
136
137 public boolean areElementsMappable()
138 {
139 return _canElementsBeMapped;
140 }
141
142 /**
143 * Returns the contributed elements as an unmodifiable {@link Map}. Internally, a proxy to the
144 * real map is returned, such that the real map may not be constructed until actually needed.
145 */
146 public synchronized Map getElementsAsMap()
147 {
148 if (!areElementsMappable())
149 throw new ApplicationRuntimeException(ImplMessages.unableToMapConfiguration(this));
150
151 if (_mappedElements != null)
152 return _mappedElements;
153
154 if (_mappedElementsProxy == null)
155 {
156 ElementsProxyMap outerProxy = new ElementsProxyMap();
157
158 new ElementsInnerProxyMap(this, outerProxy);
159
160 _shutdownCoordinator.addRegistryShutdownListener(outerProxy);
161
162 _mappedElementsProxy = outerProxy;
163 }
164
165 return _mappedElementsProxy;
166 }
167
168 /**
169 * Invoked by {@link ElementsInnerProxyList} when the actual list is needed. Returns the List
170 * (which is modifiable, but that's OK because ElementsInnerProxyList is unmodifiable) created
171 * by calling {@link #processContributionElements()}.
172 */
173 synchronized List constructElements()
174 {
175 // It's nice to have this protection, but (unlike services), you
176 // would really have to go out of your way to provoke
177 // a recursive configuration.
178
179 if (_building)
180 throw new ApplicationRuntimeException(ImplMessages
181 .recursiveConfiguration(getExtensionPointId()));
182
183 try
184 {
185 if (_elements == null)
186 {
187 _building = true;
188
189 processContributionElements();
190 }
191
192 // Now that we have the real list, we don't need the proxy anymore, either.
193
194 _elementsProxy = null;
195
196 return _elements;
197 }
198 finally
199 {
200 _building = false;
201 }
202 }
203
204 /**
205 * Analoguously to {@link #constructElements()} this method will be called by
206 * {@link ElementsInnerProxyMap} to construct the actual map.
207 */
208 synchronized Map constructMapElements()
209 {
210 // It's nice to have this protection, but (unlike services), you
211 // would really have to go out of your way to provoke
212 // a recursive configuration.
213
214 if (_building)
215 throw new ApplicationRuntimeException(ImplMessages
216 .recursiveConfiguration(getExtensionPointId()));
217
218 try
219 {
220 if (_mappedElements == null)
221 {
222 _building = true;
223
224 processContributionElements();
225 }
226
227 // Now that we have the real map, we don't need the proxy anymore, either.
228
229 _mappedElementsProxy = null;
230
231 return _mappedElements;
232 }
233 finally
234 {
235 _building = false;
236 }
237 }
238
239 /**
240 * Processes the contribution elements using the
241 * {@link org.apache.hivemind.schema.SchemaProcessor}. The processed contributions will be
242 * stored as an immutable list (in {@link #_elements}) and as an immutable map (in
243 * {@link #_mappedElements}) if applicable (see {@link #areElementsMappable()}).
244 */
245 private void processContributionElements()
246 {
247 if (LOG.isDebugEnabled())
248 LOG.debug("Constructing extension point " + getExtensionPointId());
249
250 if (_contributions == null)
251 {
252 _elements = Collections.EMPTY_LIST;
253 _mappedElements = Collections.EMPTY_MAP;
254
255 return;
256 }
257
258 SchemaProcessorImpl processor = new SchemaProcessorImpl(getErrorLog(), _contributionsSchema);
259
260 int count = _contributions.size();
261
262 try
263 {
264 for (int i = 0; i < count; i++)
265 {
266 Contribution extension = (Contribution) _contributions.get(i);
267
268 processor.process(extension.getElements(), extension.getContributingModule());
269 }
270 }
271 catch (Exception ex)
272 {
273 throw new ApplicationRuntimeException(ImplMessages.unableToConstructConfiguration(
274 getExtensionPointId(),
275 ex), ex);
276 }
277
278 if (areElementsMappable())
279 _mappedElements = Collections.unmodifiableMap(processor.getMappedElements());
280
281 _elements = Collections.unmodifiableList(processor.getElements());
282
283 // After constructing the result, if the result
284 // will be cached, then there's no need to keep
285 // the schema and extensions (used to build the
286 // result); it can all be released to the GC.
287
288 _contributionsSchema = null;
289 _contributions = null;
290 }
291
292 public Schema getSchema()
293 {
294 return _contributionsSchema;
295 }
296
297 public void setContributionsSchema(Schema schema)
298 {
299 _contributionsSchema = schema;
300
301 _canElementsBeMapped = _contributionsSchema != null
302 && _contributionsSchema.canInstancesBeKeyed();
303 }
304
305 public Schema getContributionsSchema()
306 {
307 return _contributionsSchema;
308 }
309
310 public void setShutdownCoordinator(ShutdownCoordinator coordinator)
311 {
312 _shutdownCoordinator = coordinator;
313 }
314
315 }