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
 *
9
 *   '$Author: jones $'
10
 *     '$Date: 2006-11-10 10:25:38 -0800 (Fri, 10 Nov 2006) $'
11
 * '$Revision: 3077 $'
12
 *
13
 * This program is free software; you can redistribute it and/or modify
14
 * it under the terms of the GNU General Public License as published by
15
 * the Free Software Foundation; either version 2 of the License, or
16
 * (at your option) any later version.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 * GNU General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU General Public License
24
 * along with this program; if not, write to the Free Software
25
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
 */
27

    
28
package edu.ucsb.nceas.metacat;
29

    
30
import org.apache.log4j.Logger;
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
  private static Logger logMetacat = Logger.getLogger(DBEntityResolver.class);
62
  
63
  /**
64
   * Construct an instance of the DBEntityResolver class
65
   *
66
   * @param conn the JDBC connection to which information is written
67
   */
68
  public DBEntityResolver(DBConnection conn)
69
  {
70
    this.connection= conn;
71
  }
72
  /**
73
   * Construct an instance of the DBEntityResolver class
74
   *
75
   * @param conn the JDBC connection to which information is written
76
   * @param handler the SAX handler to determine parsing context
77
   * @param dtd Reader of new dtd to be uploaded on server's file system
78
   */
79
  public DBEntityResolver(DBConnection conn, DefaultHandler handler, Reader dtd)
80
  {
81
    this.connection = conn;
82
    this.handler = handler;
83
    this.dtdtext = dtd;
84
  }
85

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

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

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

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

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

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

    
183
    }
184

    
185
  }
186

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

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

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

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

    
254

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

    
285
  }
286

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

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

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

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

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

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

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

    
359

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

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

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

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

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

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

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

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

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

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