View Javadoc

1   package org.apache.torque.betwixt;
2   
3   /*
4    * Copyright 2001-2006 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License")
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import java.io.FileNotFoundException;
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.net.URL;
23  
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  import org.xml.sax.EntityResolver;
27  import org.xml.sax.InputSource;
28  import org.xml.sax.SAXException;
29  
30  /***
31   * This class resolves the where to find the versions of the Betwixt Bean Map 
32   * and DTD files to use in converting XML data to Torque OM objects. These
33   * files are found by using the methods described below.
34   * <p>
35   * 
36   * Note: Both these files can be created from the Torque DB schema by using
37   * adding the 'Betwixt add-on' to Torque's Generator and using the datadtd task.
38   * <p>
39   * 
40   * The DTD file is locate using the following method:
41   * <P>
42   * 
43   * First, the URI on the XML Import file's DOCTYPE definition is parsed to get
44   * the specified file name (allows for DTD versioning). This name is combined
45   * with the value of the dtdPackage property and the classpath is searched. If
46   * this file is not found, the value specified by the dtdFileName property is
47   * used. If neither file is found, the normal SAX parser will attempt to load
48   * the DTD using the DOCTYPE URI
49   * <P>
50   * 
51   * The Betwixt mapping file is found by searching the classpath for a file with
52   * the mapFileName located in the mapFilePackage.
53   * <p>
54   * 
55   * @author <a href="mailto:greg.monroe@dukece.com">Greg Monroe</a>
56   */
57  public class Resolver implements EntityResolver 
58  {
59      static Log logger = LogFactory.getLog(Resolver.class);
60      
61      private String dtdPackage;
62      private String dtdFileName;
63      private String mapFilePackage;
64      private String mapFileName;
65      private String dtdUri;
66  
67      /***
68       *  A resolver to get the inputSource that a SAX parser will use for
69       *  an import DTD definition.  
70       */
71      public Resolver() 
72      {
73          super();
74      }
75      
76      public Resolver( String dtdPackage ) 
77      {
78          super();
79          setDtdPackage( dtdPackage);
80      }
81  
82      /***
83       * Return an <code>InputSource</code> that provides access to the indicated
84       * entity.  Assumes that systemId is in a URL format that ends in the dtd
85       * file name, e.g. &quot;http://www.my.org/dtds/myDTD.dtd&quot;.<p>  
86       * 
87       * The file name and dtdPackage value are combine and the class path is
88       * searched for the DTD resource.  If not found, a null is returned to
89       * indicate the parser should use it's normal resolving method.  
90       *
91       * @param publicID a java.lang.String that contains the public identifier 
92       *                  of the desired entity
93       * @param systemID a java.lang.String that contains the system identifier 
94       *                  (i.e., the URL) of the desired entity
95       * @return org.xml.sax.InputSource
96       * @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String, 
97       *                                               java.lang.String)
98       */
99      public InputSource resolveEntity( String publicID, String systemID )
100             throws SAXException, IOException 
101     {
102         InputSource dtdSource = null;
103 
104         // Try to find the dtd from systemID URI first.
105         String dtdName = systemID.substring(systemID.lastIndexOf('/')+1);
106         if ( dtdName != null && ! dtdName.equals("")) 
107         {
108             if ( ! getDtdPackage().equals("")) 
109             {
110                 dtdName = getDtdPackage()+"/"+dtdName;
111             }
112             
113             try 
114             {
115                 dtdSource = makeInputSource( dtdName );
116             }
117             catch ( Exception e ) 
118             {
119                 String eMsg = "An unexpected error occured trying to " +
120                                     "locate DTD resource, '" + dtdName + "'";
121                 logger.info(eMsg, e);
122                 throw new SAXException( eMsg, e);
123             }
124             if ( dtdSource != null ) 
125             { 
126                 return dtdSource;
127             }
128         }
129         
130         // Try to located it using the DTD file name set in the Resolver.
131         dtdName = getDtdFileName();
132         if ( dtdName != null && ! dtdName.equals("")) 
133         {
134             if ( ! getDtdPackage().equals("")) 
135             {
136                 dtdName = getDtdPackage()+"/"+dtdName;
137             }
138             
139             try 
140             {
141                 dtdSource = makeInputSource( dtdName );
142             }
143             catch ( Exception e ) 
144             {
145                 String eMsg = "An unexpected error occured trying to " +
146                                     "locate DTD resource, '" + dtdName + "'";
147                 logger.info(eMsg, e);
148                 throw new SAXException( eMsg, e);
149             }
150             if ( dtdSource != null ) 
151             { 
152                 return dtdSource;
153             }
154         }
155         logger.warn("Could not resolve DTD file locally!");
156         return null;
157     }
158     
159     /***
160      * Locate the required Betwixt map file on the classpath.
161      * 
162      * @return The InputSource to read the mapping file from.
163      * @throws Exception
164      */
165     public InputSource resolveBetwixtFile( ) throws Exception  
166     {
167         InputSource mapSource = null;
168         String fileName = getBewtixtFileName();
169         if ( fileName != null && ! fileName.equals("")) 
170         {
171             try 
172             {
173                 mapSource = makeInputSource( fileName );
174             } 
175             catch ( Exception e ) 
176             {
177                 String eMsg = "An unexpected error occured trying to " +
178                                     "locate DTD resource, '" + fileName + "'";
179                 logger.info(eMsg, e);
180                 throw e;
181             }
182             if ( mapSource != null ) 
183             { 
184                 return mapSource;
185             }
186         }
187         String eMsg = "Could not resolve required Betwixt map file, '" +
188                         fileName + "'.";
189         logger.error(eMsg);
190         throw new FileNotFoundException( eMsg );
191     }
192     /***
193      * Gets the correctly qualified Betwixt mapping file name to try to 
194      * open via a resource lookup.  E.g., packageDir/fileName or just
195      * fileName.
196      * 
197      * @return The map file name to look for. 
198      */
199     public String getBewtixtFileName() {
200         String fileName = getMapFileName();
201         if ( fileName != null && ! fileName.equals("")) 
202         {
203             if ( ! getMapFilePackage().equals("")) 
204             {
205                 fileName = getMapFilePackage()+"/"+fileName;
206             }
207         }
208         return fileName;
209     }
210 
211     /***
212      * Try to find the specified resource on the classpath.
213      * 
214      * @param packageFileName
215      * @return The source or null if not found.
216      * @throws Exception 
217      */
218     publicong> InputSource makeInputSource( String packageFileName ) 
219                                                     throws Exception 
220     {
221         InputSource source = null;
222         
223         InputStream dtdStream = getClass().getClassLoader()
224                                 .getResourceAsStream(packageFileName);
225         if (dtdStream != null) 
226         {
227             source = new InputSource(dtdStream);
228             return source;
229         }
230         else 
231         {
232             logger.warn("Could not locate DTD file at " + packageFileName );
233         }
234         return source;
235     }
236     
237     /***
238      *  Resolve the DTD file's URI to use in DOCTYPE elements in the following
239      *  manner:
240      *  <p>
241      *  
242      *  If the dtdURI property is set, just use that. Otherwise, try to
243      *  find the specified dtd file locally on the Classpath.  If that 
244      *  fails, just the dtd file name.
245      *  <p>
246      *  
247      *  @throws IllegalStateException if dtdFileName has not been set.
248      */
249     public String resolveDtdUri() 
250                 throws IllegalStateException 
251     {
252         // If URI was specified, just use that.
253         String uri = getDtdUri();
254         if ( uri != null ) 
255         {
256             return uri;
257         }
258         
259         String fileName = getDtdFileName();
260         if ( fileName != null && ! fileName.equals("")) 
261         {
262             if ( ! getDtdPackage().equals("")) 
263             {
264                 fileName = getDtdPackage()+"/"+fileName;
265             }
266             URL dtdURL = getClass().getClassLoader()
267                                 .getResource(fileName);
268             if (dtdURL != null) 
269             {
270                 return dtdURL.toString();
271             }
272             // If not found, just return the dtdFileName.
273             return getDtdFileName();
274         }
275         String eMsg = "getDtdURL() called before dtdFileName was set.";
276         logger.warn(eMsg);
277         throw new IllegalStateException(eMsg);
278     }
279 
280     /***
281      * Get the file name of the DTD that that will be used to validate
282      * the XML import with. 
283      * 
284      * @return Returns the dtdFileName.
285      */
286     public String getDtdFileName() 
287     {
288         return dtdFileName;
289     }
290 
291     /***
292      * Set the file name (no path ) of the DTD file to validate
293      * the XML against.
294      * 
295      * @param dtdFileName The dtdFileName to set.
296      */
297     public void setDtdFileName(String dtdFileName) 
298     {
299         this.dtdFileName = dtdFileName;
300     }
301 
302     /***
303      * Get the package directory the DTD will be located in. This
304      * will be a directory under the classpath or jar root,
305      * e.g. org/apache/torque/dtd
306      * 
307      * @return Returns the dtdPackage or "" if not set.
308      */
309     public String getDtdPackage() 
310     {
311         if ( this.dtdPackage == null ) 
312         {
313             this.dtdPackage = "";
314         }
315         return dtdPackage;
316     }
317 
318     /***
319      * Set the package directory the DTD will be located in. This
320      * will be a directory under the classpath or jar root,
321      * e.g. org/apache/torque/dtd
322      * 
323      * @param dtdPackage The dtdPackage to set
324      */
325     public void setDtdPackage(String dtdPackage) 
326     {
327         if ( dtdPackage.startsWith("/")) 
328         {
329             dtdPackage = dtdPackage.substring(1);
330         }
331         this.dtdPackage = dtdPackage;
332     }
333 
334     /***
335      * Get the name of the Betwixt multi (bean) mapping file to be used. E.g.
336      * myDatabase.betwixt.
337      * 
338      * @return Returns the mapFileName.
339      */
340     public String getMapFileName() 
341     {
342         return mapFileName;
343     }
344 
345     /***
346      * Set the name of the Betwixt multi (bean) mapping file to be used. E.g.
347      * myDatabase.betwixt.
348      * 
349      * @param mapFileName The mapFileName to set.
350      */
351     public void setMapFileName(String mapFileName) 
352     {
353         this.mapFileName = mapFileName;
354     }
355 
356     /***
357      * Get the package directory the Betwixt mapping file will be located in. 
358      * This will be a directory under the classpath or jar root,
359      * e.g. org/apache/torque/dtd
360      * 
361      * @return Returns the mapFilePackage.
362      */
363     public String getMapFilePackage() 
364     {
365         return mapFilePackage;
366     }
367 
368     /***
369      * Set the package directory the Betwixt mapping file will be located in. 
370      * This will be a directory under the classpath or jar root,
371      * e.g. org/apache/torque/dtd
372      * 
373      * @param mapFilePackage The mapFilePackage to set.
374      */
375     public void setMapFilePackage(String mapFilePackage) 
376     {
377         this.mapFilePackage = mapFilePackage;
378     }
379 
380     /***
381      * Gets the URI to locate the DTD via the Internet (if any)
382      * 
383      * @return Returns the dtdUri or null in not set.
384      */
385     public String getDtdUri() 
386     {
387         return dtdUri;
388     }
389 
390     /***
391      * Sets the URI to locate the DTD via the Internet (if any).  This
392      * value will be used by resolveDtdUri() method if non-null.
393      * 
394      * @param dtdURI The DTD URI to use.
395      */
396     public void setDtdUri(String dtdURI) 
397     {
398         this.dtdUri = dtdURI;
399     }
400 }