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: sgarg $'
11
 *     '$Date: 2005-10-10 11:06:55 -0700 (Mon, 10 Oct 2005) $'
12
 * '$Revision: 2663 $'
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.apache.log4j.Logger;
32
import org.xml.sax.*;
33
import org.xml.sax.helpers.DefaultHandler;
34

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

    
48
/**
49
 * A database aware Class implementing EntityResolver interface for the SAX
50
 * parser to call when processing the XML stream and intercepting any
51
 * external entities (including the external DTD subset and external
52
 * parameter entities, if any) before including them.
53
 */
54
public class DBEntityResolver implements EntityResolver
55
{
56
  private DBConnection connection = null;
57
  private DefaultHandler handler = null;
58
  private String docname = null;
59
  private String doctype = null;
60
  private String systemid = null;
61
  private Reader dtdtext = null;
62
  private static Logger logMetacat = Logger.getLogger(DBEntityResolver.class);
63
  
64
  /**
65
   * Construct an instance of the DBEntityResolver class
66
   *
67
   * @param conn the JDBC connection to which information is written
68
   */
69
  public DBEntityResolver(DBConnection conn)
70
  {
71
    this.connection= conn;
72
  }
73
  /**
74
   * Construct an instance of the DBEntityResolver class
75
   *
76
   * @param conn the JDBC connection to which information is written
77
   * @param handler the SAX handler to determine parsing context
78
   * @param dtd Reader of new dtd to be uploaded on server's file system
79
   */
80
  public DBEntityResolver(DBConnection conn, DefaultHandler handler, Reader dtd)
81
  {
82
    this.connection = conn;
83
    this.handler = handler;
84
    this.dtdtext = dtd;
85
  }
86

    
87
  /**
88
   * The Parser call this method before opening any external entity
89
   * except the top-level document entity (including the external DTD subset,
90
   * external entities referenced within the DTD, and external entities
91
   * referenced within the document element)
92
   */
93
  public InputSource resolveEntity (String publicId, String systemId)
94
                     throws SAXException
95
  {
96
    logMetacat.debug("in DBEntityResolver.resolveEntity");
97
    String dbSystemID;
98
    String doctype = null;
99

    
100
    // Won't have a handler under all cases
101
    if ( handler != null ) {
102
      if ( handler instanceof DBSAXHandler ) {
103
        DBSAXHandler dhandler = null;
104
        dhandler = (DBSAXHandler)handler;
105
        if ( dhandler.processingDTD() ) {
106
         
107
          // public ID is doctype
108
          if (publicId != null) {
109
            doctype = publicId;
110
            logMetacat.info("in get type from publicId and doctype is: "
111
                                     +doctype);
112
          // assume public ID (doctype) is docname
113
          } else if (systemId != null) {
114
            doctype = dhandler.getDocname();
115
          }
116
        }
117
      } else if ( handler instanceof AccessControlList ) {
118
        AccessControlList ahandler = null;
119
        ahandler = (AccessControlList)handler;
120
        //if ( ahandler.processingDTD() ) {
121
          // public ID is doctype
122
          if (publicId != null) {
123
            doctype = publicId;
124
          // assume public ID (doctype) is docname
125
          } else if (systemId != null) {
126
            doctype = ahandler.getDocname();
127
          }
128
        //}
129
      }
130
    }
131

    
132
    // get System ID for doctype
133
    if (doctype != null) {
134
      // look at db XML Catalog for System ID
135
      logMetacat.info("get systemId from doctype: "+doctype);
136
      dbSystemID = getDTDSystemID(doctype);
137
      logMetacat.info("The Systemid is: "+dbSystemID);
138
      boolean doctypeIsInDB = true;
139
      // no System ID found in db XML Catalog
140
      if (dbSystemID == null) {
141
        doctypeIsInDB = false;
142
        // use the provided System ID
143
        if (systemId != null) {
144
          dbSystemID = systemId;
145
        }
146
        logMetacat.info("If above Systemid is null and then get "
147
                                 +" system id from file" + dbSystemID);
148
      }
149
      // there are dtd text provided; try to upload on Metacat
150
      if ( dtdtext != null ) {
151
        dbSystemID = uploadDTD(dbSystemID);
152
      }
153

    
154
      // open URLConnection to check first
155
      InputStream istream = checkURLConnection(dbSystemID);
156

    
157
      // need to register System ID in db XML Catalog if not yet
158
      if ( !doctypeIsInDB ) {
159
        // new DTD from outside URL location; try to upload on Metacat
160
        if ( dtdtext == null ) {
161
          dbSystemID = uploadDTDFromURL(istream, dbSystemID);
162
        }
163
        registerDTD(doctype, dbSystemID);
164
      }
165
      // return a byte-input stream for use
166
      InputSource is = new InputSource(dbSystemID);
167

    
168
      // close and open URLConnection again
169
      try {
170
        istream.close();
171
      } catch (IOException e) {
172
        throw new SAXException
173
        ("DBEntityResolver.resolveEntity(): " + e.getMessage());
174
      }
175
      istream = checkURLConnection(dbSystemID);
176
      is.setByteStream(istream);
177
      return is;
178
    } else {
179
      // use provided systemId for the other cases
180
      logMetacat.info("doctype is null and using system id from file");
181
      InputStream istream = checkURLConnection(systemId);
182
      return null;
183

    
184
    }
185

    
186
  }
187

    
188
  /**
189
   * Look at db XML Catalog to get System ID (if any) for @doctype.
190
   * Return null if there are no System ID found for @doctype
191
   */
192
  public static String getDTDSystemID( String doctype )
193
                 throws SAXException
194
  {
195
    String systemid = null;
196
    Statement stmt = null;
197
    DBConnection conn = null;
198
    int serialNumber = -1;
199
    try {
200
      //check out DBConnection
201
      conn=DBConnectionPool.getDBConnection("DBEntityResolver.getDTDSystemID");
202
      serialNumber=conn.getCheckOutSerialNumber();
203

    
204
      stmt = conn.createStatement();
205
      stmt.execute("SELECT system_id FROM xml_catalog " +
206
                   "WHERE entry_type = 'DTD' AND public_id = '" +
207
                   doctype + "'");
208
      ResultSet rs = stmt.getResultSet();
209
      boolean tableHasRows = rs.next();
210
      if (tableHasRows) {
211
        systemid = rs.getString(1);
212
      }
213
      stmt.close();
214
    } catch (SQLException e) {
215
      throw new SAXException
216
      ("DBEntityResolver.getDTDSystemID(): " + e.getMessage());
217
    }
218
    finally
219
    {
220
      try
221
      {
222
        stmt.close();
223
      }//try
224
      catch (SQLException sqlE)
225
      {
226
        logMetacat.error("Error in DBEntityReolver.getDTDSystemId: "
227
                                  +sqlE.getMessage());
228
      }//catch
229
      finally
230
      {
231
        DBConnectionPool.returnDBConnection(conn, serialNumber);
232
      }//finally
233
    }//finally
234

    
235
    // return the selected System ID
236
    return systemid;
237
  }
238

    
239
  /**
240
   * Register new DTD identified by @systemId in Metacat XML Catalog
241
   * . make a reference with @systemId for @doctype in Metacat DB
242
   */
243
  private void registerDTD ( String doctype, String systemId )
244
                 throws SAXException
245
  {
246
    //DBConnection conn = null;
247
    //int serialNumber = -1;
248
    PreparedStatement pstmt = null;
249
    // make a reference in db catalog table with @systemId for @doctype
250
    try {
251
      //check out DBConnection
252
      //conn=DBConnectionPool.getDBConnection("DBEntityResolver.registerDTD");
253
      //serialNumber=conn.getCheckOutSerialNumber();
254

    
255

    
256
      pstmt = connection.prepareStatement(
257
             "INSERT INTO xml_catalog " +
258
             "(catalog_id, entry_type, public_id, system_id) " +
259
             "VALUES (null, 'DTD', ?, ?)");
260
      // Increase usage count
261
      connection.increaseUsageCount(1);
262
      // Bind the values to the query
263
      pstmt.setString(1, doctype);
264
      pstmt.setString(2, systemId);
265
      // Do the insertion
266
      pstmt.execute();
267
      pstmt.close();
268
    } catch (SQLException e) {
269
      throw new SAXException
270
      ("DBEntityResolver.registerDTD(): " + e.getMessage());
271
    }
272
    finally
273
    {
274
      try
275
      {
276
        pstmt.close();
277
      }//try
278
      catch (SQLException sqlE)
279
      {
280
        logMetacat.error("Error in DBEntityReolver.registerDTD: "
281
                                    +sqlE.getMessage());
282
      }//catch
283
      //DBConnectionPool.returnDBConnection(conn, serialNumber);
284
    }//finally
285

    
286
  }
287

    
288
  /**
289
   * Upload new DTD text identified by @systemId to Metacat file system
290
   */
291
  private String uploadDTD ( String systemId )
292
                 throws SAXException
293
  {
294
    MetaCatUtil util = new MetaCatUtil();
295
    String dtdPath = util.getOption("dtdPath");
296
    String dtdURL = util.getOption("dtdURL");
297

    
298
    // get filename from systemId
299
    String filename = systemId;
300
    int slash = Math.max(filename.lastIndexOf('/'), filename.lastIndexOf('\\'));
301
    if ( slash > -1 ) {
302
      filename = filename.substring(slash + 1);
303
    }
304

    
305
    // writing dtd text on Metacat file system as filename
306
    try {
307
      // create a buffering character-input stream
308
      // that uses a default-sized input buffer
309
      BufferedReader in = new BufferedReader(dtdtext);
310

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

    
332
      // read the input and write into the file writer
333
      String inputLine;
334
      while ( (inputLine = in.readLine()) != null ) {
335
        out.write(inputLine, 0, inputLine.length());
336
        out.newLine(); //instead of out.write('\r\n');
337
      }
338

    
339
      // the input and the output streams must be closed
340
      in.close();
341
      out.flush();
342
      out.close();
343
      fw.close();
344
     } // end of synchronized
345
    } catch (MalformedURLException e) {
346
      throw new SAXException
347
      ("DBEntityResolver.uploadDTD(): " + e.getMessage());
348
    } catch (IOException e) {
349
      throw new SAXException
350
      ("DBEntityResolver.uploadDTD(): " + e.getMessage());
351
    } catch (SecurityException e) {
352
      throw new SAXException
353
      ("DBEntityResolver.uploadDTD(): " + e.getMessage());
354
    }
355

    
356
    //String dtdURL = "http://dev.nceas.ucsb.edu/bojilova/dtd/";
357
    return  dtdURL + filename;
358
  }
