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: bojilova $'
11
 *     '$Date: 2000-12-12 13:29:29 -0800 (Tue, 12 Dec 2000) $'
12
 * '$Revision: 599 $'
13
 */
14

    
15
package edu.ucsb.nceas.metacat;
16

    
17
import org.xml.sax.*;
18
import org.xml.sax.helpers.DefaultHandler;
19

    
20
import java.sql.*;
21
import java.io.File;
22
import java.io.Reader;
23
import java.io.BufferedReader;
24
import java.io.FileWriter;
25
import java.io.BufferedWriter;
26
import java.io.InputStream;
27
import java.io.IOException;
28
import java.net.URL;
29
import java.net.URLConnection;
30
import java.net.MalformedURLException;
31

    
32
/** 
33
 * A database aware Class implementing EntityResolver interface for the SAX 
34
 * parser to call when processing the XML stream and intercepting any 
35
 * external entities (including the external DTD subset and external 
36
 * parameter entities, if any) before including them.
37
 */
38
public class DBEntityResolver implements EntityResolver
39
{
40
  private Connection conn = null;
41
  private DefaultHandler handler = null;
42
  private String docname = null;
43
  private String doctype = null;
44
  private String systemid = null;
45
  private Reader dtdtext = null;
46

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

    
111
    // get System ID for doctype
112
    if (doctype != null) {
113
      // look at db XML Catalog for System ID 
114
      dbSystemID = getDTDSystemID(doctype);
115
      boolean doctypeIsInDB = true;
116
      // no System ID found in db XML Catalog
117
      if (dbSystemID == null) {
118
        doctypeIsInDB = false;
119
        // use the provided System ID
120
        if (systemId != null) {
121
          dbSystemID = systemId;
122
        }
123
      }
124

    
125
      // there are dtd text provided; try to upload on Metacat
126
      if ( dtdtext != null ) {
127
        dbSystemID = uploadDTD(dbSystemID);
128
      }
129

    
130
      // check URLConnection first
131
      InputStream istream = checkURLConnection(dbSystemID);
132

    
133
      // need to register System ID in db XML Catalog if not yet
134
      if ( !doctypeIsInDB ) {
135
        registerDTD(doctype, dbSystemID);
136
      }
137
      
138
      // return a byte-input stream for use
139
      InputSource is = new InputSource(dbSystemID); 
140
      //is.setPublicId(doctype);
141
      //is.setSystemId(dbSystemID);
142
      is.setByteStream(istream);
143
      return is;
144

    
145
    } else {
146
      // use the provided systemId for other cases
147
      InputStream istream = checkURLConnection(systemId);
148
      return null;
149
      
150
    }
151
    
152
  }
153

    
154
  /** 
155
   * Look at db XML Catalog to get System ID (if any) for @doctype.
156
   * Return null if there are no System ID found for @doctype
157
   */
158
  private String getDTDSystemID( String doctype )
159
                 throws SAXException
160
  {
161
    String systemid = null;
162
    Statement stmt;
163
    try {
164
      stmt = conn.createStatement();
165
      stmt.execute("SELECT system_id FROM xml_catalog " + 
166
                   "WHERE entry_type = 'DTD' AND public_id = '" +
167
                   doctype + "'");
168
      ResultSet rs = stmt.getResultSet();
169
      boolean tableHasRows = rs.next();
170
      if (tableHasRows) {
171
        systemid = rs.getString(1);
172
      }
173
      stmt.close();
174
    } catch (SQLException e) {
175
      throw new SAXException
176
      ("DBEntityResolver.getDTDSystemID(): " + e.getMessage());
177
    }
178

    
179
    // return the selected System ID
180
    return systemid;
181
  }
182

    
183
  /** 
184
   * Register new DTD identified by @systemId in Metacat XML Catalog 
185
   * . make a reference with @systemId for @doctype in Metacat DB
186
   */
187
  private void registerDTD ( String doctype, String systemId )
188
                 throws SAXException
189
  {
190
    // make a reference in db catalog table with @systemId for @doctype
191
    try {
192
      PreparedStatement pstmt;
193
      pstmt = conn.prepareStatement(
194
             "INSERT INTO xml_catalog " +
195
             "(catalog_id, entry_type, public_id, system_id) " +
196
             "VALUES (null, 'DTD', ?, ?)");
197
      // Bind the values to the query
198
      pstmt.setString(1, doctype);
199
      pstmt.setString(2, systemId);
200
      // Do the insertion
201
      pstmt.execute();
202
      pstmt.close();
203
    } catch (SQLException e) {
204
      throw new SAXException
205
      ("DBEntityResolver.registerDTD(): " + e.getMessage());
206
    }
207
  }
208

    
209
  /** 
210
   * Upload new DTD text identified by @systemId on Metacat file system
211
   */
212
  private String uploadDTD ( String systemId )
213
                 throws SAXException
