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.ant;
016
017import java.io.BufferedOutputStream;
018import java.io.File;
019import java.io.FileOutputStream;
020import java.io.IOException;
021import java.io.OutputStream;
022import java.net.URL;
023import java.util.ArrayList;
024import java.util.List;
025
026import org.apache.hivemind.ModuleDescriptorProvider;
027import org.apache.hivemind.Resource;
028import org.apache.hivemind.impl.DefaultClassResolver;
029import org.apache.hivemind.impl.XmlModuleDescriptorProvider;
030import org.apache.hivemind.util.FileResource;
031import org.apache.hivemind.util.URLResource;
032import org.apache.tools.ant.BuildException;
033import org.apache.tools.ant.Task;
034import org.apache.tools.ant.types.Path;
035import org.apache.xml.serialize.OutputFormat;
036import org.apache.xml.serialize.XMLSerializer;
037import org.w3c.dom.Document;
038
039/**
040 * Reads some number of hivemodule deployment descriptors (specified as a fileset) and builds a
041 * composite registry by simply concatinating them all. The resulting file is suitable for passing
042 * through an XSLT processor to create documentation.
043 * <p>
044 * The resulting XML file does not conform to the hivemind module deployment descriptor schema. The
045 * following changes occur:
046 * <ul>
047 * <li>The outermost element is &lt;registry&gt; (which contains a list of &lt;module&gt;)
048 * <li>A unique id (unique within the file) is assigned to each &lt;module&gt;,
049 * &lt;configuration-point&gt;, &lt;service-point&gt;, &lt;contribution&gt;, &tl;schema&gt; and
050 * &lt;implementation&gt; (this is to make it easier to generate links and anchors)
051 * <li>Unqualified ids are converted to qualified ids (whereever possible).
052 * </ul>
053 * 
054 * @author Howard Lewis Ship
055 */
056public class ConstructRegistry extends Task
057{
058    private File _output;
059
060    private Path _descriptorsPath;
061
062    /**
063     * List of {@link org.apache.hivemind.Resource} of additional descriptors to parse.
064     */
065    private List _resourceQueue = new ArrayList();
066
067    public void execute() throws BuildException
068    {
069        if (_output == null)
070            throw new BuildException("You must specify an output file");
071
072        if (_descriptorsPath == null)
073            throw new BuildException("You must specify a set of module descriptors");
074
075        long outputStamp = _output.lastModified();
076
077        String[] paths = _descriptorsPath.list();
078        int count = paths.length;
079
080        boolean needsUpdate = false;
081
082        File[] descriptors = new File[count];
083
084        for (int i = 0; i < count; i++)
085        {
086            File f = new File(paths[i]);
087
088            if (f.isDirectory())
089                continue;
090
091            if (f.lastModified() > outputStamp)
092                needsUpdate = true;
093
094            descriptors[i] = f;
095        }
096
097        if (needsUpdate)
098        {
099            Document registry = constructRegistry(descriptors);
100
101            log("Writing registry to " + _output);
102
103            writeDocument(registry, _output);
104        }
105
106    }
107
108    private Document constructRegistry(File[] moduleDescriptors) throws BuildException
109    {
110        try
111        {
112            enqueue(moduleDescriptors);
113
114            ModuleDescriptorProvider provider = new XmlModuleDescriptorProvider(
115                    new DefaultClassResolver(), _resourceQueue);
116
117            RegistrySerializer generator = new RegistrySerializer();
118
119            generator.addModuleDescriptorProvider(provider);
120
121            Document result = generator.createRegistryDocument();
122
123            return result;
124        }
125        catch (Exception ex)
126        {
127            throw new BuildException(ex);
128        }
129    }
130
131    private void enqueue(File[] descriptors) throws IOException
132    {
133        for (int i = 0; i < descriptors.length; i++)
134            enqueue(descriptors[i]);
135    }
136
137    /**
138     * Queues up a single descriptor which may be a raw XML file, or a JAR (containing the XML
139     * file).
140     */
141    private void enqueue(File file) throws IOException
142    {
143        // This occurs when a bare directory is part of the classpath.
144
145        if (file == null)
146            return;
147
148        if (file.getName().endsWith(".jar"))
149        {
150            enqueueJar(file);
151            return;
152        }
153
154        String path = file.getPath().replace('\\', '/');
155
156        Resource r = new FileResource(path);
157
158        enqueue(r);
159    }
160
161    private void enqueue(Resource resource)
162    {
163        if (!_resourceQueue.contains(resource))
164            _resourceQueue.add(resource);
165    }
166
167    private void enqueueJar(File jarFile) throws IOException
168    {
169        URL jarRootURL = new URL("jar:" + jarFile.toURL() + "!/");
170
171        Resource jarResource = new URLResource(jarRootURL);
172
173        enqueueIfExists(jarResource, XmlModuleDescriptorProvider.HIVE_MODULE_XML);
174    }
175
176    private void enqueueIfExists(Resource jarResource, String path)
177    {
178        Resource r = jarResource.getRelativeResource(path);
179
180        if (r.getResourceURL() != null)
181            enqueue(r);
182    }
183
184    private void writeDocument(Document document, File file) throws BuildException
185    {
186        try
187        {
188            OutputStream out = new FileOutputStream(file);
189            BufferedOutputStream buffered = new BufferedOutputStream(out);
190
191            writeDocument(document, buffered);
192
193            buffered.close();
194        }
195        catch (IOException ex)
196        {
197            throw new BuildException(
198                    "Unable to write registry to " + file + ": " + ex.getMessage(), ex);
199        }
200    }
201
202    private void writeDocument(Document document, OutputStream out) throws IOException
203    {
204        XMLSerializer serializer = new XMLSerializer(out, new OutputFormat(document, null, true));
205        serializer.serialize(document);
206    }
207
208    public Path createDescriptors()
209    {
210        _descriptorsPath = new Path(getProject());
211        return _descriptorsPath;
212    }
213
214    public File getOutput()
215    {
216        return _output;
217    }
218
219    public void setOutput(File file)
220    {
221        _output = file;
222    }
223
224}