View Javadoc

1   package org.astrogrid.common.j2ee.environment;
2   
3   import java.net.URL;
4   import javax.xml.parsers.DocumentBuilder;
5   import javax.xml.parsers.DocumentBuilderFactory;
6   import org.apache.commons.logging.Log;
7   import org.apache.commons.logging.LogFactory;
8   import org.w3c.dom.Document;
9   import org.w3c.dom.Node;
10  import org.w3c.dom.NodeList;
11  import org.xml.sax.EntityResolver;
12  import org.xml.sax.InputSource;
13  
14  /***
15   * A Java bean representing the environment of a web application
16   * as initialized from the deployment descriptor (web.xml).
17   * Servlets and JSPs can use this bean to display and edit the
18   * environment.
19   * <p>
20   * The bean has five properties: deploymentDescriptor,
21   * contextPath, tomcatContextFileName and envEntries.
22   * <p>
23   * The deploymentDescriptor property holds a URI (written out as
24   * a String) by which the web application can access web.xml. This
25   * parameter must be written by the client to initialize the bean;
26   * setting the parameter causes the bean to parse web.xml and to
27   * store the relevant parts of the environment description. This
28   * property may not be read back by the client.
29   * <p>
30   * The contextPath property holds the context path of the root of
31   * the web-application (i.e. the path to the application front page).
32   * The context path always begins with a slash.
33   * <p>
34   * The tomcatContextFileName property holds the unqualified file-name
35   * of the file which could be used to configure the Tomcat context if
36   * the web application is running in the Tomcat web-container. If the
37   * context path is /myWebApp then the Tomcat context can be set by
38   * writing the file $CATALINA_HOME/conf/Catalina/myWebApp.xml and
39   * the property is set to myWebApp.xml.
40   * <p>
41   * The envEntry property, which is indexed, holds the collection of
42   * {@link org.astrogrid.common.j2ee.environemnt.EnvEntry} beans
43   * derived from the env-entry elements in web.xml.
44   *
45   * @author Guy Rixon
46   */
47  public class Environment {
48    private static Log log = LogFactory.getLog(Environment.class);
49  
50    /*** Creates a new instance of Environment */
51    public Environment() {}
52  
53    /***
54     * The context path of the web application.
55     */
56    private String contextPath;
57  
58    /***
59     * Gets the context path.
60     */
61    public String getContextPath() {
62      return this.contextPath;
63    }
64  
65    /***
66     * Sets the context path. Also sets the
67     * tomcatContextFileName property.
68     */
69    public void setContextPath(String path) {
70      this.contextPath = path;
71      this.tomcatContextFileName = this.contextPath.substring(1) + ".xml";
72    }
73  
74    /***
75     * The Tomcat context-file name for this application.
76     */
77    private String tomcatContextFileName;
78  
79    /***
80     * Gets the context-file name.
81     */
82    public String getTomcatContextFileName() {
83      return this.tomcatContextFileName;
84    }
85  
86    /***
87     * The environment entries required by this application.
88     */
89    private EnvEntry[] envEntries;
90  
91    /***
92     * Gets all the environment entries.
93     */
94    public EnvEntry getEnvEntry(int index) {
95      return this.envEntries[index];
96    }
97  
98    /***
99     * Gets all the environment entries.
100    */
101   public EnvEntry[] getEnvEntry() {
102     return this.envEntries;
103   }
104 
105   /***
106    * Sets the URL for the deployment descriptor (web.xml).
107    * This causes the bean to parse the descriptor and thus
108    * to extract the environment entries.
109    */
110   public void setDeploymentDescriptor(String webDotXmlUri) throws Exception {
111     log.debug("Parsing the deployment descriptor at " + webDotXmlUri);
112     
113     // Parse web.xml into a DOM tree.
114     DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
115     factory.setValidating(false);
116     factory.setCoalescing(true);
117     factory.setExpandEntityReferences(false);
118     factory.setNamespaceAware(false);
119     DocumentBuilder domParser = factory.newDocumentBuilder();
120     domParser.setEntityResolver(new DtdResolver());
121     Document webDotXmlDoc = domParser.parse(webDotXmlUri);
122     log.debug("Now we have a DOM for the deployment descriptor.");
123     
124     // Isolate the environment entries, count them and set up their beans.
125     NodeList envEntryNodes = webDotXmlDoc.getElementsByTagName("env-entry");
126     int nBeans = envEntryNodes.getLength();
127     this.envEntries = new EnvEntry[nBeans];
128     for (int i = 0; i < nBeans; i++) {
129       this.envEntries[i] = this.parseEnvEntry(envEntryNodes.item(i));
130     }
131   }
132 
133   /***
134    * Parses an env-entry element represented as a DOM fragment.
135    *
136    * @param envEntryNode The Element node representng the env-entry element.
137    * @return A bean representing the env-entry element.
138    */
139   private EnvEntry parseEnvEntry(Node envEntryNode) throws Exception {
140     EnvEntry envEntry = new EnvEntry();
141 
142     // Transcribe the values from the DOM nodes to the bean.
143     // One bean expresses one entry value but is made from several
144     // node values. Write the value of the entry as a String;
145     // let the bean decode it.
146     NodeList childNodes = envEntryNode.getChildNodes();
147     for (int i = 0; i < childNodes.getLength(); i++) {
148       Node childNode = childNodes.item(i);
149       String nodeName = childNode.getNodeName();
150       if (nodeName.equals("env-entry-name")) {
151         envEntry.setName(this.getElementValue(childNode));
152       }
153       else if (nodeName.equals("env-entry-type")) {
154         envEntry.setType(this.getElementValue(childNode));
155       }
156       else if (nodeName.equals("env-entry-value")) {
157         envEntry.setDefaultValue(this.getElementValue(childNode));
158       }
159       else if (nodeName.equals("description")) {
160         envEntry.setDescription(this.getElementValue(childNode));
161       }
162       // If it's anything else then we just don't care about it.
163     }
164     return envEntry;
165   }
166 
167   /***
168    * Gets the value of an XML element represented by a DOM node.
169    * The element must be of a simple type with a text nodes in the
170    * DOM fragment. This method gets the concatenated value of the text nodes.
171    *
172    * @param node The DOM node representing the element.
173    * @return The value of the text-node child, or null if there is no text node.
174    */
175   private String getElementValue(Node node) {
176     // Trap all errors, including particularly Null Pointer Exceptions.
177     try {
178       // Make sure that there is at most one text node holding all the
179       // text for this element.
180       node.normalize();
181 
182       // Get all the children. Go through the family and return the
183       // text node if there is one.
184       NodeList children = node.getChildNodes();
185       for (int i = 0; i < children.getLength(); i++) {
186         Node child = children.item(i);
187         if (child.getNodeType() == Node.TEXT_NODE) {
188           return child.getNodeValue();
189         }
190       }
191 
192       // At this point we know that there was no text node,
193       // so the value is null.
194       return null;
195     }
196     catch (Throwable t) {
197       return null;
198     }
199   }
200   
201 
202   /***
203    * A resolver for the DTDs governing the deployment descriptor.
204    * This resolver forces the parser to use local copies of the DTDs.
205    * It stops the parser crashing due to lack of DTDs when the copies on
206    * the web are not available.
207    */
208   protected class DtdResolver implements EntityResolver {
209  
210     /***
211      * Locates the DTD for the deployment descriptor.
212      * The given public-ID of the DTD is resolved to a local copy
213      * of the DTD text. The given system-ID is not used. This class
214      * can only resolve the DTDs for the servlet 2.2 and 2.3 specifications.
215      *
216      * @param publicId The public id of the DTD in the instance document.
217      * @param systemId The system ID for the DTD in the instance document.
218      * @return A source based on the local copy of the DTD.
219      */
220     public InputSource resolveEntity(String publicId, String systemId) {
221       log.debug("Resolving " + publicId);
222       if (publicId.equals("-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN")) {
223         URL u = this.getClass().getResource("web-app_2_2.dtd");
224         log.debug("Resolved to " + u);
225         return new InputSource(u.toString());
226       }
227       else if (publicId.equals("-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN")) {
228         URL u = this.getClass().getResource("web-app_2_3.dtd");
229         log.debug("Resolved to " + u);
230         return new InputSource(u.toString());
231       }
232       else {
233         log.debug("Not resolved.");
234         return null;
235       }
236     }
237   }
238   
239 
240 }