KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > util > xml > JBossEntityResolver


1 /*
2  * JBoss, Home of Professional Open Source
3  * Copyright 2005, JBoss Inc., and individual contributors as indicated
4  * by the @authors tag. See the copyright.txt in the distribution for a
5  * full listing of individual contributors.
6  *
7  * This is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as
9  * published by the Free Software Foundation; either version 2.1 of
10  * the License, or (at your option) any later version.
11  *
12  * This software is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this software; if not, write to the Free
19  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21  */

22 package org.jboss.util.xml;
23
24 import java.io.IOException JavaDoc;
25 import java.io.InputStream JavaDoc;
26 import java.net.MalformedURLException JavaDoc;
27 import java.net.URI JavaDoc;
28 import java.net.URISyntaxException JavaDoc;
29 import java.net.URL JavaDoc;
30 import java.security.AccessController JavaDoc;
31 import java.security.PrivilegedAction JavaDoc;
32 import java.util.Collections JavaDoc;
33 import java.util.Map JavaDoc;
34
35 import org.jboss.logging.Logger;
36 import org.xml.sax.EntityResolver JavaDoc;
37 import org.xml.sax.InputSource JavaDoc;
38 import org.xml.sax.SAXException JavaDoc;
39
40 import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;
41
42 /**
43  * Local entity resolver to handle standard J2EE DTDs and Schemas as well as JBoss
44  * specific DTDs.
45  * <p/>
46  * Function boolean isEntityResolved() is here to avoid validation errors in
47  * descriptors that do not have a DOCTYPE declaration.
48  *
49  * @author Scott.Stark@jboss.org
50  * @author Thomas.Diesler@jboss.org
51  * @version $Revision: 2105 $
52  */

