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 }