Project

General

Profile

1 72 bojilova
/**
2 203 jones
 *  '$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 243 jones
 *    Authors: Jivka Bojilova, Matt Jones
8 349 jones
 *    Release: @release@
9 72 bojilova
 *
10 203 jones
 *   '$Author$'
11
 *     '$Date$'
12
 * '$Revision$'
13 669 jones
 *
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 72 bojilova
 */
28
29 75 jones
package edu.ucsb.nceas.metacat;
30 72 bojilova
31
import org.xml.sax.*;
32 598 bojilova
import org.xml.sax.helpers.DefaultHandler;
33 72 bojilova
34
import java.sql.*;
35 243 jones
import java.io.File;
36 598 bojilova
import java.io.Reader;
37
import java.io.BufferedReader;
38
import java.io.FileWriter;
39
import java.io.BufferedWriter;
40 243 jones
import java.io.InputStream;
41
import java.io.IOException;
42 72 bojilova
import java.net.URL;
43 151 bojilova
import java.net.URLConnection;
44 72 bojilova
import java.net.MalformedURLException;
45
46
/**
47 122 jones
 * A database aware Class implementing EntityResolver interface for the SAX
48
 * parser to call when processing the XML stream and intercepting any
49
 * external entities (including the external DTD subset and external
50
 * parameter entities, if any) before including them.
51 72 bojilova
 */
