Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that implements org.xml.sax.EntityResolver interface
4
 *             for resolving external entities
5
 *  Copyright: 2000 Regents of the University of California and the
6
 *             National Center for Ecological Analysis and Synthesis
7
 *    Authors: Jivka Bojilova, Matt Jones
8
 *    Release: @release@
9
 *
10
 *   '$Author: tao $'
11
 *     '$Date: 2002-06-13 11:54:51 -0700 (Thu, 13 Jun 2002) $'
12
 * '$Revision: 1217 $'
13
 *
14
 * This program is free software; you can redistribute it and/or modify
15
 * it under the terms of the GNU General Public License as published by
16
 * the Free Software Foundation; either version 2 of the License, or
17
 * (at your option) any later version.
18
 *
19
 * This program is distributed in the hope that it will be useful,
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 * GNU General Public License for more details.
23
 *
24
 * You should have received a copy of the GNU General Public License
25
 * along with this program; if not, write to the Free Software
26
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27
 */
28

    
29
package edu.ucsb.nceas.metacat;
30

    
31
import org.xml.sax.*;
32
import org.xml.sax.helpers.DefaultHandler;
33

    
34
import java.sql.*;
35
import java.io.File;
36
import java.io.Reader;
37
import java.io.BufferedReader;
38
import java.io.BufferedInputStream;
39
import java.io.FileWriter;
40
import java.io.BufferedWriter;
41
import java.io.InputStream;
42
import java.io.IOException;
43
import java.net.URL;
44
import java.net.URLConnection;
45
import java.net.MalformedURLException;
46

    
47
/** 
48
 * A database aware Class implementing EntityResolver interface for the SAX 
49
 * parser to call when processing the XML stream and intercepting any 
50
 * external entities (including the external DTD subset and external 
51
 * parameter entities, if any) before including them.
52
 */
