Project

General

Profile

1 15 jones
/**
2 203 jones
 *  '$RCSfile$'
3
 *    Purpose: A Class that represents an XML node and its contents
4
 *  Copyright: 2000 Regents of the University of California and the
5
 *             National Center for Ecological Analysis and Synthesis
6
 *    Authors: Matt Jones
7 15 jones
 *
8 203 jones
 *   '$Author$'
9
 *     '$Date$'
10
 * '$Revision$'
11 669 jones
 *
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 2 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program; if not, write to the Free Software
24
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25 15 jones
 */
26
27 75 jones
package edu.ucsb.nceas.metacat;
28 15 jones
29 6020 leinfelder
import java.sql.PreparedStatement;
30
import java.sql.ResultSet;
31
import java.sql.SQLException;
32
import java.sql.Statement;
33
import java.util.Enumeration;
34 15 jones
import java.util.Hashtable;
35 6020 leinfelder
36 2663 sgarg
import org.apache.log4j.Logger;
37 461 bojilova
import org.xml.sax.SAXException;
38 15 jones
39 5015 daigle
import edu.ucsb.nceas.metacat.database.DBConnection;
40
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
41
import edu.ucsb.nceas.metacat.database.DatabaseService;
42 747 bojilova
43 2358 sgarg
/**
44 137 jones
 * A Class that represents an XML node and its contents and
45 31 jones
 * can write its own representation to a database connection
46
 */
