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: daigle $'
10
 *     '$Date: 2008-07-15 09:58:15 -0700 (Tue, 15 Jul 2008) $'
11
 * '$Revision: 4123 $'
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 edu.ucsb.nceas.metacat.util.SystemUtil;
35
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
36

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

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

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

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

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

    
156
      // open URLConnection to check first
157
      InputStream istream = checkURLConnection(dbSystemID);
158

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

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

    
186
    }
187

    
188
  }
189

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

    
206
      stmt = conn.createStatement();
207
      stmt.execute("SELECT system_id FROM xml_catalog " +
208
                   "WHERE entry_type = 'DTD' AND public_id = '" +
209
                   doctype + "'");
210
      ResultSet rs = stmt.getResultSet();
211
      boolean tableHasRows = rs.next();
212
      if (tableHasRows) {
213
        systemid = rs.getString(1);
214
        // system id may not have server url on front.  Add it if not.
215
        if (!systemid.startsWith("http://")) {
216
        	systemid = SystemUtil.getContextURL() + systemid;
217
        }
218
      }
219
      stmt.close();
220
    } catch (SQLException e) {
221
      throw new SAXException
222
      ("DBEntityResolver.getDTDSystemID(): " + e.getMessage());
223
    } catch (PropertyNotFoundException pnfe) {
224
        throw new SAXException
225
        ("DBEntityResolver.getDTDSystemID(): " + pnfe.getMessage());
226
      }
227
    finally
228
    {
229
      try
230
      {
231
        stmt.close();
232
      }//try
233
      catch (SQLException sqlE)
234
      {
235
        logMetacat.error("Error in DBEntityReolver.getDTDSystemId: "
236
                                  +sqlE.getMessage());
237
      }//catch
238
      finally
239
      {
240
        DBConnectionPool.returnDBConnection(conn, serialNumber);
241
      }//finally
242
    }//finally
243

    
244
    // return the selected System ID
245
    return systemid;
246
  }
247

    
248
  /**
249
   * Register new DTD identified by @systemId in Metacat XML Catalog
250
   * . make a reference with @systemId for @doctype in Metacat DB
251
   */
252
  private void registerDTD ( String doctype, String systemId )
253
                 throws SAXException
