Clover Coverage Report - XWiki Commons - Parent POM 4.0-SNAPSHOT (Aggregated)
Coverage timestamp: Mon Mar 12 2012 17:13:48 CET
../../../../../img/srcFileCovDistChart9.png 20% of files have more coverage
133   403   34   10.23
32   235   0.26   13
13     2.62  
1    
 
  XarMojo       Line # 54 133 0% 34 31 82.6% 0.8258427
 
No Tests
 
1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package com.xpn.xwiki.tool.xar;
21   
22    import java.io.File;
23    import java.io.FileOutputStream;
24    import java.io.FilenameFilter;
25    import java.io.OutputStream;
26    import java.util.Collection;
27    import java.util.LinkedList;
28    import java.util.Queue;
29    import java.util.Set;
30   
31    import org.apache.maven.artifact.Artifact;
32    import org.apache.maven.plugin.MojoExecutionException;
33    import org.codehaus.plexus.archiver.ArchiveEntry;
34    import org.codehaus.plexus.archiver.zip.ZipArchiver;
35    import org.dom4j.Document;
36    import org.dom4j.Element;
37    import org.dom4j.dom.DOMDocument;
38    import org.dom4j.dom.DOMElement;
39    import org.dom4j.io.OutputFormat;
40    import org.dom4j.io.SAXReader;
41    import org.dom4j.io.XMLWriter;
42   
43    /**
44    * Gather all resources in a XAR file (which is actually a ZIP file). Also generates a XAR descriptor if none is
45    * provided.
46    *
47    * @version $Id: 4548215612f0342e18525b6f7f307c7e0ed8acc8 $
48    * @goal xar
49    * @phase package
50    * @requiresProject
51    * @requiresDependencyResolution compile
52    * @threadSafe
53    */
 
