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
69
70
71
72
73
74
75
76
77
78
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
89
90
91
92
93
94
95
96
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
107
108
109
110
111
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
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
159
160
161
162
163
164
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
245
246
247
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();
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
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 }