53
public class DBEntityResolver implements EntityResolver
54
{
55
  private DBConnection connection = null;
56
  private DefaultHandler handler = null;
57
  private String docname = null;
58
  private String doctype = null;
59
  private String systemid = null;
60
  private Reader dtdtext = null;
61

    
62
  /** 
63
   * Construct an instance of the DBEntityResolver class
64
   *
65
   * @param conn the JDBC connection to which information is written
66
   */
67
  public DBEntityResolver(DBConnection conn)
68
  {
69
    this.connection= conn;
70
  }
71
  /** 
72
   * Construct an instance of the DBEntityResolver class
73
   *
74
   * @param conn the JDBC connection to which information is written
75
   * @param handler the SAX handler to determine parsing context
76
   * @param dtd Reader of new dtd to be uploaded on server's file system
77
   */
78
  public DBEntityResolver(DBConnection conn, DefaultHandler handler, Reader dtd)
79
  {
80
    this.connection = conn;
81
    this.handler = handler;
82
    this.dtdtext = dtd;
83
  }
84
   
85
  /** 
86
   * The Parser call this method before opening any external entity 
87
   * except the top-level document entity (including the external DTD subset,
88
   * external entities referenced within the DTD, and external entities 
89
   * referenced within the document element)
90
   */
91
  public InputSource resolveEntity (String publicId, String systemId)
92
                     throws SAXException
93
  {
94
    String dbSystemID;
95
    String doctype = null;
96
    
97
    // Won't have a handler under all cases
98
    if ( handler != null ) {
99
      if ( handler instanceof DBSAXHandler ) {
100
        DBSAXHandler dhandler = null;
101
        dhandler = (DBSAXHandler)handler;
102
        if ( dhandler.processingDTD() ) {
103
          // public ID is doctype
104
          if (publicId != null) {   
105
            doctype = publicId;
106
          // assume public ID (doctype) is docname
107
          } else if (systemId != null) {
108
            doctype = dhandler.getDocname();
109
          }
110
        }
111
      } else if ( handler instanceof AccessControlList ) {
112
        AccessControlList ahandler = null;
113
        ahandler = (AccessControlList)handler;
114
        //if ( ahandler.processingDTD() ) {
115
          // public ID is doctype
116
          if (publicId != null) {   
117
            doctype = publicId;
118
          // assume public ID (doctype) is docname
119
          } else if (systemId != null) {
120
            doctype = ahandler.getDocname();
121
          }
122
        //}
123
      }
124
    }
125

    
126
    // get System ID for doctype
127
    if (doctype != null) {
128
      // look at db XML Catalog for System ID 
129
      dbSystemID = getDTDSystemID(doctype);
130
      boolean doctypeIsInDB = true;
131
      // no System ID found in db XML Catalog
132
      if (dbSystemID == null) {
133
        doctypeIsInDB = false;
134
        // use the provided System ID
135
        if (systemId != null) {
136
          dbSystemID = systemId;
137
        }
138
      }
139

    
140
      // there are dtd text provided; try to upload on Metacat
141
      if ( dtdtext != null ) {
142
        dbSystemID = uploadDTD(dbSystemID);
143
      }
144

    
145
      // open URLConnection to check first
146
      InputStream istream = checkURLConnection(dbSystemID);
147

    
148
      // need to register System ID in db XML Catalog if not yet
149
      if ( !doctypeIsInDB ) {
150
        // new DTD from outside URL location; try to upload on Metacat
151
        if ( dtdtext == null ) {
152
          dbSystemID = uploadDTDFromURL(istream, dbSystemID);
153
        }
154
        registerDTD(doctype, dbSystemID);
155
      }
156
      
157
      // return a byte-input stream for use
158
      InputSource is = new InputSource(dbSystemID); 
159

    
160
      // close and open URLConnection again
161
      try {
162
        istream.close();
163
      } catch (IOException e) {
164
        throw new SAXException 
165
        ("DBEntityResolver.resolveEntity(): " + e.getMessage());
166
      }    
167
      istream = checkURLConnection(dbSystemID);
168
      is.setByteStream(istream);
169
      return is;
170

    
171
    } else {
172
      // use provided systemId for the other cases
173
      InputStream istream = checkURLConnection(systemId);
174
      return null;
175
      
176
    }
177
    
178
  }
179

    
180
  /** 
181
   * Look at db XML Catalog to get System ID (if any) for @doctype.
182
   * Return null if there are no System ID found for @doctype
183
   */
184
  private String getDTDSystemID( String doctype )
185
                 throws SAXException
186
  {
187
    String systemid = null;
188
    Statement stmt = null;
189
    DBConnection conn = null;
190
    int serialNumber = -1;
191
    try {
192
      //check out DBConnection
193
      conn=DBConnectionPool.getDBConnection("DBEntityResolver.getDTDSystemID");
194
      serialNumber=conn.getCheckOutSerialNumber();
195
      
196
      stmt = conn.createStatement();
197
      stmt.execute("SELECT system_id FROM xml_catalog " + 
198
                   "WHERE entry_type = 'DTD' AND public_id = '" +
199
                   doctype + "'");
200
      ResultSet rs = stmt.getResultSet();
201
      boolean tableHasRows = rs.next();
202
      if (tableHasRows) {
203
        systemid = rs.getString(1);
204
      }
205
      stmt.close();
206
    } catch (SQLException e) {
207
      throw new SAXException
208
      ("DBEntityResolver.getDTDSystemID(): " + e.getMessage());
209
    }
210
    finally
211
    {
212
      try
213
      {
214
        stmt.close();
215
      }//try
216
      catch (SQLException sqlE)
217
      {
218
        MetaCatUtil.debugMessage("Error in DBEntityReolver.getDTDSystemId: "
219
                                  +sqlE.getMessage(), 30);
220
      }//catch
221
      finally
222
      {
223
        DBConnectionPool.returnDBConnection(conn, serialNumber);
224
      }//finally
225
    }//finally
226

    
227
    // return the selected System ID
228
    return systemid;
229
  }
230

    
231
  /** 
232
   * Register new DTD identified by @systemId in Metacat XML Catalog 
233
   * . make a reference with @systemId for @doctype in Metacat DB
234
   */
235
  private void registerDTD ( String doctype, String systemId )
236
                 throws SAXException
237
  {
238
    //DBConnection conn = null;
239
    //int serialNumber = -1;
240
    PreparedStatement pstmt = null;
241
    // make a reference in db catalog table with @systemId for @doctype
242
    try {
243
      //check out DBConnection
244
      //conn=DBConnectionPool.getDBConnection("DBEntityResolver.registerDTD");
245
      //serialNumber=conn.getCheckOutSerialNumber();
246
      
247
      
248
      pstmt = connection.prepareStatement(
249
             "INSERT INTO xml_catalog " +
250
             "(catalog_id, entry_type, public_id, system_id) " +
251
             "VALUES (null, 'DTD', ?, ?)");
252
      // Increase usage count
253
      connection.increaseUsageCount(1);
254
      // Bind the values to the query
255
      pstmt.setString(1, doctype);
256
      pstmt.setString(2, systemId);
257
      // Do the insertion
258
      pstmt.execute();
259
      pstmt.close();
260
    } catch (SQLException e) {
261
      throw new SAXException
262
      ("DBEntityResolver.registerDTD(): " + e.getMessage());
263
    }
264
    finally
265
    {
266
      try
267
      {
268
        pstmt.close();
269
      }//try
270
      catch (SQLException sqlE)
271
      {
272
        MetaCatUtil.debugMessage("Error in DBEntityReolver.registerDTD: "
273
                                    +sqlE.getMessage(), 30);
274
      }//catch
275
      //DBConnectionPool.returnDBConnection(conn, serialNumber);
276
    }//finally
277
    
278
  }
279

    
280
  /** 
281
   * Upload new DTD text identified by @systemId to Metacat file system
282
   */
283
  private String uploadDTD ( String systemId )
284
                 throws SAXException
285
  {
286
    MetaCatUtil util = new MetaCatUtil();
287
    String dtdPath = util.getOption("dtdPath");
288
    String dtdURL = util.getOption("dtdURL");
289
    
290
    // get filename from systemId
291
    String filename = systemId;
292
    int slash = Math.max(filename.lastIndexOf('/'), filename.lastIndexOf('\\'));
293
    if ( slash > -1 ) {
294
      filename = filename.substring(slash + 1);
295
    }
296

    
297
    // writing dtd text on Metacat file system as filename
298
    try {
299
      // create a buffering character-input stream
300
      // that uses a default-sized input buffer
301
      BufferedReader in = new BufferedReader(dtdtext);
302

    
303
      // open file writer to write the input into it
304
      //String dtdPath = "/opt/tomcat/webapps/bojilova/dtd/";
305
      File f = new File(dtdPath, filename);
306
     synchronized (f) { 
307
      try {
308
        if ( f.exists() ) {
309
          throw new IOException("File already exist: " + f.getCanonicalFile());
310
        //if ( f.exists() && !f.canWrite() ) {
311
        //  throw new IOException("Not writable: " + f.getCanonicalFile());
312
        }
313
      } catch (SecurityException se) {
314
        // if a security manager exists,
315
        // its checkRead method is called for f.exist()
316
        // or checkWrite method is called for f.canWrite()
317
        throw se;
318
      }
319
      // create a buffered character-output stream
320
      // that uses a default-sized output buffer
321
      FileWriter fw = new FileWriter(f);
322
      BufferedWriter out = new BufferedWriter(fw);
323

    
324
      // read the input and write into the file writer
325
	    String inputLine;
326
	    while ( (inputLine = in.readLine()) != null ) {
327
	      out.write(inputLine, 0, inputLine.length());
328
  	    out.newLine(); //instead of out.write('\r\n');
329
	    }
330

    
331
      // the input and the output streams must be closed
332
	    in.close();
333
	    out.flush();
334
	    out.close();
335
	    fw.close();
336
     } // end of synchronized      
337
    } catch (MalformedURLException e) {
338
      throw new SAXException
339
      ("DBEntityResolver.uploadDTD(): " + e.getMessage());
340
    } catch (IOException e) {
341
      throw new SAXException
342
      ("DBEntityResolver.uploadDTD(): " + e.getMessage());
343
    } catch (SecurityException e) {
344
      throw new SAXException
345
      ("DBEntityResolver.uploadDTD(): " + e.getMessage());
346
    }
347
    
348
    //String dtdURL = "http://dev.nceas.ucsb.edu/bojilova/dtd/";
349
    return  dtdURL + filename;
350
  }
351

    
352

    
353
  /** 
354
   * Upload new DTD located at outside URL to Metacat file system
355
   */
356
  private String uploadDTDFromURL(InputStream istream, String systemId)
357
                 throws SAXException
358
  {
359
    MetaCatUtil util = new MetaCatUtil();
360
    String dtdPath = util.getOption("dtdPath");
361
    String dtdURL = util.getOption("dtdURL");
362
    
363
    // get filename from systemId
364
    String filename = systemId;
365
    int slash = Math.max(filename.lastIndexOf('/'), filename.lastIndexOf('\\'));
366
    if ( slash > -1 ) {
367
      filename = filename.substring(slash + 1);
368
    }
369

    
370
    // writing dtd text on Metacat file system as filename
371
    try {
372
      // create a buffering character-input stream
373
      // that uses a default-sized input buffer
374
      BufferedInputStream in = new BufferedInputStream(istream);
375

    
376
      // open file writer to write the input into it
377
      //String dtdPath = "/opt/tomcat/webapps/bojilova/dtd/";
378
      File f = new File(dtdPath, filename);
379
     synchronized (f) { 
380
      try {
381
        if ( f.exists() ) {
382
          throw new IOException("File already exist: " + f.getCanonicalFile());
383
        //if ( f.exists() && !f.canWrite() ) {
384
        //  throw new IOException("Not writable: " + f.getCanonicalFile());
385
        }
386
      } catch (SecurityException se) {
387
        // if a security manager exists,
388
        // its checkRead method is called for f.exist()
389
        // or checkWrite method is called for f.canWrite()
390
        throw se;
391
      }
392
      // create a buffered character-output stream
393
      // that uses a default-sized output buffer
394
      FileWriter fw = new FileWriter(f);
395
      BufferedWriter out = new BufferedWriter(fw);
396

    
397
      // read the input and write into the file writer
398
	    int inputByte;
399
	    while ( (inputByte = in.read()) != -1 ) {
400
	      out.write(inputByte);
401
  	    //out.newLine(); //instead of out.write('\r\n');
402
	    }
403

    
404
      // the input and the output streams must be closed
405
	    in.close();
406
	    out.flush();
407
	    out.close();
408
	    fw.close();
409
     } // end of synchronized
410
    } catch (MalformedURLException e) {
411
      throw new SAXException
412
      ("DBEntityResolver.uploadDTDFromURL(): " + e.getMessage());
413
    } catch (IOException e) {
414
      throw new SAXException
415
      ("DBEntityResolver.uploadDTDFromURL(): " + e.getMessage());
416
    } catch (SecurityException e) {
417
      throw new SAXException
418
      ("DBEntityResolver.uploadDTDFromURL(): " + e.getMessage());
419
    }
420
    
421
    //String dtdURL = "http://dev.nceas.ucsb.edu/bojilova/dtd/";
422
    return  dtdURL + filename;
423
  }
424

    
425
  /** 
426
   * Check URL Connection for @systemId, and return an InputStream
427
   * that can be used to read from the systemId URL.  The parser ends
428
   * up using this via the InputSource to read the DTD.
429
   *
430
   * @param systemId a URI (in practice URL) to be checked and opened
431
   */
432
  private InputStream checkURLConnection (String systemId)
433
                      throws SAXException
434
  {
435
    try {
436

    
437
      return (new URL(systemId).openStream());
438

    
439
    } catch (MalformedURLException e) {
440
      throw new SAXException
441
      ("DBEntityResolver.checkURLConnection(): " + e.getMessage());
442
    } catch (IOException e) {
443
      throw new SAXException
444
      ("DBEntityResolver.checkURLConnection(): " + e.getMessage());
445
    }    
446
  }   
447
}
(18-18/48)