52
public class DBEntityResolver implements EntityResolver
53
{
54 598 bojilova
  private Connection conn = null;
55
  private DefaultHandler handler = null;
56
  private String docname = null;
57
  private String doctype = null;
58
  private String systemid = null;
59
  private Reader dtdtext = null;
60 72 bojilova
61 598 bojilova
  /**
62
   * Construct an instance of the DBEntityResolver class
63
   *
64
   * @param conn the JDBC connection to which information is written
65 599 bojilova
   */
66
  public DBEntityResolver(Connection conn)
67
  {
68
    this.conn = conn;
69
  }
70
  /**
71
   * Construct an instance of the DBEntityResolver class
72
   *
73
   * @param conn the JDBC connection to which information is written
74 598 bojilova
   * @param handler the SAX handler to determine parsing context
75
   * @param dtd Reader of new dtd to be uploaded on server's file system
76
   */
77
  public DBEntityResolver(Connection conn, DefaultHandler handler, Reader dtd)
78
  {
79
    this.conn = conn;
80
    this.handler = handler;
81
    this.dtdtext = dtd;
82
  }
83 72 bojilova
84 598 bojilova
  /**
85
   * The Parser call this method before opening any external entity
86
   * except the top-level document entity (including the external DTD subset,
87
   * external entities referenced within the DTD, and external entities
88
   * referenced within the document element)
89
   */
90
  public InputSource resolveEntity (String publicId, String systemId)
91
                     throws SAXException
92
  {
93
    String dbSystemID;
94
    String doctype = null;
95 72 bojilova
96 598 bojilova
    // Won't have a handler under all cases
97
    if ( handler != null ) {
98
      if ( handler instanceof DBSAXHandler ) {
99
        DBSAXHandler dhandler = null;
100
        dhandler = (DBSAXHandler)handler;
101
        if ( dhandler.processingDTD() ) {
102
          // public ID is doctype
103
          if (publicId != null) {
104
            doctype = publicId;
105
          // assume public ID (doctype) is docname
106
          } else if (systemId != null) {
107
            doctype = dhandler.getDocname();
108 72 bojilova
          }
109
        }
110 598 bojilova
      } else if ( handler instanceof AccessControlList ) {
111
        AccessControlList ahandler = null;
112
        ahandler = (AccessControlList)handler;
113
        //if ( ahandler.processingDTD() ) {
114
          // public ID is doctype
115
          if (publicId != null) {
116
            doctype = publicId;
117
          // assume public ID (doctype) is docname
118
          } else if (systemId != null) {
119
            doctype = ahandler.getDocname();
120
          }
121
        //}
122
      }
123
    }
124 72 bojilova
125 598 bojilova
    // get System ID for doctype
126
    if (doctype != null) {
127
      // look at db XML Catalog for System ID
128
      dbSystemID = getDTDSystemID(doctype);
129
      boolean doctypeIsInDB = true;
130
      // no System ID found in db XML Catalog
131
      if (dbSystemID == null) {
132
        doctypeIsInDB = false;
133
        // use the provided System ID
134
        if (systemId != null) {
135
          dbSystemID = systemId;
136
        }
137
      }
138 72 bojilova
139 598 bojilova
      // there are dtd text provided; try to upload on Metacat
140
      if ( dtdtext != null ) {
141
        dbSystemID = uploadDTD(dbSystemID);
142
      }
143
144
      // check URLConnection first
145
      InputStream istream = checkURLConnection(dbSystemID);
146
147
      // need to register System ID in db XML Catalog if not yet
148
      if ( !doctypeIsInDB ) {
149
        registerDTD(doctype, dbSystemID);
150
      }
151
152
      // return a byte-input stream for use
153
      InputSource is = new InputSource(dbSystemID);
154
      //is.setPublicId(doctype);
155
      //is.setSystemId(dbSystemID);
156
      is.setByteStream(istream);
157
      return is;
158
159
    } else {
160
      // use the provided systemId for other cases
161
      InputStream istream = checkURLConnection(systemId);
162
      return null;
163
164
    }
165
166
  }
167
168
  /**
169
   * Look at db XML Catalog to get System ID (if any) for @doctype.
170
   * Return null if there are no System ID found for @doctype
171
   */
172
  private String getDTDSystemID( String doctype )
173
                 throws SAXException
174
  {
175
    String systemid = null;
176
    Statement stmt;
177
    try {
178
      stmt = conn.createStatement();
179
      stmt.execute("SELECT system_id FROM xml_catalog " +
180
                   "WHERE entry_type = 'DTD' AND public_id = '" +
181
                   doctype + "'");
182
      ResultSet rs = stmt.getResultSet();
183
      boolean tableHasRows = rs.next();
184
      if (tableHasRows) {
185
        systemid = rs.getString(1);
186
      }
187
      stmt.close();
188
    } catch (SQLException e) {
189
      throw new SAXException
190
      ("DBEntityResolver.getDTDSystemID(): " + e.getMessage());
191
    }
192
193
    // return the selected System ID
194
    return systemid;
195
  }
196
197
  /**
198
   * Register new DTD identified by @systemId in Metacat XML Catalog
199
   * . make a reference with @systemId for @doctype in Metacat DB
200
   */
201
  private void registerDTD ( String doctype, String systemId )
202
                 throws SAXException
203
  {
204
    // make a reference in db catalog table with @systemId for @doctype
205
    try {
206
      PreparedStatement pstmt;
207
      pstmt = conn.prepareStatement(
208 243 jones
             "INSERT INTO xml_catalog " +
209 598 bojilova
             "(catalog_id, entry_type, public_id, system_id) " +
210
             "VALUES (null, 'DTD', ?, ?)");
211
      // Bind the values to the query
212
      pstmt.setString(1, doctype);
213
      pstmt.setString(2, systemId);
214
      // Do the insertion
215
      pstmt.execute();
216
      pstmt.close();
217
    } catch (SQLException e) {
218
      throw new SAXException
219
      ("DBEntityResolver.registerDTD(): " + e.getMessage());
220
    }
221
  }
222 243 jones
223 598 bojilova
  /**
224
   * Upload new DTD text identified by @systemId on Metacat file system
225
   */
226
  private String uploadDTD ( String systemId )
227
                 throws SAXException
228
  {
229
    MetaCatUtil util = new MetaCatUtil();
230
    String dtdPath = util.getOption("dtdPath");
231
    String dtdURL = util.getOption("dtdURL");
232
233
    // get filename from systemId
234
    String filename = systemId;
235
    int slash = Math.max(filename.lastIndexOf('/'), filename.lastIndexOf('\\'));
236
    if ( slash > -1 ) {
237
      filename = filename.substring(slash + 1);
238
    }
239 245 jones
240 598 bojilova
    // writing dtd text on Metacat file system as filename
241
    try {
242
      // create a buffering character-input stream
243
      // that uses a default-sized input buffer
244
      BufferedReader in = new BufferedReader(dtdtext);
245 245 jones
246 598 bojilova
      // open file writer to write the input into it
247
      //String dtdPath = "/opt/tomcat/webapps/bojilova/dtd/";
248
      File f = new File(dtdPath, filename);
249
      try {
250
        if ( f.exists() && !f.canWrite() ) {
251
          throw new IOException("Not writable: " + f.getCanonicalFile());
252
        }
253
      } catch (SecurityException se) {
254
        // if a security manager exists,
255
        // its checkRead method is called for f.exist()
256
        // or checkWrite method is called for f.canWrite()
257
        throw se;
258
      }
259
      // create a buffered character-output stream
260
      // that uses a default-sized output buffer
261
      FileWriter fw = new FileWriter(f);
262
      BufferedWriter out = new BufferedWriter(fw);
263
264
      // read the input and write into the file writer
265
	    String inputLine;
266
	    while ( (inputLine = in.readLine()) != null ) {
267
	      out.write(inputLine, 0, inputLine.length());
268
  	    out.newLine(); //instead of out.write('\r\n');
269
	    }
270
271
      // the input and the output streams must be closed
272
	    in.close();
273
	    out.flush();
274
	    out.close();
275
	    fw.close();
276
277
    } catch (MalformedURLException e) {
278
      throw new SAXException
279
      ("DBEntityResolver.uploadDTD(): " + e.getMessage());
280
    } catch (IOException e) {
281
      throw new SAXException
282
      ("DBEntityResolver.uploadDTD(): " + e.getMessage());
283
    } catch (SecurityException e) {
284
      throw new SAXException
285
      ("DBEntityResolver.uploadDTD(): " + e.getMessage());
286
    }
287
288
    //String dtdURL = "http://dev.nceas.ucsb.edu/bojilova/dtd/";
289
    return  dtdURL + filename;
290
  }
291
292
  /**
293
   * Check URL Connection for @systemId, and return an InputStream
294
   * that can be used to read from the systemId URL.  The parser ends
295
   * up using this via the InputSource to read the DTD.
296
   *
297
   * @param systemId a URI (in practice URL) to be checked and opened
298
   */
299
  private InputStream checkURLConnection (String systemId)
300
                      throws SAXException
301
  {
302
    try {
303
304
      return (new URL(systemId).openStream());
305
306
    } catch (MalformedURLException e) {
307
      throw new SAXException
308
      ("DBEntityResolver.checkURLConnection(): " + e.getMessage());
309
    } catch (IOException e) {
310
      throw new SAXException
311
      ("DBEntityResolver.checkURLConnection(): " + e.getMessage());
312
    }
313
  }
314 72 bojilova
}