47 137 jones
public class DBSAXNode extends BasicNode {
48 15 jones
49 1217 tao
  private DBConnection	connection;
50 176 jones
  private DBSAXNode	parentNode;
51 2663 sgarg
  private Logger logMetacat = Logger.getLogger(DBSAXNode.class);
52 15 jones
53 2358 sgarg
  /**
54 136 jones
   * Construct a new node instance for DOCUMENT nodes
55 133 jones
   *
56
   * @param conn the JDBC Connection to which all information is written
57
   */
58 1217 tao
  public DBSAXNode (DBConnection conn, String docid) throws SAXException {
59 747 bojilova
60 408 jones
    super();
61 1217 tao
    this.connection = conn;
62 176 jones
    this.parentNode = null;
63 457 bojilova
    writeChildNodeToDB("DOCUMENT", null, null, docid);
64 758 bojilova
    updateRootNodeID(getNodeID());
65 136 jones
  }
66
67 2358 sgarg
  /**
68 136 jones
   * Construct a new node instance for ELEMENT nodes
69
   *
70
   * @param conn the JDBC Connection to which all information is written
71
   * @param tagname the name of the node
72
   * @param parentNode the parent node for this node being created
73
   */
74 1217 tao
  public DBSAXNode (DBConnection conn, String qName, String lName,
75 2358 sgarg
                    DBSAXNode parentNode, long rootnodeid,
76
                    String docid, String doctype)
77 826 bojilova
                                               throws SAXException {
78 21 jones
79 826 bojilova
    super(lName);
80 1217 tao
    this.connection = conn;
81 747 bojilova
    this.parentNode = parentNode;
82 136 jones
    setParentID(parentNode.getNodeID());
83 457 bojilova
    setRootNodeID(rootnodeid);
84
    setDocID(docid);
85 136 jones
    setNodeIndex(parentNode.incChildNum());
86 826 bojilova
    writeChildNodeToDB("ELEMENT", qName, null, docid);
87 471 bojilova
    //No writing XML Index from here. New Thread used instead.
88
    //updateNodeIndex(docid, doctype);
89 133 jones
  }
90 2358 sgarg
91
  /**
92 1803 tao
   * Construct a new node instance for DTD nodes
93
   * This Node will write docname, publicId and systemId into db. Only
94
   * handle systemid  existed.(external dtd)
95
   *
96
   * @param conn the JDBC Connection to which all information is written
97
   * @param tagname the name of the node
98
   * @param parentNode the parent node for this node being created
99
   */
100 2358 sgarg
  public DBSAXNode (DBConnection conn, String docName, String publicId,
101
                    String systemId, DBSAXNode parentNode, long rootnodeid,
102
                    String docid) throws SAXException
103 1803 tao
  {
104
105
    super();
106
    this.connection = conn;
107
    this.parentNode = parentNode;
108
    setParentID(parentNode.getNodeID());
109
    setRootNodeID(rootnodeid);
110
    setDocID(docid);
111
    // insert dtd node only for external dtd definiation
112
    if (systemId != null)
113
    {
114
      //write docname to DB
115
      if (docName != null)
116
      {
117
        setNodeIndex(parentNode.incChildNum());
118
        writeDTDNodeToDB(DocumentImpl.DOCNAME, docName, docid);
119
      }
120
      //write publicId to DB
121
      if (publicId != null)
122
      {
123
        setNodeIndex(parentNode.incChildNum());
124
        writeDTDNodeToDB(DocumentImpl.PUBLICID, publicId, docid);
125
      }
126
      //write systemId to DB
127
      setNodeIndex(parentNode.incChildNum());
128
      writeDTDNodeToDB(DocumentImpl.SYSTEMID, systemId, docid);
129
    }
130
  }
131 5208 daigle
132 133 jones
  /** creates SQL code and inserts new node into DB connection */
133 1416 tao
  public long writeChildNodeToDB(String nodetype, String nodename,
134 2358 sgarg
                                 String data, String docid)
135 5208 daigle
                                 throws SAXException {
136
    String limitedData = null;
137
    int leftover = 0;
138
    if (data != null) {
139
    	leftover = data.length();
140
    }
141
    int offset = 0;
142
    boolean moredata = true;
143
    long endNodeId = -1;
144
145
    // This loop deals with the case where there are more characters
146
    // than can fit in a single database text field (limit is
147
    // MAXDATACHARS). If the text to be inserted exceeds MAXDATACHARS,
148
    // write a series of nodes that are MAXDATACHARS long, and then the
149
    // final node contains the remainder
150
    while (moredata) {
151 5311 daigle
        if (leftover > (DBSAXHandler.MAXDATACHARS)) {
152 5208 daigle
        	limitedData = data.substring(offset, DBSAXHandler.MAXDATACHARS);
153 5311 daigle
            leftover -= (DBSAXHandler.MAXDATACHARS - 1);
154
            offset += (DBSAXHandler.MAXDATACHARS - 1);
155 5208 daigle
        } else {
156 5311 daigle
        	if (data != null) {
157
        		limitedData = data.substring(offset, offset + leftover);
158
        	} else {
159
        		limitedData = null;
160
        	}
161
        	moredata = false;
162 5208 daigle
        }
163 5311 daigle
164
        endNodeId =  writeChildNodeToDBDataLimited(nodetype, nodename, limitedData, docid);
165 5208 daigle
    }
166
167
    return endNodeId;
168
  }
169
170
  /** creates SQL code and inserts new node into DB connection */
171
  public long writeChildNodeToDBDataLimited(String nodetype, String nodename,
172
                                 String data, String docid)
173 2358 sgarg
                                 throws SAXException
174 1416 tao
  {
175
    long nid = -1;
176 2358 sgarg
    try
177 1416 tao
    {
178 2358 sgarg
179 133 jones
      PreparedStatement pstmt;
180 6012 leinfelder
181 133 jones
      if (nodetype == "DOCUMENT") {
182 1217 tao
        pstmt = connection.prepareStatement(
183 133 jones
            "INSERT INTO xml_nodes " +
184 826 bojilova
            "(nodetype, nodename, nodeprefix, docid) " +
185
            "VALUES (?, ?, ?, ?)");
186 2358 sgarg
187 5208 daigle
        logMetacat.debug("DBSAXNode.writeChildNodeToDBDataLimited - inserting doc name: " + nodename);
188 133 jones
      } else {
189 6020 leinfelder
          pstmt = connection.prepareStatement(
190
              "INSERT INTO xml_nodes " +
191
              "(nodetype, nodename, nodeprefix, docid, " +
192
              "rootnodeid, parentnodeid, nodedata, nodeindex) " +
193
              "VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
194 133 jones
      }
195 6020 leinfelder
196
      // Increase DBConnection usage count
197
      connection.increaseUsageCount(1);
198
199 133 jones
      // Bind the values to the query
200 758 bojilova
      pstmt.setString(1, nodetype);
201 826 bojilova
      int idx;
202
      if ( nodename != null && (idx = nodename.indexOf(":")) != -1 ) {
203
        pstmt.setString(2, nodename.substring(idx+1));
204
        pstmt.setString(3, nodename.substring(0,idx));
205
      } else {
206
        pstmt.setString(2, nodename);
207
        pstmt.setString(3, null);
208
      }
209
      pstmt.setString(4, docid);
210 5208 daigle
      if (nodetype != "DOCUMENT") {
211 133 jones
        if (nodetype == "ELEMENT") {
212 826 bojilova
          pstmt.setLong(5, getRootNodeID());
213
          pstmt.setLong(6, getParentID());
214
          pstmt.setString(7, data);
215
          pstmt.setInt(8, getNodeIndex());
216 133 jones
        } else {
217 826 bojilova
          pstmt.setLong(5, getRootNodeID());
218
          pstmt.setLong(6, getNodeID());
219
          pstmt.setString(7, data);
220
          pstmt.setInt(8, incChildNum());
221 72 bojilova
        }
222 133 jones
      }
223
      // Do the insertion
224 5208 daigle
      logMetacat.debug("DBSAXNode.writeChildNodeToDBDataLimited - SQL insert: " + pstmt.toString());
225 133 jones
      pstmt.execute();
226
      pstmt.close();
227 758 bojilova
228
      // get the generated unique id afterward
229 5319 jones
      nid = DatabaseService.getInstance().getDBAdapter().getUniqueID(connection.getConnections(), "xml_nodes");
230 5208 daigle
      //should increase connection usage!!!!!!
231 2358 sgarg
232 408 jones
      if (nodetype.equals("DOCUMENT")) {
233
        // Record the root node id that was generated from the database
234
        setRootNodeID(nid);
235
      }
236
237 177 jones
      if (nodetype.equals("DOCUMENT") || nodetype.equals("ELEMENT")) {
238 176 jones
239 177 jones
        // Record the node id that was generated from the database
240
        setNodeID(nid);
241 176 jones
242 177 jones
        // Record the node type that was passed to the method
243
        setNodeType(nodetype);
244
245
      }
246
247 5208 daigle
    } catch (SQLException sqle) {
248
      logMetacat.error("DBSAXNode.writeChildNodeToDBDataLimited - SQL error inserting node: " +
249
    		  "(" + nodetype + ", " + nodename + ", " + data + ") : " + sqle.getMessage());
250
      sqle.printStackTrace(System.err);
251
      throw new SAXException(sqle.getMessage());
252 110 bojilova
    }
253 1416 tao
    return nid;
254 133 jones
  }
255 110 bojilova
256 2358 sgarg
  /**
257 758 bojilova
   * update rootnodeid=nodeid for 'DOCUMENT' type of nodes only
258
   */
259
  public void updateRootNodeID(long nodeid) throws SAXException {
260
      try {
261
        PreparedStatement pstmt;
262 1217 tao
        pstmt = connection.prepareStatement(
263 758 bojilova
              "UPDATE xml_nodes set rootnodeid = ? " +
264
              "WHERE nodeid = ?");
265 1217 tao
        // Increase DBConnection usage count
266
        connection.increaseUsageCount(1);
267 758 bojilova
268
        // Bind the values to the query
269
        pstmt.setLong(1, nodeid);
270
        pstmt.setLong(2, nodeid);
271
        // Do the update
272
        pstmt.execute();
273
        pstmt.close();
274
      } catch (SQLException e) {
275 2358 sgarg
        System.out.println("Error in DBSaxNode.updateRootNodeID: " +
276 758 bojilova
                           e.getMessage());
277
        throw new SAXException(e.getMessage());
278
      }
279
  }
280
281 2358 sgarg
  /**
282
   * creates SQL code to put nodename for the document node
283
   * into DB connection
284 133 jones
   */
285 461 bojilova
  public void writeNodename(String nodename) throws SAXException {
286 133 jones
      try {
287
        PreparedStatement pstmt;
288 1217 tao
        pstmt = connection.prepareStatement(
289 133 jones
              "UPDATE xml_nodes set nodename = ? " +
290
              "WHERE nodeid = ?");
291 1217 tao
        // Increase DBConnection usage count
292
        connection.increaseUsageCount(1);
293 16 jones
294 133 jones
        // Bind the values to the query
295
        pstmt.setString(1, nodename);
296 134 jones
        pstmt.setLong(2, getNodeID());
297 133 jones
        // Do the insertion
298
        pstmt.execute();
299
        pstmt.close();
300
      } catch (SQLException e) {
301 2358 sgarg
        System.out.println("Error in DBSaxNode.writeNodeName: " +
302 675 berkley
                           e.getMessage());
303 461 bojilova
        throw new SAXException(e.getMessage());
304 133 jones
      }
305
  }
306 16 jones
307 1803 tao
 /** creates SQL code and inserts new node into DB connection */
308 2358 sgarg
  public long writeDTDNodeToDB(String nodename, String data, String docid)
309
                                 throws SAXException
310 1803 tao
  {
311
    long nid = -1;
312 2358 sgarg
    try
313 1803 tao
    {
314 2358 sgarg
315 1803 tao
      PreparedStatement pstmt;
316 5208 daigle
      logMetacat.info("DBSAXNode.writeDTDNodeToDB - Insert dtd into db: "+nodename +" "+data);
317 6020 leinfelder
318
      pstmt = connection.prepareStatement(
319
              "INSERT INTO xml_nodes " +
320
              "(nodetype, nodename, docid, " +
321
              "rootnodeid, parentnodeid, nodedata, nodeindex) " +
322
              "VALUES (?, ?, ?, ?, ?, ?, ?)");
323 2358 sgarg
324 1803 tao
       // Increase DBConnection usage count
325
      connection.increaseUsageCount(1);
326 2358 sgarg
327 1803 tao
      // Bind the values to the query
328
      pstmt.setString(1, DocumentImpl.DTD);
329
      pstmt.setString(2, nodename);
330
      pstmt.setString(3, docid);
331
      pstmt.setLong(4, getRootNodeID());
332
      pstmt.setLong(5, getParentID());
333
      pstmt.setString(6, data);
334
      pstmt.setInt(7, getNodeIndex());
335 2358 sgarg
336 1803 tao
      // Do the insertion
337
      pstmt.execute();
338
      pstmt.close();
339
340
      // get the generated unique id afterward
341 5319 jones
      nid = DatabaseService.getInstance().getDBAdapter().getUniqueID(connection.getConnections(), "xml_nodes");
342 2358 sgarg
343 1803 tao
    } catch (SQLException e) {
344
      System.out.println("Error in DBSaxNode.writeDTDNodeToDB");
345
      System.err.println("Error inserting node: (" + DocumentImpl.DTD + ", " +
346 2358 sgarg
                                                     nodename + ", " +
347 1803 tao
                                                     data + ")" );
348
      System.err.println(e.getMessage());
349
      e.printStackTrace(System.err);
350
      throw new SAXException(e.getMessage());
351
    }
352
    return nid;
353
  }
354 2358 sgarg
355
356 174 bojilova
  /** get next node id from DB connection */
357 461 bojilova
  private long generateNodeID() throws SAXException {
358 174 bojilova
      long nid=0;
359 133 jones
      Statement stmt;
360 1217 tao
      DBConnection dbConn = null;
361
      int serialNumber = -1;
362 133 jones
      try {
363 1217 tao
        // Get DBConnection
364
        dbConn=DBConnectionPool.getDBConnection("DBSAXNode.generateNodeID");
365
        serialNumber=dbConn.getCheckOutSerialNumber();
366
        stmt = dbConn.createStatement();
367 174 bojilova
        stmt.execute("SELECT xml_nodes_id_seq.nextval FROM dual");
368 133 jones
        ResultSet rs = stmt.getResultSet();
369
        boolean tableHasRows = rs.next();
370
        if (tableHasRows) {
371 174 bojilova
          nid = rs.getLong(1);
372 19 jones
        }
373 133 jones
        stmt.close();
374
      } catch (SQLException e) {
375 2358 sgarg
        System.out.println("Error in DBSaxNode.generateNodeID: " +
376 675 berkley
                            e.getMessage());
377 461 bojilova
        throw new SAXException(e.getMessage());
378 15 jones
      }
379 1217 tao
      finally
380
      {
381
        // Return DBconnection
382
        DBConnectionPool.returnDBConnection(dbConn, serialNumber);
383
      }//finally
384 15 jones
385 174 bojilova
      return nid;
386 133 jones
  }
387 18 jones
388 137 jones
  /** Add a new attribute to this node, or set its value */
389 1416 tao
  public long setAttribute(String attName, String attValue, String docid)
390 2358 sgarg
              throws SAXException
391 1416 tao
  {
392
    long nodeId = -1;
393 2358 sgarg
    if (attName != null)
394 1416 tao
    {
395 133 jones
      // Enter the attribute in the hash table
396
      super.setAttribute(attName, attValue);
397 18 jones
398 133 jones
      // And enter the attribute in the database
399 1416 tao
      nodeId = writeChildNodeToDB("ATTRIBUTE", attName, attValue, docid);
400 2358 sgarg
    }
401
    else
402 1416 tao
    {
403 133 jones
      System.err.println("Attribute name must not be null!");
404 461 bojilova
      throw new SAXException("Attribute name must not be null!");
405 18 jones
    }
406 1416 tao
    return nodeId;
407 133 jones
  }
408 176 jones
409 821 bojilova
  /** Add a namespace to this node */
410 1416 tao
  public long setNamespace(String prefix, String uri, String docid)
411 2358 sgarg
              throws SAXException
412 1416 tao
  {
413
    long nodeId = -1;
414 2358 sgarg
    if (prefix != null)
415 1416 tao
    {
416 821 bojilova
      // Enter the namespace in a hash table
417
      super.setNamespace(prefix, uri);
418
      // And enter the namespace in the database
419 1416 tao
      nodeId = writeChildNodeToDB("NAMESPACE", prefix, uri, docid);
420 2358 sgarg
    }
421
    else
422 1416 tao
    {
423 821 bojilova
      System.err.println("Namespace prefix must not be null!");
424
      throw new SAXException("Namespace prefix must not be null!");
425
    }
426 1416 tao
    return nodeId;
427 821 bojilova
  }
428
429 176 jones
430 2358 sgarg
431
  /**
432 471 bojilova
   * USED FROM SEPARATE THREAD RUNNED from DBSAXHandler on endDocument()
433
   * Update the node index (xml_index) for this node by generating
434
   * test strings that represent all of the relative and absolute
435
   * paths through the XML tree from document root to this node
436
   */
437 2358 sgarg
  public void updateNodeIndex(DBConnection conn, String docid, String doctype)
438 471 bojilova
               throws SAXException
439
  {
440
    Hashtable pathlist = new Hashtable();
441
    boolean atStartingNode = true;
442
    boolean atRootDocumentNode = false;
443
    DBSAXNode nodePointer = this;
444
    StringBuffer currentPath = new StringBuffer();
445
    int counter = 0;
446
447
    // Create a Hashtable of all of the paths to reach this node
448
    // including absolute paths and relative paths
449
    while (!atRootDocumentNode) {
450
      if (atStartingNode) {
451
        currentPath.insert(0, nodePointer.getTagName());
452
        pathlist.put(currentPath.toString(), new Long(getNodeID()));
453
        counter++;
454
        atStartingNode = false;
455
      } else {
456
        currentPath.insert(0, "/");
457
        currentPath.insert(0, nodePointer.getTagName());
458
        pathlist.put(currentPath.toString(), new Long(getNodeID()));
459
        counter++;
460
      }
461
462
      // advance to the next parent node
463
      nodePointer = nodePointer.getParentNode();
464
465
      // If we're at the DOCUMENT node (root of DOM tree), add
466
      // the root "/" to make the absolute path
467
      if (nodePointer.getNodeType().equals("DOCUMENT")) {
468
        currentPath.insert(0, "/");
469
        pathlist.put(currentPath.toString(), new Long(getNodeID()));
470
        counter++;
471
        atRootDocumentNode = true;
472 2358 sgarg
      }
473 471 bojilova
    }
474
475
    try {
476
      // Create an insert statement to reuse for all of the path insertions
477
      PreparedStatement pstmt = conn.prepareStatement(
478 2358 sgarg
              "INSERT INTO xml_index (nodeid, path, docid, doctype, " +
479
               "parentnodeid) " +
480 471 bojilova
              "VALUES (?, ?, ?, ?, ?)");
481 1217 tao
      // Increase usage count
482
      conn.increaseUsageCount(1);
483 2358 sgarg
484 471 bojilova
      pstmt.setString(3, docid);
485
      pstmt.setString(4, doctype);
486
      pstmt.setLong(5, getParentID());
487 2358 sgarg
488 471 bojilova
      // Step through the hashtable and insert each of the path values
489 176 jones
      Enumeration en = pathlist.keys();
490
      while (en.hasMoreElements()) {
491
        String path = (String)en.nextElement();
492
        Long nodeid = (Long)pathlist.get(path);
493
        pstmt.setLong(1, nodeid.longValue());
494
        pstmt.setString(2, path);
495 2358 sgarg
496 457 bojilova
        pstmt.executeUpdate();
497 176 jones
      }
498
      // Close the database statement
499
      pstmt.close();
500
    } catch (SQLException sqe) {
501 675 berkley
      System.err.println("SQL Exception while inserting path to index in " +
502 899 berkley
                         "DBSAXNode.updateNodeIndex for document " + docid);
503 176 jones
      System.err.println(sqe.getMessage());
504 461 bojilova
      throw new SAXException(sqe.getMessage());
505 176 jones
    }
506
  }
507 2358 sgarg
508 176 jones
  /** get the parent of this node */
509
  public DBSAXNode getParentNode() {
510
    return parentNode;
511
  }
512 15 jones
}