View Javadoc

1   /***************************************************************************************
2    * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved.                 *
3    * http://aspectwerkz.codehaus.org                                                    *
4    * ---------------------------------------------------------------------------------- *
5    * The software in this package is published under the terms of the LGPL license      *
6    * a copy of which has been included with this distribution in the license.txt file.  *
7    **************************************************************************************/
8   package org.codehaus.aspectwerkz.definition;
9   
10  import org.codehaus.aspectwerkz.exception.DefinitionException;
11  import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
12  import org.dom4j.Document;
13  import org.dom4j.DocumentException;
14  import org.dom4j.Element;
15  import org.dom4j.DocumentHelper;
16  import org.dom4j.io.SAXReader;
17  import org.xml.sax.EntityResolver;
18  import org.xml.sax.InputSource;
19  
20  import java.io.File;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.FileInputStream;
24  import java.io.BufferedInputStream;
25  import java.net.MalformedURLException;
26  import java.net.URL;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Set;
30  
31  /***
32   * Parses the XML definition file using <tt>dom4j</tt>.
33   *
34   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
35   */
36  public class XmlParser {
37      /***
38       * The current DTD public id. The matching dtd will be searched as a resource.
39       */
40      private final static String DTD_PUBLIC_ID = "-//AspectWerkz//DTD 2.0//EN";
41  
42      /***
43       * The DTD alias, for better user experience.
44       */
45      private final static String DTD_PUBLIC_ID_ALIAS = "-//AspectWerkz//DTD//EN";
46  
47      /***
48       * A handler to the DTD stream so that we are only using one file descriptor
49       */
50      private final static InputStream DTD_STREAM = XmlParser.class.getResourceAsStream("/aspectwerkz2.dtd");
51  
52      /***
53       * The timestamp, holding the last time that the definition was parsed.
54       */
55      private static File s_timestamp = new File(".timestamp");
56  
57      /***
58       * The AspectWerkz definitions.
59       */
60      private static Set s_definitions = null;
61  
62  //    /***
63  //     * Returns the aspect class names defined in the XML file.
64  //     *
65  //     * @param definitionFile the definition file
66  //     * @return the definitions
67  //     */
68  //    public static List getAspectClassNames(final File definitionFile) {
69  //        if (definitionFile == null) {
70  //            throw new IllegalArgumentException("definition file can not be null");
71  //        }
72  //        if (!definitionFile.exists()) {
73  //            throw new DefinitionException("definition file " + definitionFile.toString() + " does not exist");
74  //        }
75  //        try {
76  //            return getAspectClassNames(definitionFile.toURL());
77  //        } catch (MalformedURLException e) {
78  //            throw new DefinitionException(definitionFile + " does not exist");
79  //        }
80  //    }
81  
82  //    /***
83  //     * Returns the aspect class names defined in the XML file.
84  //     *
85  //     * @param definitionURL the definition URL
86  //     * @return the definitions
87  //     */
88  //    public static List getAspectClassNames(final URL definitionURL) {
89  //        if (definitionURL == null) {
90  //            throw new IllegalArgumentException("definition file can not be null");
91  //        }
92  //        try {
93  //            Document document = createDocument(definitionURL);
94  //            return DocumentParser.parseAspectClassNames(document);
95  //        } catch (DocumentException e) {
96  //            throw new DefinitionException("XML definition file <" + definitionURL + "> has errors: " + e.toString());
97  //        }
98  //    }
99  
100 //    /***
101 //     * Returns the aspect class names defined in the XML file.
102 //     *
103 //     * @param stream the input stream containing the document
104 //     * @return the definitions
105 //     */
106 //    public static List getAspectClassNames(final InputStream stream) {
107 //        try {
108 //            Document document = createDocument(stream);
109 //            return DocumentParser.parseAspectClassNames(document);
110 //        } catch (DocumentException e) {
111 //            throw new DefinitionException("XML definition file on classpath has errors: " + e.toString());
112 //        }
113 //    }
114 
115 //    /***
116 //     * Parses the XML definition file, only if it has been updated. Uses a timestamp to check for modifications.
117 //     *
118 //     * @param loader         the current class loader
119 //     * @param definitionFile the definition file
120 //     * @param isDirty        flag to mark the the definition as updated or not
121 //     * @return the definitions
122 //     */
123 //    public static Set parse(final ClassLoader loader, final File definitionFile, boolean isDirty) {
124 //        if (definitionFile == null) {
125 //            throw new IllegalArgumentException("definition file can not be null");
126 //        }
127 //        if (!definitionFile.exists()) {
128 //            throw new DefinitionException("definition file " + definitionFile.toString() + " does not exist");
129 //        }
130 //
131 //        // if definition is not updated; don't parse but return it right away
132 //        if (isNotUpdated(definitionFile)) {
133 //            isDirty = false;
134 //            return s_definitions;
135 //        }
136 //
137 //        // updated definition, ready to be parsed
138 //        try {
139 //            Document document = createDocument(definitionFile.toURL());
140 //            s_definitions = DocumentParser.parse(loader, document);
141 //            setParsingTimestamp();
142 //            isDirty = true;
143 //            return s_definitions;
144 //        } catch (MalformedURLException e) {
145 //            throw new DefinitionException(definitionFile + " does not exist");
146 //        } catch (DocumentException e) {
147 //            throw new DefinitionException("XML definition file <" + definitionFile + "> has errors: " + e.toString());
148 //        }
149 //    }
150 
151 //    /***
152 //     * Parses the XML definition file retrieved from an input stream.
153 //     *
154 //     * @param loader the current class loader
155 //     * @param stream the input stream containing the document
156 //     * @return the definitions
157 //     */
158 //    public static Set parse(final ClassLoader loader, final InputStream stream) {
159 //        try {
160 //            Document document = createDocument(stream);
161 //            s_definitions = DocumentParser.parse(loader, document);
162 //            return s_definitions;
163 //        } catch (DocumentException e) {
164 //            throw new DefinitionException("XML definition file on classpath has errors: " + e.getMessage());
165 //        }
166 //    }
167 
168     /***
169      * Parses the XML definition file not using the cache.
170      *
171      * @param loader the current class loader
172      * @param url    the URL to the definition file
173      * @return the definition object
174      */
175     public static Set parseNoCache(final ClassLoader loader, final URL url) {
176         try {
177             Document document = createDocument(url);
178             s_definitions = DocumentParser.parse(loader, document);
179             return s_definitions;
180         } catch (Exception e) {
181             throw new WrappedRuntimeException(e);
182         }
183     }
184 
185     /***
186      * Merges two DOM documents.
187      *
188      * @param document1 the first document
189      * @param document2 the second document
190      * @return the definition merged document
191      */
192     public static Document mergeDocuments(final Document document1, final Document document2) {
193         if ((document2 == null) && (document1 != null)) {
194             return document1;
195         }
196         if ((document1 == null) && (document2 != null)) {
197             return document2;
198         }
199         if ((document1 == null) && (document2 == null)) {
200             return null;
201         }
202         try {
203             Element root1 = document1.getRootElement();
204             Element root2 = document2.getRootElement();
205             for (Iterator it1 = root2.elementIterator(); it1.hasNext();) {
206                 Element element = (Element) it1.next();
207                 element.setParent(null);
208                 root1.add(element);
209             }
210         } catch (Exception e) {
211             throw new WrappedRuntimeException(e);
212         }
213         return document1;
214     }
215 
216     /***
217      * Creates a DOM document.
218      *
219      * @param url the URL to the file containing the XML
220      * @return the DOM document
221      * @throws DocumentException
222      */
223     public static Document createDocument(final URL url) throws DocumentException {
224         SAXReader reader = new SAXReader();
225         setEntityResolver(reader);
226         InputStream in = null;
227         try {
228             in = url.openStream();
229             return reader.read(in);
230         } catch (IOException e) {
231             throw new DocumentException(e);
232         } finally {
233             try {in.close();} catch (Throwable t) {;}
234         }
235     }
236 
237 //    /***
238 //     * Creates a DOM document.
239 //     *
240 //     * @param stream the stream containing the XML
241 //     * @return the DOM document
242 //     * @throws DocumentException
243 //     */
244 //    public static Document createDocument(final InputStream stream) throws DocumentException {
245 //        SAXReader reader = new SAXReader();
246 //        setEntityResolver(reader);
247 //        return reader.read(stream);
248 //    }
249 
250     /***
251      * Creates a DOM document.
252      *
253      * @param string the string containing the XML
254      * @return the DOM document
255      * @throws DocumentException
256      */
257     public static Document createDocument(final String string) throws DocumentException {
258         return DocumentHelper.parseText(string);
259     }
260 
261     /***
262      * Sets the entity resolver which is created based on the DTD from in the root dir of the AspectWerkz distribution.
263      *
264      * @param reader the reader to set the resolver in
265      */
266     private static void setEntityResolver(final SAXReader reader) {
267         EntityResolver resolver = new EntityResolver() {
268             public InputSource resolveEntity(String publicId, String systemId) {
269                 if (publicId.equals(DTD_PUBLIC_ID) || publicId.equals(DTD_PUBLIC_ID_ALIAS)) {
270                     InputStream in = DTD_STREAM;
271                     if (in == null) {
272                         System.err.println("AspectWerkz - WARN - could not open DTD");
273                         return new InputSource();
274                     } else {
275                         return new InputSource(in);
276                     }
277                 } else {
278                     System.err.println(
279                             "AspectWerkz - WARN - deprecated DTD "
280                             + publicId
281                             + " - consider upgrading to "
282                             + DTD_PUBLIC_ID
283                     );
284                     return new InputSource(); // avoid null pointer exception
285                 }
286             }
287         };
288         reader.setEntityResolver(resolver);
289     }
290 
291     /***
292      * Checks if the definition file has been updated since the last parsing.
293      *
294      * @param definitionFile the definition file
295      * @return boolean
296      */
297     private static boolean isNotUpdated(final File definitionFile) {
298         return (definitionFile.lastModified() < getParsingTimestamp()) && (s_definitions != null);
299     }
300 
301     /***
302      * Sets the timestamp for the latest parsing of the definition file.
303      */
304     private static void setParsingTimestamp() {
305         final long newModifiedTime = System.currentTimeMillis();
306         s_timestamp.setLastModified(newModifiedTime);
307     }
308 
309     /***
310      * Returns the timestamp for the last parsing of the definition file.
311      *
312      * @return the timestamp
313      */
314     private static long getParsingTimestamp() {
315         final long modifiedTime = s_timestamp.lastModified();
316         if (modifiedTime == 0L) {
317             // no timestamp, create a new one
318             try {
319                 s_timestamp.createNewFile();
320             } catch (IOException e) {
321                 throw new RuntimeException("could not create timestamp file: " + s_timestamp.getAbsolutePath());
322             }
323         }
324         return modifiedTime;
325     }
326 }