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
015package org.apache.hivemind.impl;
016
017import java.util.ArrayList;
018import java.util.Collections;
019import java.util.List;
020import java.util.Map;
021
022import org.apache.commons.logging.Log;
023import org.apache.commons.logging.LogFactory;
024import org.apache.hivemind.*;
025import org.apache.hivemind.ApplicationRuntimeException;
026import org.apache.hivemind.Occurances;
027import org.apache.hivemind.internal.ConfigurationPoint;
028import org.apache.hivemind.internal.Contribution;
029import org.apache.hivemind.schema.Schema;
030import 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 */
038public 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}