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.parse;
016
017import java.io.IOException;
018import java.net.URL;
019
020import javax.xml.parsers.FactoryConfigurationError;
021import javax.xml.parsers.ParserConfigurationException;
022import javax.xml.parsers.SAXParser;
023import javax.xml.parsers.SAXParserFactory;
024
025import org.apache.commons.logging.Log;
026import org.apache.commons.logging.LogFactory;
027import org.apache.hivemind.ApplicationRuntimeException;
028import org.apache.hivemind.ClassResolver;
029import org.apache.hivemind.ErrorHandler;
030import org.apache.hivemind.Resource;
031import org.xml.sax.InputSource;
032import org.xml.sax.SAXException;
033
034/**
035 * The XmlResourceProcessor processes XML {@link Resource resources} using the
036 * {@link DescriptorParser} which is used as a SAX ContentHandler. The result of
037 * {@link #processResource(Resource) processing a resource} is a {@link ModuleDescriptor}.
038 * 
039 * @see org.apache.hivemind.parse.DescriptorParser
040 * @see org.apache.hivemind.ModuleDescriptorProvider
041 * @since 1.1
042 * @author Knut Wannheden
043 */
044public class XmlResourceProcessor
045{
046    private static final Log LOG = LogFactory.getLog(XmlResourceProcessor.class);
047
048    protected ClassResolver _resolver;
049
050    protected ErrorHandler _errorHandler;
051
052    private DescriptorParser _contentHandler;
053
054    private SAXParser _saxParser;
055
056    public XmlResourceProcessor(ClassResolver resolver, ErrorHandler errorHandler)
057    {
058        _resolver = resolver;
059        _errorHandler = errorHandler;
060    }
061
062    /**
063     * Initializes the {@link DescriptorParser parser},
064     * {@link #processResource(Resource) processes} the Resource, resets the parser, and finally
065     * returns the parsed {@link ModuleDescriptor}.
066     * 
067     * @throws ApplicationRuntimeException
068     *             Thrown if errors are encountered while parsing the resource.
069     */
070    public ModuleDescriptor processResource(Resource resource)
071    {
072        if (_contentHandler == null)
073            _contentHandler = new DescriptorParser(_errorHandler);
074
075        _contentHandler.initialize(resource, _resolver);
076
077        try
078        {
079            if (LOG.isDebugEnabled())
080                LOG.debug("Parsing " + resource);
081
082            ModuleDescriptor descriptor = parseResource(resource, getSAXParser(), _contentHandler);
083
084            if (LOG.isDebugEnabled())
085                LOG.debug("Result: " + descriptor);
086
087            return descriptor;
088        }
089        catch (ApplicationRuntimeException e)
090        {
091            throw e;
092        }
093        catch (Exception e)
094        {
095            _saxParser = null;
096
097            throw new ApplicationRuntimeException(
098                    ParseMessages.errorReadingDescriptor(resource, e), resource, _contentHandler
099                            .getLocation(), e);
100        }
101        finally
102        {
103            _contentHandler.resetParser();
104        }
105    }
106
107    /**
108     * Returns the ModuleDescriptor obtained by parsing the specified Resource using the given
109     * {@link SAXParser} and {@link DescriptorParser}. Called by {@link #processResource(Resource)}
110     * after the DescriptorParser has been
111     * {@link DescriptorParser#initialize(Resource, ClassResolver) initialized}. Suitable for
112     * overriding by subclasses.
113     */
114    protected ModuleDescriptor parseResource(Resource resource, SAXParser parser,
115            DescriptorParser contentHandler) throws SAXException, IOException
116    {
117        InputSource source = getInputSource(resource);
118
119        parser.parse(source, contentHandler);
120
121        return contentHandler.getModuleDescriptor();
122    }
123
124    private InputSource getInputSource(Resource resource)
125    {
126        try
127        {
128            URL url = resource.getResourceURL();
129
130            return new InputSource(url.openStream());
131        }
132        catch (Exception e)
133        {
134            throw new ApplicationRuntimeException(ParseMessages.missingResource(resource),
135                    resource, null, e);
136        }
137    }
138
139    private SAXParser getSAXParser() throws ParserConfigurationException, SAXException,
140            FactoryConfigurationError
141    {
142        if (_saxParser == null)
143            _saxParser = SAXParserFactory.newInstance().newSAXParser();
144
145        return _saxParser;
146    }
147
148}