254
  {
255
    //DBConnection conn = null;
256
    //int serialNumber = -1;
257
    PreparedStatement pstmt = null;
258
    // make a reference in db catalog table with @systemId for @doctype
259
    try {
260
      //check out DBConnection
261
      //conn=DBConnectionPool.getDBConnection("DBEntityResolver.registerDTD");
262
      //serialNumber=conn.getCheckOutSerialNumber();
263

    
264

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

    
295
  }
296

    
297
  /**
298
	 * Upload new DTD text identified by
299
	 * 
300
	 * @systemId to Metacat file system
301
	 */
302
	private String uploadDTD(String systemId) throws SAXException {
303
		String dtdPath = null;
304
		String dtdURL = null;
305
		try {
306
			dtdPath = SystemUtil.getContextDir() + "/dtd/";
307
			dtdURL = SystemUtil.getContextURL() + "/dtd/";
308
		} catch (PropertyNotFoundException pnfe) {
309
			throw new SAXException("DBEntityResolver.uploadDTD: " + pnfe.getMessage());
310
		}
311

    
312
		// get filename from systemId
313
		String filename = systemId;
314
		int slash = Math.max(filename.lastIndexOf('/'), filename.lastIndexOf('\\'));
315
		if (slash > -1) {
316
			filename = filename.substring(slash + 1);
317
		}
318

    
319
		// writing dtd text on Metacat file system as filename
320
		try {
321
			// create a buffering character-input stream
322
			// that uses a default-sized input buffer
323
			BufferedReader in = new BufferedReader(dtdtext);
324

    
325
			// open file writer to write the input into it
326
			// String dtdPath = "/opt/tomcat/webapps/bojilova/dtd/";
327
			File f = new File(dtdPath, filename);
328
			synchronized (f) {
329
				try {
330
					if (f.exists()) {
331
						throw new IOException("File already exist: "
332
								+ f.getCanonicalFile());
333
						// if ( f.exists() && !f.canWrite() ) {
334
						// throw new IOException("Not writable: " +
335
						// f.getCanonicalFile());
336
					}
337
				} catch (SecurityException se) {
338
					// if a security manager exists,
339
					// its checkRead method is called for f.exist()
340
					// or checkWrite method is called for f.canWrite()
341
					throw se;
342
				}
343
				// create a buffered character-output stream
344
				// that uses a default-sized output buffer
345
				FileWriter fw = new FileWriter(f);
346
				BufferedWriter out = new BufferedWriter(fw);
347

    
348
				// read the input and write into the file writer
349
				String inputLine;
350
				while ((inputLine = in.readLine()) != null) {
351
					out.write(inputLine, 0, inputLine.length());
352
					out.newLine(); // instead of out.write('\r\n');
353
				}
354

    
355
				// the input and the output streams must be closed
356
				in.close();
357
				out.flush();
358
				out.close();
359
				fw.close();
360
			} // end of synchronized
361
		} catch (MalformedURLException e) {
362
			throw new SAXException("DBEntityResolver.uploadDTD(): " + e.getMessage());
363
		} catch (IOException e) {
364
			throw new SAXException("DBEntityResolver.uploadDTD(): " + e.getMessage());
365
		} catch (SecurityException e) {
366
			throw new SAXException("DBEntityResolver.uploadDTD(): " + e.getMessage());
367
		}
368

    
369
		// String dtdURL = "http://dev.nceas.ucsb.edu/bojilova/dtd/";
370
		return dtdURL + filename;
371
	}
372

    
373

    
374
  /**
375
	 * Upload new DTD located at outside URL to Metacat file system
376
	 */
377
	private String uploadDTDFromURL(InputStream istream, String systemId)
378
			throws SAXException {
379
		String dtdPath = null;
380
		String dtdURL = null;
381
		try {
382
			dtdPath = SystemUtil.getContextDir() + "/dtd/";
383
			dtdURL = SystemUtil.getContextURL() + "/dtd/";
384
		} catch (PropertyNotFoundException pnfe) {
385
			throw new SAXException("DBEntityResolver.uploadDTDFromURL: "
386
					+ pnfe.getMessage());
387
		}
388

    
389
		// get filename from systemId
390
		String filename = systemId;
391
		int slash = Math.max(filename.lastIndexOf('/'), filename.lastIndexOf('\\'));
392
		if (slash > -1) {
393
			filename = filename.substring(slash + 1);
394
		}
395

    
396
		// writing dtd text on Metacat file system as filename
397
		try {
398
			// create a buffering character-input stream
399
			// that uses a default-sized input buffer
400
			BufferedInputStream in = new BufferedInputStream(istream);
401

    
402
			// open file writer to write the input into it
403
			//String dtdPath = "/opt/tomcat/webapps/bojilova/dtd/";
404
			File f = new File(dtdPath, filename);
405
			synchronized (f) {
406
				try {
407
					if (f.exists()) {
408
						throw new IOException("File already exist: "
409
								+ f.getCanonicalFile());
410
						//if ( f.exists() && !f.canWrite() ) {
411
						//  throw new IOException("Not writable: " + f.getCanonicalFile());
412
					}
413
				} catch (SecurityException se) {
414
					// if a security manager exists,
415
					// its checkRead method is called for f.exist()
416
					// or checkWrite method is called for f.canWrite()
417
					throw se;
418
				}
419
				// create a buffered character-output stream
420
				// that uses a default-sized output buffer
421
				FileWriter fw = new FileWriter(f);
422
				BufferedWriter out = new BufferedWriter(fw);
423

    
424
				// read the input and write into the file writer
425
				int inputByte;
426
				while ((inputByte = in.read()) != -1) {
427
					out.write(inputByte);
428
					//out.newLine(); //instead of out.write('\r\n');
429
				}
430

    
431
				// the input and the output streams must be closed
432
				in.close();
433
				out.flush();
434
				out.close();
435
				fw.close();
436
			} // end of synchronized
437
		} catch (MalformedURLException e) {
438
			throw new SAXException("DBEntityResolver.uploadDTDFromURL(): "
439
					+ e.getMessage());
440
		} catch (IOException e) {
441
			throw new SAXException("DBEntityResolver.uploadDTDFromURL(): "
442
					+ e.getMessage());
443
		} catch (SecurityException e) {
444
			throw new SAXException("DBEntityResolver.uploadDTDFromURL(): "
445
					+ e.getMessage());
446
		}
447

    
448
		//String dtdURL = "http://dev.nceas.ucsb.edu/bojilova/dtd/";
449
		return dtdURL + filename;
450
	}
451

    
452
	/**
453
	 * Check URL Connection for @systemId, and return an InputStream
454
	 * that can be used to read from the systemId URL.  The parser ends
455
	 * up using this via the InputSource to read the DTD.
456
	 *
457
	 * @param systemId a URI (in practice URL) to be checked and opened
458
	 */
459
	public static InputStream checkURLConnection(String systemId) throws SAXException {
460
		try {
461
			return (new URL(systemId).openStream());
462

    
463
		} catch (MalformedURLException e) {
464
			throw new SAXException("DBEntityResolver.checkURLConnection(): "
465
					+ e.getMessage());
466
		} catch (IOException e) {
467
			throw new SAXException("DBEntityResolver.checkURLConnection(): "
468
					+ e.getMessage());
469
		}
470
	}
471
}
(19-19/67)