214
  {
215
    MetaCatUtil util = new MetaCatUtil();
216
    String dtdPath = util.getOption("dtdPath");
217
    String dtdURL = util.getOption("dtdURL");
218
    
219
    // get filename from systemId
220
    String filename = systemId;
221
    int slash = Math.max(filename.lastIndexOf('/'), filename.lastIndexOf('\\'));
222
    if ( slash > -1 ) {
223
      filename = filename.substring(slash + 1);
224
    }
225

    
226
    // writing dtd text on Metacat file system as filename
227
    try {
228
      // create a buffering character-input stream
229
      // that uses a default-sized input buffer
230
      BufferedReader in = new BufferedReader(dtdtext);
231

    
232
      // open file writer to write the input into it
233
      //String dtdPath = "/opt/tomcat/webapps/bojilova/dtd/";
234
      File f = new File(dtdPath, filename);
235
      try {
236
        if ( f.exists() && !f.canWrite() ) {
237
          throw new IOException("Not writable: " + f.getCanonicalFile());
238
        }
239
      } catch (SecurityException se) {
240
        // if a security manager exists,
241
        // its checkRead method is called for f.exist()
242
        // or checkWrite method is called for f.canWrite()
243
        throw se;
244
      }
245
      // create a buffered character-output stream
246
      // that uses a default-sized output buffer
247
      FileWriter fw = new FileWriter(f);
248
      BufferedWriter out = new BufferedWriter(fw);
249

    
250
      // read the input and write into the file writer
251
	    String inputLine;
252
	    while ( (inputLine = in.readLine()) != null ) {
253
	      out.write(inputLine, 0, inputLine.length());
254
  	    out.newLine(); //instead of out.write('\r\n');
255
	    }
256

    
257
      // the input and the output streams must be closed
258
	    in.close();
259
	    out.flush();
260
	    out.close();
261
	    fw.close();
262
      
263
    } catch (MalformedURLException e) {
264
      throw new SAXException
265
      ("DBEntityResolver.uploadDTD(): " + e.getMessage());
266
    } catch (IOException e) {
267
      throw new SAXException
268
      ("DBEntityResolver.uploadDTD(): " + e.getMessage());
269
    } catch (SecurityException e) {
270
      throw new SAXException
271
      ("DBEntityResolver.uploadDTD(): " + e.getMessage());
272
    }
273
    
274
    //String dtdURL = "http://dev.nceas.ucsb.edu/bojilova/dtd/";
275
    return  dtdURL + filename;
276
  }
277

    
278
  /** 
279
   * Check URL Connection for @systemId, and return an InputStream
280
   * that can be used to read from the systemId URL.  The parser ends
281
   * up using this via the InputSource to read the DTD.
282
   *
283
   * @param systemId a URI (in practice URL) to be checked and opened
284
   */
285
  private InputStream checkURLConnection (String systemId)
286
                      throws SAXException
287
  {
288
    try {
289

    
290
      return (new URL(systemId).openStream());
291

    
292
    } catch (MalformedURLException e) {
293
      throw new SAXException
294
      ("DBEntityResolver.checkURLConnection(): " + e.getMessage());
295
    } catch (IOException e) {
296
      throw new SAXException
297
      ("DBEntityResolver.checkURLConnection(): " + e.getMessage());
298
    }    
299
  }   
300
}
301

    
302
/**
303
 * '$Log$
304
 * 'Revision 1.15  2000/08/14 20:53:33  jones
305
 * 'Added "release" keyword to all metacat source files so that the release
306
 * 'number will be evident in software distributions.
307
 * '
308
 * 'Revision 1.14  2000/06/30 00:52:04  jones
309
 * 'changed char stream to byte stream
310
 * '
311
 * 'Revision 1.13  2000/06/29 23:27:08  jones
312
 * 'Fixed bug in DBEntityResolver so that it now properly delegates to
313
 * 'the system id found inthe database.
314
 * 'Changed DBValidate to use DBEntityResolver, rather than the OASIS
315
 * 'catalog, and to return validation results in XML format.
316
 * '
317
 * 'Revision 1.12  2000/06/27 04:31:07  jones
318
 * 'Fixed bugs associated with the new UPDATE and DELETE functions of
319
 * 'DBWriter.  There were problematic interactions between some static
320
 * 'variables used in DBEntityResolver and the way in which the
321
 * 'Servlet objects are re-used across multiple client invocations.
322
 * '
323
 * 'Generally cleaned up error reporting.  Now all errors and success
324
 * 'results are reported as XML documents from MetaCatServlet.  Need
325
 * 'to make the command line tools do the same.
326
 * '
327
 * 'Revision 1.11  2000/06/26 10:35:04  jones
328
 * 'Merged in substantial changes to DBWriter and associated classes and to
329
 * 'the MetaCatServlet in order to accomodate the new UPDATE and DELETE
330
 * 'functions.  The command line tools and the parameters for the
331
 * 'servlet have changed substantially.
332
 * '
333
 * 'Revision 1.10.2.2  2000/06/25 23:38:16  jones
334
 * 'Added RCSfile keyword
335
 * '
336
 * 'Revision 1.10.2.1  2000/06/25 23:34:17  jones
337
 * 'Changed documentation formatting, added log entries at bottom of source files
338
 * ''
339
 */
(13-13/38)