54    public class XarMojo extends AbstractXarMojo
55    {
56    /**
57    * Indicate if xar dependencies should be included in the produced xar package.
58    *
59    * @parameter expression="${includeDependencies}" default-value="false"
60    */
61    private boolean includeDependencies;
62   
 
63  3 toggle @Override
64    public void execute() throws MojoExecutionException
65    {
66  3 if (this.project.getResources().size() < 1) {
67  0 getLog().warn("No XAR created as no resources were found");
68  0 return;
69    }
70   
71  3 try {
72  3 performArchive();
73    } catch (Exception e) {
74  1 throw new MojoExecutionException("Error while creating XAR file", e);
75    }
76    }
77   
78    /**
79    * Create the XAR by zipping the resource files.
80    *
81    * @throws Exception if the zipping failed for some reason
82    */
 
83  3 toggle private void performArchive() throws Exception
84    {
85  3 File xarFile = new File(this.project.getBuild().getDirectory(), this.project.getArtifactId() + ".xar");
86   
87  3 String resourcesLocation =
88    (this.project.getBasedir().getAbsolutePath() + "/src/main/resources").replace("/", File.separator);
89  3 File resourcesDir = new File(resourcesLocation);
90   
91    // The source dir points to the target/classes directory where the Maven resources plugin
92    // has copied the XAR files during the process-resources phase.
93    // For package.xml, however, we look in src/main/resources.
94  3 File sourceDir = new File(this.project.getBuild().getOutputDirectory());
95   
96  3 ZipArchiver archiver = new ZipArchiver();
97  3 archiver.setEncoding(this.encoding);
98  3 archiver.setDestFile(xarFile);
99  3 archiver.setIncludeEmptyDirs(false);
100  3 archiver.setCompress(true);
101   
102  3 if (this.includeDependencies) {
103    // Unzip dependent XARs on top of this project's XML documents but without overwriting
104    // existing files since we want this projet's files to be used if they override a file
105    // present in a XAR dependency.
106  0 unpackDependentXars();
107    }
108   
109    // If no package.xml can be found at the top level of the current project, generate one
110    // otherwise, try to use the existing one
111  3 FilenameFilter packageXmlFiler = new FilenameFilter()
112    {
 
113  5 toggle @Override
114    public boolean accept(File dir, String name)
115    {
116  5 return (name.equals(PACKAGE_XML));
117    }
118    };
119  3 if (!resourcesDir.exists() || resourcesDir.list(packageXmlFiler).length == 0) {
120  1 addFilesToArchive(archiver, sourceDir);
121    } else {
122  2 File packageXml = new File(resourcesDir, PACKAGE_XML);
123  2 addFilesToArchive(archiver, sourceDir, packageXml);
124    }
125   
126  2 archiver.createArchive();
127   
128  2 this.project.getArtifact().setFile(xarFile);
129    }
130   
131    /**
132    * Unpack xar dependencies before pack then into it.
133    *
134    * @throws MojoExecutionException error when unpack dependencies.
135    */
 
136  0 toggle private void unpackDependentXars() throws MojoExecutionException
137    {
138  0 Set<Artifact> artifacts = this.project.getArtifacts();
139  0 if (artifacts != null) {
140  0 for (Artifact artifact : artifacts) {
141  0 if (!artifact.isOptional()) {
142  0 if ("xar".equals(artifact.getType())) {
143  0 unpackXarToOutputDirectory(artifact);
144    }
145    }
146    }
147    }
148    }
149   
150    /**
151    * Create and add package configuration file to the package.
152    *
153    * @param packageFile the package when to add configuration file.
154    * @param files the files in the package.
155    * @throws Exception error when writing the configuration file.
156    */
 
157  1 toggle private void generatePackageXml(File packageFile, Collection<ArchiveEntry> files) throws Exception
158    {
159  1 getLog().info("Generating package.xml descriptor at [" + packageFile.getPath() + "]");
160   
161  1 OutputFormat outputFormat = new OutputFormat("", true);
162  1 outputFormat.setEncoding(this.encoding);
163  1 OutputStream out = new FileOutputStream(packageFile);
164  1 XMLWriter writer = new XMLWriter(out, outputFormat);
165  1 writer.write(toXML(files));
166  1 writer.close();
167  1 out.close();
168    }
169   
170    /**
171    * Generate a DOM4J Document containing the generated XML.
172    *
173    * @param files the list of files that we want to include in the generated package XML file.
174    * @return the DOM4J Document containing the generated XML
175    */
 
176  1 toggle private Document toXML(Collection<ArchiveEntry> files)
177    {
178  1 Document doc = new DOMDocument();
179   
180  1 Element packageElement = new DOMElement("package");
181  1 doc.setRootElement(packageElement);
182   
183  1 Element infoElement = new DOMElement("infos");
184  1 packageElement.add(infoElement);
185  1 addInfoElements(infoElement);
186   
187  1 Element filesElement = new DOMElement(FILES_TAG);
188  1 packageElement.add(filesElement);
189  1 addFileElements(files, filesElement);
190   
191  1 return doc;
192    }
193   
194    /**
195    * Add all the XML elements under the &lt;info&gt; element (name, description, license, author, version and whether
196    * it's a backup pack or not).
197    *
198    * @param infoElement the info element to which to add to
199    */
 
200  1 toggle private void addInfoElements(Element infoElement)
201    {
202  1 Element el = new DOMElement("name");
203  1 el.addText(this.project.getName());
204  1 infoElement.add(el);
205   
206  1 el = new DOMElement("description");
207  1 String description = this.project.getDescription();
208  1 if (description == null) {
209  0 el.addText("");
210    } else {
211  1 el.addText(description);
212    }
213  1 infoElement.add(el);
214   
215  1 el = new DOMElement("licence");
216  1 el.addText("");
217  1 infoElement.add(el);
218   
219  1 el = new DOMElement("author");
220  1 el.addText("XWiki.Admin");
221  1 infoElement.add(el);
222   
223  1 el = new DOMElement("version");
224  1 el.addText(this.project.getVersion());
225  1 infoElement.add(el);
226   
227  1 el = new DOMElement("backupPack");
228  1 el.addText("true");
229  1 infoElement.add(el);
230    }
231   
232    /**
233    * Add all the XML elements under the &lt;files&gt; element (the list of files present in the XAR).
234    *
235    * @param files the list of files that we want to include in the generated package XML file.
236    * @param filesElement the files element to which to add to
237    */
 
238  1 toggle private void addFileElements(Collection<ArchiveEntry> files, Element filesElement)
239    {
240  1 for (ArchiveEntry entry : files) {
241    // Don't add files in META-INF to the package.xml file
242  8 if (entry.getName().indexOf("META-INF") == -1) {
243  8 XWikiDocument xdoc = getDocFromXML(entry.getFile());
244  8 if (xdoc != null) {
245  8 String fullName = xdoc.getFullName();
246  8 Element element = new DOMElement(FILE_TAG);
247  8 element.setText(fullName);
248  8 element.addAttribute("language", xdoc.getLanguage());
249  8 element.addAttribute("defaultAction", "0");
250  8 filesElement.add(element);
251    }
252    }
253    }
254    }
255   
256    /**
257    * Load a XWiki document from its XML representation.
258    *
259    * @param file the file to parse.
260    * @return the loaded document object or null if the document cannot be parsed
261    */
 
262  8 toggle private XWikiDocument getDocFromXML(File file)
263    {
264  8 XWikiDocument doc = null;
265   
266  8 try {
267  8 doc = new XWikiDocument();
268  8 doc.fromXML(file);
269    } catch (Exception e) {
270  0 getLog().warn(
271    "Failed to parse [" + file.getAbsolutePath() + "], skipping it. " + "The error was [" + e.getMessage()
272    + "]", e);
273    }
274   
275  8 return doc;
276    }
277   
278    /**
279    * Gets the list of document names from a 'package.xml'-like document.
280    *
281    * @param file the XML document to parse
282    * @return the list of document names contained in the XML document
283    * @throws Exception if the XML document is invalid or it contains no document list or it doesn't exist
284    */
 
285  4 toggle protected static Collection<String> getDocumentNamesFromXML(File file) throws Exception
286    {
287  4 Collection<String> result = new LinkedList<String>();
288  4 SAXReader reader = new SAXReader();
289  4 Document domdoc;
290  4 domdoc = reader.read(file);
291   
292  4 Element filesElement = domdoc.getRootElement().element(FILES_TAG);
293   
294  4 if (filesElement == null) {
295  1 throw new Exception("The supplied document contains no document list ");
296    }
297   
298  3 Collection elements = filesElement.elements(FILE_TAG);
299  3 for (Object item : elements) {
300  18 if (item instanceof Element) {
301  18 Element currentElement = (Element) item;
302  18 String documentName = currentElement.getText();
303  18 result.add(documentName);
304    }
305    }
306   
307  3 return result;
308    }
309   
310    /**
311    * Adds the files from a specific directory to an archive. It also builds a package.xml file based on that content
312    * which is also added to the archive.
313    *
314    * @param archiver the archive in which the files will be added
315    * @param sourceDir the directory whose contents will be added to the archive
316    * @throws Exception if the files cannot be added to the archive
317    */
 
318  1 toggle private void addFilesToArchive(ZipArchiver archiver, File sourceDir) throws Exception
319    {
320  1 File generatedPackageFile = new File(sourceDir, PACKAGE_XML);
321  1 if (generatedPackageFile.exists()) {
322  1 generatedPackageFile.delete();
323    }
324   
325  1 archiver.addDirectory(sourceDir, getIncludes(), getExcludes());
326  1 generatePackageXml(generatedPackageFile, archiver.getFiles().values());
327  1 archiver.addFile(generatedPackageFile, PACKAGE_XML);
328    }
329   
330    /**
331    * Adds files from a specific directory to an archive. It uses an existing package.xml to filter the files to be
332    * added.
333    *
334    * @param archiver the archive in which the files will be added
335    * @param sourceDir the directory whose contents will be added to the archive
336    * @param packageXml the corresponding package.xml file
337    * @throws Exception if the files cannot be added to the archive
338    */
 
339  2 toggle private void addFilesToArchive(ZipArchiver archiver, File sourceDir, File packageXml) throws Exception
340    {
341  2 Collection<String> documentNames;
342  2 getLog().info("Using the existing package.xml descriptor at [" + packageXml.getPath() + "]");
343  2 try {
344  2 documentNames = getDocumentNamesFromXML(packageXml);
345    } catch (Exception e) {
346  1 getLog().error("The existing [" + PACKAGE_XML + "] is invalid.");
347  1 throw e;
348    }
349   
350    // Next, we scan the hole directory and subdirectories for documents.
351   
352  1 Queue<File> fileQueue = new LinkedList<File>();
353  1 addContentsToQueue(fileQueue, sourceDir);
354  12 while (!fileQueue.isEmpty() && !documentNames.isEmpty()) {
355  11 File currentFile = fileQueue.poll();
356  11 if (currentFile.isDirectory()) {
357  3 addContentsToQueue(fileQueue, currentFile);
358    } else {
359  8 String documentName = XWikiDocument.getFullName(currentFile);
360  8 if (documentNames.contains(documentName)) {
361   
362    // building the path the current file will have within the archive
363    /*
364    * DO NOT USE String.split since it requires a regexp. Under Windows XP, the FileSeparator is '\'
365    * when not escaped is a special character of the regexp String archivedFilePath =
366    * currentFile.getAbsolutePath().split(sourceDir.getAbsolutePath() + File.separator)[1];
367    */
368  5 String archivedFilePath =
369    currentFile.getAbsolutePath()
370    .substring((sourceDir.getAbsolutePath() + File.separator).length());
371  5 archivedFilePath.replace(File.separatorChar, '/');
372   
373  5 archiver.addFile(currentFile, archivedFilePath);
374  5 documentNames.remove(documentName);
375    }
376    }
377    }
378   
379  1 if (!documentNames.isEmpty()) {
380  0 StringBuilder errorMessage = new StringBuilder("The following documents could not be found: ");
381  0 for (String name : documentNames) {
382  0 errorMessage.append(name);
383  0 errorMessage.append(" ");
384    }
385  0 throw new Exception(errorMessage.toString());
386    }
387   
388  1 archiver.addFile(packageXml, PACKAGE_XML);
389    }
390   
391    /**
392    * Adds the contents of a specific directory to a queue of files.
393    *
394    * @param fileQueue the queue of files
395    * @param sourceDir the directory to be scanned
396    */
 
397  4 toggle private static void addContentsToQueue(Queue<File> fileQueue, File sourceDir)
398    {
399  4 for (File currentFile : sourceDir.listFiles()) {
400  11 fileQueue.add(currentFile);
401    }
402    }
403    }