359

    
360

    
361
  /**
362
   * Upload new DTD located at outside URL to Metacat file system
363
   */
364
  private String uploadDTDFromURL(InputStream istream, String systemId)
365
                 throws SAXException
366
  {
367
    MetaCatUtil util = new MetaCatUtil();
368
    String dtdPath = util.getOption("dtdPath");
369
    String dtdURL = util.getOption("dtdURL");
370

    
371
    // get filename from systemId
372
    String filename = systemId;
373
    int slash = Math.max(filename.lastIndexOf('/'), filename.lastIndexOf('\\'));
374
    if ( slash > -1 ) {
375
      filename = filename.substring(slash + 1);
376
    }
377

    
378
    // writing dtd text on Metacat file system as filename
379
    try {
380
      // create a buffering character-input stream
381
      // that uses a default-sized input buffer
382
      BufferedInputStream in = new BufferedInputStream(istream);
383

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

    
405
      // read the input and write into the file writer
406
      int inputByte;
407
      while ( (inputByte = in.read()) != -1 ) {
408
        out.write(inputByte);
409
        //out.newLine(); //instead of out.write('\r\n');
410
      }
411

    
412
      // the input and the output streams must be closed
413
      in.close();
414
      out.flush();
415
      out.close();
416
      fw.close();
417
     } // end of synchronized
418
    } catch (MalformedURLException e) {
419
      throw new SAXException
420
      ("DBEntityResolver.uploadDTDFromURL(): " + e.getMessage());
421
    } catch (IOException e) {
422
      throw new SAXException
423
      ("DBEntityResolver.uploadDTDFromURL(): " + e.getMessage());
424
    } catch (SecurityException e) {
425
      throw new SAXException
426
      ("DBEntityResolver.uploadDTDFromURL(): " + e.getMessage());
427
    }
428

    
429
    //String dtdURL = "http://dev.nceas.ucsb.edu/bojilova/dtd/";
430
    return  dtdURL + filename;
431
  }
432

    
433
  /**
434
   * Check URL Connection for @systemId, and return an InputStream
435
   * that can be used to read from the systemId URL.  The parser ends
436
   * up using this via the InputSource to read the DTD.
437
   *
438
   * @param systemId a URI (in practice URL) to be checked and opened
439
   */
440
  public static InputStream checkURLConnection (String systemId)
441
                      throws SAXException
442
  {
443
    try {
444

    
445
      return (new URL(systemId).openStream());
446

    
447
    } catch (MalformedURLException e) {
448
      throw new SAXException
449
      ("DBEntityResolver.checkURLConnection(): " + e.getMessage());
450
    } catch (IOException e) {
451
      throw new SAXException
452
      ("DBEntityResolver.checkURLConnection(): " + e.getMessage());
453
    }
454
  }
455
}
(21-21/65)