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.io.IOException;
018import java.net.URL;
019import java.util.ArrayList;
020import java.util.Enumeration;
021import java.util.Iterator;
022import java.util.List;
023
024import org.apache.commons.logging.Log;
025import org.apache.commons.logging.LogFactory;
026import org.apache.hivemind.ApplicationRuntimeException;
027import org.apache.hivemind.ClassResolver;
028import org.apache.hivemind.ErrorHandler;
029import org.apache.hivemind.HiveMind;
030import org.apache.hivemind.ModuleDescriptorProvider;
031import org.apache.hivemind.Resource;
032import org.apache.hivemind.parse.ModuleDescriptor;
033import org.apache.hivemind.parse.SubModuleDescriptor;
034import org.apache.hivemind.parse.XmlResourceProcessor;
035import org.apache.hivemind.util.URLResource;
036
037/**
038 * Implementation of the {@link ModuleDescriptorProvider} interface which uses the
039 * {@link org.apache.hivemind.parse.DescriptorParser} to provide module descriptors defined in XML.
040 * The module descriptors are loaded from files or resources on the classpath.
041 * 
042 * @author Knut Wannheden
043 * @since 1.1
044 */
045public class XmlModuleDescriptorProvider implements ModuleDescriptorProvider
046{
047    private static final Log LOG = LogFactory.getLog(XmlModuleDescriptorProvider.class);
048
049    /**
050     * The default path, within a JAR or the classpath, to the XML HiveMind module deployment
051     * descriptor: <code>META-INF/hivemodule.xml</code>. Use this constant with the
052     * {@link #XmlModuleDescriptorProvider(ClassResolver, String)} constructor.
053     */
054    public static final String HIVE_MODULE_XML = "META-INF/hivemodule.xml";
055
056    /**
057     * Set of all specified resources processed by this ModuleDescriptorProvider. Descriptors of
058     * sub-modules are not included.
059     */
060    private List _resources = new ArrayList();
061
062    /**
063     * List of parsed {@link ModuleDescriptor} instances. Also includes referenced sub-modules.
064     */
065    private List _moduleDescriptors = new ArrayList();
066
067    private ClassResolver _resolver;
068
069    private ErrorHandler _errorHandler;
070
071    /**
072     * Parser instance used by all parsing of module descriptors.
073     */
074    private XmlResourceProcessor _processor;
075
076    /**
077     * Convenience constructor. Equivalent to using
078     * {@link #XmlModuleDescriptorProvider(ClassResolver, String)}with {@link #HIVE_MODULE_XML} as
079     * the second argument.
080     */
081    public XmlModuleDescriptorProvider(ClassResolver resolver)
082    {
083        this(resolver, HIVE_MODULE_XML);
084    }
085
086    /**
087     * Loads all XML module descriptors found on the classpath (using the given
088     * {@link org.apache.hivemind.ClassResolver}. Only module descriptors matching the specified
089     * path are loaded. Use the {@link XmlModuleDescriptorProvider#HIVE_MODULE_XML} constant to load
090     * all descriptors in the default location.
091     */
092    public XmlModuleDescriptorProvider(ClassResolver resolver, String resourcePath)
093    {
094        _resolver = resolver;
095        _resources.addAll(getDescriptorResources(resourcePath, _resolver));
096    }
097
098    /**
099     * Constructs an XmlModuleDescriptorProvider only loading the ModuleDescriptor identified by the
100     * given {@link org.apache.hivemind.Resource}.
101     */
102    public XmlModuleDescriptorProvider(ClassResolver resolver, Resource resource)
103    {
104        _resolver = resolver;
105        _resources.add(resource);
106    }
107
108    /**
109     * Constructs an XmlModuleDescriptorProvider loading all ModuleDescriptor identified by the
110     * given List of {@link org.apache.hivemind.Resource} objects.
111     */
112    public XmlModuleDescriptorProvider(ClassResolver resolver, List resources)
113    {
114        _resolver = resolver;
115        _resources.addAll(resources);
116    }
117
118    private List getDescriptorResources(String resourcePath, ClassResolver resolver)
119    {
120        if (LOG.isDebugEnabled())
121            LOG.debug("Processing modules visible to " + resolver);
122
123        List descriptors = new ArrayList();
124
125        ClassLoader loader = resolver.getClassLoader();
126        Enumeration e = null;
127
128        try
129        {
130            e = loader.getResources(resourcePath);
131        }
132        catch (IOException ex)
133        {
134            throw new ApplicationRuntimeException(ImplMessages.unableToFindModules(resolver, ex),
135                    ex);
136        }
137
138        while (e.hasMoreElements())
139        {
140            URL descriptorURL = (URL) e.nextElement();
141
142            descriptors.add(new URLResource(descriptorURL));
143        }
144
145        return descriptors;
146    }
147
148    public List getModuleDescriptors(ErrorHandler handler)
149    {
150        _errorHandler = handler;
151
152        _processor = getResourceProcessor(_resolver, handler);
153
154        for (Iterator i = _resources.iterator(); i.hasNext();)
155        {
156            Resource resource = (Resource) i.next();
157
158            processResource(resource);
159        }
160
161        _processor = null;
162
163        _errorHandler = null;
164
165        return _moduleDescriptors;
166    }
167
168    private void processResource(Resource resource)
169    {
170        try
171        {
172            ModuleDescriptor md = _processor.processResource(resource);
173
174            _moduleDescriptors.add(md);
175
176            // After parsing a module, parse any additional modules identified
177            // within the module (using the <sub-module> element) recursively.
178            processSubModules(md);
179        }
180        catch (RuntimeException ex)
181        {
182            _errorHandler.error(LOG, ex.getMessage(), HiveMind.getLocation(ex), ex);
183        }
184    }
185
186    private void processSubModules(ModuleDescriptor moduleDescriptor)
187    {
188        List subModules = moduleDescriptor.getSubModules();
189
190        if (subModules == null)
191            return;
192
193        for (Iterator i = subModules.iterator(); i.hasNext();)
194        {
195            SubModuleDescriptor smd = (SubModuleDescriptor) i.next();
196
197            Resource descriptorResource = smd.getDescriptor();
198
199            if (descriptorResource.getResourceURL() == null)
200            {
201                _errorHandler.error(
202                        LOG,
203                        ImplMessages.subModuleDoesNotExist(descriptorResource),
204                        smd.getLocation(),
205                        null);
206                continue;
207            }
208
209            processResource(smd.getDescriptor());
210        }
211    }
212
213    protected XmlResourceProcessor getResourceProcessor(ClassResolver resolver, ErrorHandler handler)
214    {
215        return new XmlResourceProcessor(resolver, handler);
216    }
217}