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: 2003-11-17 15:17:30 -0800 (Mon, 17 Nov 2003) $'
12
 * '$Revision: 1895 $'
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
    MetaCatUtil.debugMessage("in DBEntityResolver.resolveEntity", 60);
95
    String dbSystemID;
96
    String doctype = null;
97

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

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

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

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

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

    
182
    }
183

    
184
  }
185

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

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

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

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

    
253

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

    
284
  }
285

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

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

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

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

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

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

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

    
358

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

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

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

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

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

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

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

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

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

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