53 public class JBossEntityResolver implements EntityResolver JavaDoc
54 {
55    private static final Logger log = Logger.getLogger(JBossEntityResolver.class);
56
57    /** A class wide Map<String, String> of publicId/systemId to dtd/xsd file */
58    private static Map JavaDoc entities = new ConcurrentReaderHashMap();
59    /** A class flag indicating whether an attempt to resolve a systemID as a
60     non-file URL should produce a warning rather than a trace level log msg.
61     */

62    private static boolean warnOnNonFileURLs;
63
64    private boolean entityResolved = false;
65    /** A local entities map that overrides the class level entities */
66    private Map JavaDoc localEntities;
67
68    static
69    {
70       AccessController.doPrivileged(new PrivilegedAction JavaDoc()
71       {
72          public Object JavaDoc run()
73          {
74             warnOnNonFileURLs = new Boolean JavaDoc(System.getProperty("org.jboss.resolver.warning", "false")).booleanValue();
75             return null;
76          }
77       });
78       registerEntity("http://java.sun.com/xml/ns/j2ee/application_1_4.xsd", "application_1_4.xsd");
79       registerEntity("http://java.sun.com/xml/ns/javaee/application_5.xsd", "application_5.xsd");
80       registerEntity("http://java.sun.com/xml/ns/j2ee/application-client_1_4.xsd", "application-client_1_4.xsd");
81       registerEntity("http://java.sun.com/xml/ns/javaee/application-client_5.xsd", "application-client_5.xsd");
82       registerEntity("http://java.sun.com/xml/ns/j2ee/connector_1_5.xsd", "connector_1_5.xsd");
83       registerEntity("http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd", "ejb-jar_2_1.xsd");
84       registerEntity("http://java.sun.com/xml/ns/j2ee/j2ee_1_4.xsd", "j2ee_1_4.xsd");
85       registerEntity("http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd", "web-app_2_4.xsd");
86       registerEntity("http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd", "web-app_2_5.xsd");
87       registerEntity("http://schemas.xmlsoap.org/soap/encoding/", "soap-encoding_1_1.xsd");
88       registerEntity("http://www.ibm.com/webservices/xsd/j2ee_web_services_client_1_1.xsd", "j2ee_web_services_client_1_1.xsd");
89       registerEntity("http://www.ibm.com/webservices/xsd/j2ee_web_services_1_1.xsd", "j2ee_web_services_1_1.xsd");
90       registerEntity("http://www.ibm.com/webservices/xsd/j2ee_jaxrpc_mapping_1_1.xsd", "j2ee_jaxrpc_mapping_1_1.xsd");
91       registerEntity("http://www.w3.org/2001/xml.xsd", "xml.xsd");
92       registerEntity("http://www.w3.org/2005/05/xmlmime", "xml-media-types.xsd");
93
94       // ejb related
95
registerEntity("-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN", "ejb-jar.dtd");
96       registerEntity("-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN", "ejb-jar_2_0.dtd");
97       // ear stuff
98
registerEntity("-//Sun Microsystems, Inc.//DTD J2EE Application 1.2//EN", "application_1_2.dtd");
99       registerEntity("-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN", "application_1_3.dtd");
100       registerEntity("-//Sun Microsystems, Inc.//DTD J2EE Application Client 1.3//EN", "application-client_1_3.dtd");
101       // connector descriptors
102
registerEntity("-//Sun Microsystems, Inc.//DTD Connector 1.0//EN", "connector_1_0.dtd");
103       // war meta-data
104
registerEntity("-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN", "web-app_2_2.dtd");
105       registerEntity("-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN", "web-app_2_3.dtd");
106       // jboss-specific
107
registerEntity("-//JBoss//DTD J2EE Application 1.3//EN", "jboss-app_3_0.dtd");
108       registerEntity("-//JBoss//DTD J2EE Application 1.3V2//EN", "jboss-app_3_2.dtd");
109       registerEntity("-//JBoss//DTD J2EE Application 1.4//EN", "jboss-app_4_0.dtd");
110       registerEntity("-//JBoss//DTD JAWS//EN", "jaws.dtd");
111       registerEntity("-//JBoss//DTD JAWS 2.4//EN", "jaws_2_4.dtd");
112       registerEntity("-//JBoss//DTD JAWS 3.0//EN", "jaws_3_0.dtd");
113       registerEntity("-//JBoss//DTD JBOSS//EN", "jboss.dtd");
114       registerEntity("-//JBoss//DTD JBOSS 2.4//EN", "jboss_2_4.dtd");
115       registerEntity("-//JBoss//DTD JBOSS 3.0//EN", "jboss_3_0.dtd");
116       registerEntity("-//JBoss//DTD JBOSS 3.2//EN", "jboss_3_2.dtd");
117       registerEntity("-//JBoss//DTD JBOSS 4.0//EN", "jboss_4_0.dtd");
118       registerEntity("-//JBoss//DTD JBOSS 5.0//EN", "jboss_5_0.dtd");
119       registerEntity("-//JBoss//DTD JBOSSCMP-JDBC 3.0//EN", "jbosscmp-jdbc_3_0.dtd");
120       registerEntity("-//JBoss//DTD JBOSSCMP-JDBC 3.2//EN", "jbosscmp-jdbc_3_2.dtd");
121       registerEntity("-//JBoss//DTD JBOSSCMP-JDBC 4.0//EN", "jbosscmp-jdbc_4_0.dtd");
122       registerEntity("-//JBoss//DTD Web Application 2.2//EN", "jboss-web.dtd");
123       registerEntity("-//JBoss//DTD Web Application 2.3//EN", "jboss-web_3_0.dtd");
124       registerEntity("-//JBoss//DTD Web Application 2.3V2//EN", "jboss-web_3_2.dtd");
125       registerEntity("-//JBoss//DTD Web Application 2.4//EN", "jboss-web_4_0.dtd");
126       registerEntity("-//JBoss//DTD Web Application 5.0//EN", "jboss-web_5_0.dtd");
127       registerEntity("-//JBoss//DTD Application Client 3.2//EN", "jboss-client_3_2.dtd");
128       registerEntity("-//JBoss//DTD Application Client 4.0//EN", "jboss-client_4_0.dtd");
129       registerEntity("-//JBoss//DTD Application Client 5.0//EN", "jboss-client_5_0.dtd");
130       registerEntity("-//JBoss//DTD MBean Service 3.2//EN", "jboss-service_3_2.dtd");
131       registerEntity("-//JBoss//DTD MBean Service 4.0//EN", "jboss-service_4_0.dtd");
132       registerEntity("-//JBoss//DTD JBOSS XMBEAN 1.0//EN", "jboss_xmbean_1_0.dtd");
133       registerEntity("-//JBoss//DTD JBOSS XMBEAN 1.1//EN", "jboss_xmbean_1_1.dtd");
134       registerEntity("-//JBoss//DTD JBOSS XMBEAN 1.2//EN", "jboss_xmbean_1_2.dtd");
135       registerEntity("-//JBoss//DTD JBOSS Security Config 3.0//EN", "security_config.dtd");
136       registerEntity("-//JBoss//DTD JBOSS JCA Config 1.0//EN", "jboss-ds_1_0.dtd");
137       registerEntity("-//JBoss//DTD JBOSS JCA Config 1.5//EN", "jboss-ds_1_5.dtd");
138       registerEntity("http://www.jboss.org/j2ee/schema/security-config_4_0.xsd", "security-config_4_0.xsd");
139       registerEntity("urn:jboss:aop-deployer", "aop-deployer_1_1.xsd");
140       registerEntity("urn:jboss:aop-beans:1.0", "aop-beans_1_0.xsd");
141       registerEntity("urn:jboss:bean-deployer", "bean-deployer_1_0.xsd");
142       registerEntity("urn:jboss:bean-deployer:2.0", "bean-deployer_2_0.xsd");
143       registerEntity("urn:jboss:javabean:1.0", "javabean_1_0.xsd");
144       registerEntity("urn:jboss:security-config:4.1", "security-config_4_1.xsd");
145       registerEntity("urn:jboss:security-config:5.0", "security-config_5_0.xsd");
146       registerEntity("urn:jboss:jndi-binding-service:1.0", "jndi-binding-service_1_0.xsd");
147       registerEntity("urn:jboss:user-roles:1.0", "user-roles_1_0.xsd");
148       // xml
149
registerEntity("-//W3C//DTD/XMLSCHEMA 200102//EN", "XMLSchema.dtd");
150       registerEntity("http://www.w3.org/2001/XMLSchema.dtd", "XMLSchema.dtd");
151       registerEntity("datatypes", "datatypes.dtd"); // This dtd doesn't have a publicId - see XMLSchema.dtd
152
registerEntity("http://www.w3.org/XML/1998/namespace", "xml.xsd");
153
154       //ejb3 + jee5 related
155
registerEntity("http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd", "ejb-jar_3_0.xsd");
156       registerEntity("http://java.sun.com/xml/ns/javaee/javaee_web_services_client_1_2.xsd", "javaee_web_services_client_1_2.xsd");
157       registerEntity("http://java.sun.com/xml/ns/javaee/javaee_5.xsd", "javaee_5.xsd");
158       registerEntity("http://www.jboss.org/j2ee/schema/jboss_5_0.xsd", "jboss_5_0.xsd");
159    }
160
161    /**
162     Obtain a read-only view of the current entity map.
163
164     @return Map<String, String> of the publicID/systemID to dtd/schema file name
165     */

166    public static Map JavaDoc getEntityMap()
167    {
168       return Collections.unmodifiableMap(entities);
169    }
170
171    public static boolean isWarnOnNonFileURLs()
172    {
173       return warnOnNonFileURLs;
174    }
175    public static void setWarnOnNonFileURLs(boolean warnOnNonFileURLs)
176    {
177       JBossEntityResolver.warnOnNonFileURLs = warnOnNonFileURLs;
178    }
179
180    /**
181     * Register the mapping from the public id/system id to the dtd/xsd file
182     * name. This overwrites any existing mapping.
183     *
184     * @param id the DOCTYPE public id or system id such as
185     * "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN"
186     * @param dtdFileName the simple dtd/xsd file name, "ejb-jar.dtd"
187     */

188    public static void registerEntity(String JavaDoc id, String JavaDoc dtdFileName)
189    {
190       entities.put(id, dtdFileName);
191    }
192
193    /**
194     * Register the mapping from the public id/system id to the dtd/xsd file
195     * name. This overwrites any existing mapping.
196     *
197     * @param id the DOCTYPE public id or system id such as
198     * "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN"
199     * @param dtdOrSchema the simple dtd/xsd file name, "ejb-jar.dtd"
200     */

201    public synchronized void registerLocalEntity(String JavaDoc id, String JavaDoc dtdOrSchema)
202    {
203       if( localEntities == null )
204          localEntities = new ConcurrentReaderHashMap();
205       localEntities.put(id, dtdOrSchema);
206    }
207
208    /**
209     Returns DTD/Schema inputSource. The resolution logic is:
210
211     1. Check the publicId against the current registered values in the class
212     mapping of entity name to dtd/schema file name. If found, the resulting
213     file name is passed to the loadClasspathResource to locate the file as a
214     classpath resource.
215
216     2. Check the systemId against the current registered values in the class
217     mapping of entity name to dtd/schema file name. If found, the resulting
218     file name is passed to the loadClasspathResource to locate the file as a
219     classpath resource.
220
221     3. Strip the systemId name down to the simple file name by removing an URL
222     style path elements (myschemas/x.dtd becomes x.dtd), and call
223     loadClasspathResource to locate the simple file name as a classpath resource.
224
225     4. Attempt to resolve the systemId as a URL from which the schema can be
226     read. If the URL input stream can be opened this returned as the resolved
227     input.
228
229     @param publicId - Public ID of DTD, or null if it is a schema
230     @param systemId - the system ID of DTD or Schema
231     @return InputSource of entity
232     */

233    public InputSource JavaDoc resolveEntity(String JavaDoc publicId, String JavaDoc systemId) throws SAXException JavaDoc, IOException JavaDoc
234    {
235       entityResolved = false;
236
237       // nothing to resolve
238
if( publicId == null && systemId == null )
239          return null;
240
241       boolean trace = log.isTraceEnabled();
242
243       // Look for a registered publicID
244
InputSource JavaDoc inputSource = resolvePublicID(publicId, trace);
245
246       if( inputSource == null )
247       {
248          // Try to resolve the systemID from the registry
249
inputSource = resolveSystemID(systemId, trace);
250       }
251
252       if( inputSource == null )
253       {
254          // Try to resolve the systemID as a classpath reference under dtd or schema
255
inputSource = resolveClasspathName(systemId, trace);
256       }
257
258       if( inputSource == null )
259       {
260          // Try to resolve the systemID as a absolute URL
261
inputSource = resolveSystemIDasURL(systemId, trace);
262       }
263
264       entityResolved = (inputSource != null);
265       
266       if (entityResolved == false)
267          log.debug("Cannot resolve [publicID=" + publicId + ",systemID=" + systemId + "]");
268       
269       return inputSource;
270    }
271
272    /**
273     * Returns the boolean value to inform id DTD was found in the XML file or not
274     *
275     * @todo this is not thread safe and should be removed?
276     *
277     * @return boolean - true if DTD was found in XML
278     */

279    public boolean isEntityResolved()
280    {
281       return entityResolved;
282    }
283
284    /**
285     Load the schema from the class entity to schema file mapping.
286     @see #registerEntity(String, String)
287
288     @param publicId - the public entity name of the schema
289     @param trace - trace level logging flag
290     @return the InputSource for the schema file found on the classpath, null
291       if the publicId is not registered or found.
292     */

293    private InputSource JavaDoc resolvePublicID(String JavaDoc publicId, boolean trace)
294    {
295       if( publicId == null )
296          return null;
297
298       if (trace)
299          log.trace("resolvePublicID, publicId=" + publicId);
300
301       InputSource JavaDoc inputSource = null;
302
303       String JavaDoc filename = null;
304       if( localEntities != null )
305          filename = (String JavaDoc) localEntities.get(publicId);
306       if( filename == null )
307          filename = (String JavaDoc) entities.get(publicId);
308
309       if( filename != null )
310       {
311          if (trace)
312             log.trace("Found entity from publicId=" + publicId + " fileName=" + filename);
313
314          InputStream JavaDoc ins = loadClasspathResource(filename, trace);
315          if( ins != null )
316          {
317             inputSource = new InputSource JavaDoc(ins);
318             inputSource.setPublicId(publicId);
319          }
320          else
321          {
322             log.trace("Cannot load publicId from classpath resource: " + filename);
323             
324             // Try the file name as a URI
325
inputSource = resolveSystemIDasURL(filename, trace);
326             
327             if (inputSource == null)
328                log.warn("Cannot load publicId from resource: " + filename);
329          }
330       }
331
332       return inputSource;
333    }
334
335    /**
336     Attempt to use the systemId as a URL from which the schema can be read. This
337     checks to see whether the systemId is a key to an entry in the class
338     entity map.
339
340     @param systemId - the systemId
341     @param trace - trace level logging flag
342     @return the URL InputSource if the URL input stream can be opened, null
343       if the systemId is not a URL or could not be opened.
344     */

345    private InputSource JavaDoc resolveSystemID(String JavaDoc systemId, boolean trace)
346    {
347       if( systemId == null )
348          return null;
349
350       if( trace )
351          log.trace("resolveSystemID, systemId="+systemId);
352
353       InputSource JavaDoc inputSource = null;
354
355       // Try to resolve the systemId as an entity key
356
String JavaDoc filename = null;
357       if( localEntities != null )
358          filename = (String JavaDoc) localEntities.get(systemId);
359       if( filename == null )
360          filename = (String JavaDoc) entities.get(systemId);
361
362       if ( filename != null )
363       {
364          if( trace )
365             log.trace("Found entity systemId=" + systemId + " fileName=" + filename);
366
367          InputStream JavaDoc ins = loadClasspathResource(filename, trace);
368          if( ins != null )
369          {
370             inputSource = new InputSource JavaDoc(ins);
371             inputSource.setSystemId(systemId);
372          }
373          else
374          {
375             log.warn("Cannot load systemId from resource: " + filename);
376          }
377       }
378
379       return inputSource;
380    }
381
382    /**
383    Attempt to use the systemId as a URL from which the schema can be read. This
384    uses the systemID as a URL.
385
386    @param systemId - the systemId
387    @param trace - trace level logging flag
388    @return the URL InputSource if the URL input stream can be opened, null
389      if the systemId is not a URL or could not be opened.
390    */

391   private InputSource JavaDoc resolveSystemIDasURL(String JavaDoc systemId, boolean trace)
392   {
393      if( systemId == null )
394         return null;
395
396      if( trace )
397         log.trace("resolveSystemIDasURL, systemId="+systemId);
398
399      InputSource JavaDoc inputSource = null;
400
401      // Try to use the systemId as a URL to the schema
402
try
403       {
404          if (trace)
405             log.trace("Trying to resolve systemId as a URL");
406          
407          URL JavaDoc url = new URL JavaDoc(systemId);
408          if (warnOnNonFileURLs && url.getProtocol().equalsIgnoreCase("file") == false)
409          {
410             log.warn("Trying to resolve systemId as a non-file URL: " + systemId);
411          }
412
413          InputStream JavaDoc ins = url.openStream();
414          if (ins != null)
415          {
416             inputSource = new InputSource JavaDoc(ins);
417             inputSource.setSystemId(systemId);
418          }
419          else
420          {
421             log.warn("Cannot load systemId as URL: " + systemId);
422          }
423          
424          if (trace)
425             log.trace("Resolved systemId as a URL");
426       }
427       catch (MalformedURLException JavaDoc ignored)
428       {
429          if (trace)
430             log.trace("SystemId is not a url: " + systemId, ignored);
431       }
432       catch (IOException JavaDoc e)
433       {
434          if (trace)
435             log.trace("Failed to obtain URL.InputStream from systemId: " + systemId, e);
436       }
437       return inputSource;
438   }
439
440    /**
441     Resolve the systemId as a classpath resource. If not found, the
442     systemId is simply used as a classpath resource name.
443
444     @param systemId - the system ID of DTD or Schema
445     @param trace - trace level logging flag
446     @return the InputSource for the schema file found on the classpath, null
447       if the systemId is not registered or found.
448     */

449    private InputSource JavaDoc resolveClasspathName(String JavaDoc systemId, boolean trace)
450    {
451       if( systemId == null )
452          return null;
453
454       if( trace )
455          log.trace("resolveClasspathName, systemId="+systemId);
456       String JavaDoc filename = systemId;
457       // Parse the systemId as a uri to get the final path component
458
try
459       {
460          URI JavaDoc url = new URI JavaDoc(systemId);
461          String JavaDoc path = url.getPath();
462          if( path == null )
463             path = url.getSchemeSpecificPart();
464          int slash = path.lastIndexOf('/');
465          if( slash >= 0 )
466             filename = path.substring(slash + 1);
467          else
468             filename = path;
469
470          if(path.length() == 0)
471             return null;
472
473          if (trace)
474             log.trace("Mapped systemId to filename: " + filename);
475       }
476       catch (URISyntaxException JavaDoc e)
477       {
478          if (trace)
479             log.trace("systemId: is not a URI, using systemId as resource", e);
480       }
481
482       // Resolve the filename as a classpath resource
483
InputStream JavaDoc is = loadClasspathResource(filename, trace);
484       InputSource JavaDoc inputSource = null;
485       if( is != null )
486       {
487          inputSource = new InputSource JavaDoc(is);
488          inputSource.setSystemId(systemId);
489       }
490       return inputSource;
491    }
492
493    /**
494     Look for the resource name on the thread context loader resource path. This
495     first simply tries the resource name as is, and if not found, the resource
496     is prepended with either "dtd/" or "schema/" depending on whether the
497     resource ends in ".dtd" or ".xsd".
498
499     @param resource - the classpath resource name of the schema
500     @param trace - trace level logging flag
501     @return the resource InputStream if found, null if not found.
502     */

503    private InputStream JavaDoc loadClasspathResource(String JavaDoc resource, boolean trace)
504    {
505       ClassLoader JavaDoc loader = Thread.currentThread().getContextClassLoader();
506       URL JavaDoc url = loader.getResource(resource);
507       if( url == null )
508       {
509          /* Prefix the simple filename with the schema type patch as this is the
510                naming convention for the jboss bundled schemas.
511             */

512          if( resource.endsWith(".dtd") )
513             resource = "dtd/" + resource;
514          else if( resource.endsWith(".xsd") )
515             resource = "schema/" + resource;
516          url = loader.getResource(resource);
517       }
518
519       InputStream JavaDoc inputStream = null;
520       if( url != null )
521       {
522          if( trace )
523             log.trace(resource+" maps to URL: "+url);
524          try
525          {
526             inputStream = url.openStream();
527          }
528          catch(IOException JavaDoc e)
529          {
530             log.debug("Failed to open url stream", e);
531          }
532       }
533       return inputStream;
534    }
535
536 }
537
Popular Tags