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.ant;
016    
017    import java.io.BufferedOutputStream;
018    import java.io.File;
019    import java.io.FileOutputStream;
020    import java.io.IOException;
021    import java.io.OutputStream;
022    import java.net.URL;
023    import java.util.ArrayList;
024    import java.util.List;
025    
026    import org.apache.hivemind.ModuleDescriptorProvider;
027    import org.apache.hivemind.Resource;
028    import org.apache.hivemind.impl.DefaultClassResolver;
029    import org.apache.hivemind.impl.XmlModuleDescriptorProvider;
030    import org.apache.hivemind.util.FileResource;
031    import org.apache.hivemind.util.URLResource;
032    import org.apache.tools.ant.BuildException;
033    import org.apache.tools.ant.Task;
034    import org.apache.tools.ant.types.Path;
035    import org.apache.xml.serialize.OutputFormat;
036    import org.apache.xml.serialize.XMLSerializer;
037    import 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     */
056    public 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    }