Project

General

Profile

1 17 jones
/**
2 203 jones
 *  '$RCSfile$'
3
 *    Purpose: A Class that handles the SAX XML events as they
4
 *             are generated from XML documents
5
 *  Copyright: 2000 Regents of the University of California and the
6
 *             National Center for Ecological Analysis and Synthesis
7
 *    Authors: Matt Jones, Jivka Bojilova
8 349 jones
 *    Release: @release@
9 17 jones
 *
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 17 jones
 */
28
29 75 jones
package edu.ucsb.nceas.metacat;
30 51 jones
31 17 jones
import java.sql.*;
32 638 bojilova
import java.io.StringReader;
33 17 jones
import java.util.Stack;
34 471 bojilova
import java.util.Vector;
35
import java.util.Enumeration;
36 18 jones
import java.util.EmptyStackException;
37 17 jones
38 185 jones
import org.xml.sax.Attributes;
39
import org.xml.sax.SAXException;
40 204 jones
import org.xml.sax.SAXParseException;
41 186 jones
import org.xml.sax.ext.DeclHandler;
42 185 jones
import org.xml.sax.ext.LexicalHandler;
43
import org.xml.sax.helpers.DefaultHandler;
44 17 jones
45 31 jones
/**
46
 * A database aware Class implementing callback bethods for the SAX parser to
47
 * call when processing the XML stream and generating events
48
 */
49 186 jones
public class DBSAXHandler extends DefaultHandler
50 471 bojilova
                          implements LexicalHandler, DeclHandler, Runnable {
51 17 jones
52 204 jones
   private boolean	atFirstElement;
53 243 jones
   private boolean	processingDTD;
54 204 jones
   private String 	docname = null;
55 135 jones
   private String 	doctype;
56
   private String 	systemid;
57 17 jones
   private boolean 	stackCreated = false;
58 471 bojilova
   private Stack 	  nodeStack;
59
   private Vector   nodeIndex;
60
   private Connection	  conn = null;
61 396 jones
   private DocumentImpl currentDocument;
62 185 jones
   private DBSAXNode    rootNode;
63 471 bojilova
   private String   action = null;
64
   private String   docid = null;
65
   private String   user = null;
66 645 bojilova
   private String   group = null;
67 680 bojilova
   private String   pub = null;
68 471 bojilova
   private Thread   xmlIndex;
69
   private boolean endDocument = false;
70 549 berkley
   private int serverCode = 1;
71 17 jones
72 220 jones
   private static final int MAXDATACHARS = 4000;
73 691 bojilova
// DOCTITLE attr cleared from the db
74
//   private static final int MAXTITLELEN = 1000;
75 220 jones
76 31 jones
   /** Construct an instance of the handler class
77
    *
78
    * @param conn the JDBC connection to which information is written
79
    */
80 122 jones
   public DBSAXHandler(Connection conn) {
81 185 jones
     this.conn = conn;
82 204 jones
     this.atFirstElement = true;
83 243 jones
     this.processingDTD = false;
84 17 jones
85 185 jones
     // Create the stack for keeping track of node context
86
     // if it doesn't already exist
87
     if (!stackCreated) {
88
       nodeStack = new Stack();
89 471 bojilova
       nodeIndex = new Vector();
90 185 jones
       stackCreated = true;
91
     }
92 17 jones
   }
93 549 berkley
94 203 jones
   /** Construct an instance of the handler class
95
    *
96
    * @param conn the JDBC connection to which information is written
97 425 bojilova
    * @param action - "INSERT" or "UPDATE"
98
    * @param docid to be inserted or updated into JDBC connection
99 680 bojilova
    * @param user the user connected to MetaCat servlet and owns the document
100
    * @param group the group to which user belongs
101
    * @param pub flag for public "read" access on document
102
    * @param serverCode the serverid from xml_replication on which this document
103
    *        resides.
104
    *
105 203 jones
    */
106 680 bojilova
   public DBSAXHandler(Connection conn, String action, String docid,
107
                       String user, String group, String pub, int serverCode)
108 425 bojilova
   {
109 203 jones
     this(conn);
110
     this.action = action;
111
     this.docid = docid;
112 425 bojilova
     this.user = user;
113 645 bojilova
     this.group = group;
114 680 bojilova
     this.pub = pub;
115
     this.serverCode = serverCode;
116 471 bojilova
     this.xmlIndex = new Thread(this);
117 203 jones
   }
118 680 bojilova
119
// NOT USED ANY MORE
120
//   public DBSAXHandler(Connection conn,String action,String docid,
121
//                       String user, String group)
122
//   {
123
//     this(conn);
124
//     this.action = action;
125
//     this.docid = docid;
126
//     this.user = user;
127
//     this.group = group;
128
//     this.xmlIndex = new Thread(this);
129
//     //this.xmlIndex.setPriority(Thread.MIN_PRIORITY);
130
//   }
131 203 jones
132 72 bojilova
   /** SAX Handler that receives notification of beginning of the document */
133 122 jones
   public void startDocument() throws SAXException {
134 203 jones
     MetaCatUtil.debugMessage("start Document");
135
136 185 jones
     // Create the document node representation as root
137 457 bojilova
     rootNode = new DBSAXNode(conn, this.docid);
138 185 jones
     // Add the node to the stack, so that any text data can be
139
     // added as it is encountered
140
     nodeStack.push(rootNode);
141 72 bojilova
   }
142
143
   /** SAX Handler that receives notification of end of the document */
144 122 jones
   public void endDocument() throws SAXException {
145 203 jones
     MetaCatUtil.debugMessage("end Document");
146 471 bojilova
     // Starting new thread for writing XML Index.
147
     // It calls the run method of the thread.
148
     try {
149
       xmlIndex.start();
150
     } catch (NullPointerException e) {
151
       xmlIndex = null;
152
       throw new
153
       SAXException("Problem with starting thread for writing XML Index. " +
154
                    e.getMessage());
155
     }
156 72 bojilova
   }
157
158 185 jones
   /** SAX Handler that is called at the start of each XML element */
159
   public void startElement(String uri, String localName,
160
                            String qName, Attributes atts)
161
               throws SAXException {
162 203 jones
     MetaCatUtil.debugMessage("Start ELEMENT " + localName);
163 72 bojilova
164 203 jones
     DBSAXNode parentNode = null;
165
     DBSAXNode currentNode = null;
166 17 jones
167 203 jones
     // Get a reference to the parent node for the id
168
     try {
169
       parentNode = (DBSAXNode)nodeStack.peek();
170
     } catch (EmptyStackException e) {
171 408 jones
       parentNode = null;
172 203 jones
     }
173 18 jones
174 203 jones
     // Document representation that points to the root document node
175 204 jones
     if (atFirstElement) {
176
       atFirstElement = false;
177 203 jones
       // If no DOCTYPE declaration: docname = root element name
178
       if (docname == null) {
179
         docname = localName;
180 204 jones
         doctype = docname;
181
         MetaCatUtil.debugMessage("DOCNAME-a: " + docname);
182
         MetaCatUtil.debugMessage("DOCTYPE-a: " + doctype);
183 203 jones
       } else if (doctype == null) {
184 204 jones
         doctype = docname;
185
         MetaCatUtil.debugMessage("DOCTYPE-b: " + doctype);
186 203 jones
       }
187
       rootNode.writeNodename(docname);
188
       try {
189 697 bojilova
         // for validated XML Documents store a reference to XML DB Catalog
190
         String catalogid = null;
191
         if ( systemid != null ) {
192
           Statement stmt = conn.createStatement();
193
           ResultSet rs = stmt.executeQuery(
194
                          "SELECT catalog_id FROM xml_catalog " +
195
                          "WHERE entry_type = 'DTD' " +
196
                          "AND public_id = '" + doctype + "'");
197
           boolean hasRow = rs.next();
198
           if ( hasRow ) {
199
            catalogid = rs.getString(1);
200
           }
201
           stmt.close();
202
         }
203 396 jones
         currentDocument = new DocumentImpl(conn, rootNode.getNodeID(),
204 697 bojilova
                               docname, doctype, docid, action, user,
205
                               this.pub, catalogid, this.serverCode);
206 457 bojilova
       } catch (Exception ane) {
207 675 berkley
         throw (new SAXException("Error in DBSaxHandler.startElement " +
208
                                 action, ane));
209 408 jones
       }
210 203 jones
     }
211 135 jones
212 203 jones
     // Create the current node representation
213 457 bojilova
     currentNode = new DBSAXNode(conn, localName, parentNode,
214
                                 currentDocument.getRootNodeID(),docid,
215
                                 currentDocument.getDoctype());
216 471 bojilova
217 203 jones
     // Add all of the attributes
218
     for (int i=0; i<atts.getLength(); i++) {
219 457 bojilova
       currentNode.setAttribute(atts.getLocalName(i), atts.getValue(i), docid);
220 203 jones
     }
221 17 jones
222 203 jones
     // Add the node to the stack, so that any text data can be
223
     // added as it is encountered
224
     nodeStack.push(currentNode);
225 471 bojilova
     // Add the node to the vector used by thread for writing XML Index
226
     nodeIndex.addElement(currentNode);
227
228 203 jones
  }
229 471 bojilova
230
  // The run method of xmlIndex thread. It writes XML Index for the document.
231
  public void run () {
232
    DBSAXNode currNode = null;
233
    DBSAXNode prevNode = null;
234 513 bojilova
    Connection dbconn = null;
235 638 bojilova
    String doctype = currentDocument.getDoctype();
236 471 bojilova
    int step = 0;
237
    int counter = 0;
238 17 jones
239 471 bojilova
    try {
240
      // Opening separate db connection for writing XML Index
241
      MetaCatUtil util = new MetaCatUtil();
242 513 bojilova
      dbconn = util.openDBConnection();
243
      dbconn.setAutoCommit(false);
244 471 bojilova
245
      // Going through the elements of the document and writing its Index
246
      Enumeration nodes = nodeIndex.elements();
247
      while ( nodes.hasMoreElements() ) {
248
        currNode = (DBSAXNode)nodes.nextElement();
249 638 bojilova
        currNode.updateNodeIndex(dbconn, docid, doctype);
250 471 bojilova
      }
251
252 513 bojilova
      dbconn.commit();
253 638 bojilova
254 483 berkley
      //if this is a package file then write the package info to
255 523 berkley
      //the xml_relation table. RelationHandler checks to see
256 483 berkley
      //if it is a package file so you don't have to do it here.
257 638 bojilova
      if ( doctype.equals(util.getOption("packagedoctype")) )
258
      {
259
        DocumentImpl xmldoc = new DocumentImpl(dbconn, docid);
260
        RelationHandler rth = new RelationHandler(xmldoc, dbconn);
261
      }
262 640 bojilova
      else if ( doctype.equals(util.getOption("accessdoctype")) )
263 638 bojilova
      {
264
        DocumentImpl xmldoc = new DocumentImpl(dbconn, docid);
265
        String xml = xmldoc.toString();
266 645 bojilova
        try {
267
          AccessControlList aclobj =
268
          new AccessControlList(dbconn, docid, new StringReader(xml),
269 684 bojilova
                                user, group, serverCode);
270 645 bojilova
          dbconn.commit();
271
        } catch (SAXException e) {
272
          try {
273
            dbconn.rollback();
274
            dbconn.close();
275
          } catch (SQLException sqle) {}
276 675 berkley
          System.out.println("Error writing ACL in DBSaxHandler.run " +
277
                             e.getMessage());
278 645 bojilova
        }
279 638 bojilova
      }
280 483 berkley
281 638 bojilova
282 513 bojilova
      dbconn.close();
283 483 berkley
284 471 bojilova
    } catch (Exception e) {
285
      try {
286 513 bojilova
        dbconn.rollback();
287
        dbconn.close();
288 471 bojilova
      } catch (SQLException sqle) {}
289 675 berkley
      System.out.println("Error writing XML Index in DBSaxHandler.run " +
290
                         e.getMessage());
291 471 bojilova
    }
292
  }
293
294 460 bojilova
  /** SAX Handler that is called for each XML text node */
295
  public void characters(char[] cbuf, int start, int len) throws SAXException {
296 203 jones
     MetaCatUtil.debugMessage("CHARACTERS");
297 186 jones
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
298 220 jones
     String data = null;
299
     int leftover = len;
300
     int offset = start;
301
     boolean moredata = true;
302
303
     // This loop deals with the case where there are more characters
304
     // than can fit in a single database text field (limit is
305
     // MAXDATACHARS).  If the text to be inserted exceeds MAXDATACHARS,
306
     // write a series of nodes that are MAXDATACHARS long, and then the
307
     // final node contains the remainder
308
     while (moredata) {
309
       if (leftover > MAXDATACHARS) {
310
         data = new String(cbuf, offset, MAXDATACHARS);
311
         leftover -= MAXDATACHARS;
312
         offset += MAXDATACHARS;
313
       } else {
314
         data = new String(cbuf, offset, leftover);
315
         moredata = false;
316
       }
317 122 jones
318 220 jones
       // Write the content of the node to the database
319 457 bojilova
       currentNode.writeChildNodeToDB("TEXT", null, data, docid);
320 220 jones
     }
321 460 bojilova
322 691 bojilova
// DOCTITLE attr cleared from the db
323
//     // write the title of document if there are tag <title>
324
//     if ( currentNode.getTagName().equals("title") ) {
325
//       if ( leftover > MAXTITLELEN )
326
//         currentDocument.setTitle(new String(cbuf, start, MAXTITLELEN));
327
//       else
328
//         currentDocument.setTitle(new String(cbuf, start, leftover));
329
//     }
330 17 jones
   }
331
332 31 jones
   /**
333 660 bojilova
    * SAX Handler that is called for each XML text node that is
334
    * Ignorable white space
335 31 jones
    */
336 660 bojilova
   public void ignorableWhitespace(char[] cbuf, int start, int len)
337
               throws SAXException {
338
     // When validation is turned "on", white spaces are reported here
339
     // When validation is turned "off" white spaces are not reported here,
340
     // but through characters() callback
341 203 jones
     MetaCatUtil.debugMessage("IGNORABLEWHITESPACE");
342 660 bojilova
     //System.out.println("Whitespace:" + len + "x" + new String(cbuf, start, len) + "x");
343
344
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
345
     String data = null;
346
     int leftover = len;
347
     int offset = start;
348
     boolean moredata = true;
349
350
     // This loop deals with the case where there are more characters
351
     // than can fit in a single database text field (limit is
352
     // MAXDATACHARS).  If the text to be inserted exceeds MAXDATACHARS,
353
     // write a series of nodes that are MAXDATACHARS long, and then the
354
     // final node contains the remainder
355
     while (moredata) {
356
       if (leftover > MAXDATACHARS) {
357
         data = new String(cbuf, offset, MAXDATACHARS);
358
         leftover -= MAXDATACHARS;
359
         offset += MAXDATACHARS;
360
       } else {
361
         data = new String(cbuf, offset, leftover);
362
         moredata = false;
363
       }
364
365
       // Write the content of the node to the database
366
       currentNode.writeChildNodeToDB("TEXT", null, data, docid);
367
     }
368 17 jones
   }
369
370 122 jones
   /**
371
    * SAX Handler called once for each processing instruction found:
372
    * node that PI may occur before or after the root element.
373
    */
374
   public void processingInstruction(String target, String data)
375
          throws SAXException {
376 203 jones
     MetaCatUtil.debugMessage("PI");
377 186 jones
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
378 457 bojilova
     currentNode.writeChildNodeToDB("PI", target, data, docid);
379 92 bojilova
   }
380 72 bojilova
381 31 jones
   /** SAX Handler that is called at the end of each XML element */
382 185 jones
   public void endElement(String uri, String localName,
383
                          String qName) throws SAXException {
384 203 jones
     MetaCatUtil.debugMessage("End ELEMENT " + localName);
385 17 jones
386 185 jones
     // Get the node from the stack
387
     DBSAXNode currentNode = (DBSAXNode)nodeStack.pop();
388 17 jones
   }
389
390 185 jones
   //
391
   // the next section implements the LexicalHandler interface
392
   //
393
394
   /** SAX Handler that receives notification of DOCTYPE. Sets the DTD */
395
   public void startDTD(String name, String publicId, String systemId)
396
               throws SAXException {
397
     docname = name;
398
     doctype = publicId;
399
     systemid = systemId;
400
401 697 bojilova
//System.out.println("Start DTD");
402
//System.out.println("DOCNAME: " + docname);
403
//System.out.println("DOCTYPE: " + doctype);
404
//System.out.println("  SYSID: " + systemid);
405
406 204 jones
     MetaCatUtil.debugMessage("Start DTD");
407 203 jones
     MetaCatUtil.debugMessage("DOCNAME: " + docname);
408
     MetaCatUtil.debugMessage("DOCTYPE: " + doctype);
409
     MetaCatUtil.debugMessage("  SYSID: " + systemid);
410 185 jones
   }
411
412
   /**
413
    * SAX Handler that receives notification of end of DTD
414
    */
415
   public void endDTD() throws SAXException {
416 697 bojilova
417
//System.out.println("end DTD");
418 204 jones
     MetaCatUtil.debugMessage("end DTD");
419 185 jones
   }
420
421
   /**
422
    * SAX Handler that receives notification of comments in the DTD
423
    */
424
   public void comment(char[] ch, int start, int length) throws SAXException {
425 203 jones
     MetaCatUtil.debugMessage("COMMENT");
426 660 bojilova
     if ( !processingDTD ) {
427
       DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
428
       currentNode.writeChildNodeToDB("COMMENT", null, new String(ch), docid);
429
     }
430 185 jones
   }
431
432
   /**
433
    * SAX Handler that receives notification of the start of CDATA sections
434
    */
435
   public void startCDATA() throws SAXException {
436 203 jones
     MetaCatUtil.debugMessage("start CDATA");
437 185 jones
   }
438
439
   /**
440
    * SAX Handler that receives notification of the end of CDATA sections
441
    */
442
   public void endCDATA() throws SAXException {
443 203 jones
     MetaCatUtil.debugMessage("end CDATA");
444 185 jones
   }
445
446
   /**
447
    * SAX Handler that receives notification of the start of entities
448
    */
449
   public void startEntity(String name) throws SAXException {
450 243 jones
     MetaCatUtil.debugMessage("start ENTITY: " + name);
451 697 bojilova
//System.out.println("start ENTITY: " + name);
452 243 jones
     if (name.equals("[dtd]")) {
453
       processingDTD = true;
454
     }
455 185 jones
   }
456
457
   /**
458
    * SAX Handler that receives notification of the end of entities
459
    */
460
   public void endEntity(String name) throws SAXException {
461 243 jones
     MetaCatUtil.debugMessage("end ENTITY: " + name);
462 697 bojilova
//System.out.println("end ENTITY: " + name);
463 243 jones
     if (name.equals("[dtd]")) {
464
       processingDTD = false;
465
     }
466 185 jones
   }
467 186 jones
468
   /**
469
    * SAX Handler that receives notification of element declarations
470
    */
471
   public void elementDecl(String name, String model)
472
                        throws org.xml.sax.SAXException {
473 697 bojilova
//System.out.println("ELEMENTDECL: " + name + " " + model);
474 243 jones
     MetaCatUtil.debugMessage("ELEMENTDECL: " + name + " " + model);
475 186 jones
   }
476
477
   /**
478
    * SAX Handler that receives notification of attribute declarations
479
    */
480
   public void attributeDecl(String eName, String aName,
481
                        String type, String valueDefault, String value)
482
                        throws org.xml.sax.SAXException {
483 697 bojilova
//System.out.println("ATTRIBUTEDECL: " + eName + " "
484
//                        + aName + " " + type + " " + valueDefault + " "
485
//                        + value);
486 243 jones
     MetaCatUtil.debugMessage("ATTRIBUTEDECL: " + eName + " "
487
                        + aName + " " + type + " " + valueDefault + " "
488
                        + value);
489 186 jones
   }
490
491
   /**
492
    * SAX Handler that receives notification of internal entity declarations
493
    */
494
   public void internalEntityDecl(String name, String value)
495
                        throws org.xml.sax.SAXException {
496 697 bojilova
//System.out.println("INTERNENTITYDECL: " + name + " " + value);
497 243 jones
     MetaCatUtil.debugMessage("INTERNENTITYDECL: " + name + " " + value);
498 186 jones
   }
499
500
   /**
501
    * SAX Handler that receives notification of external entity declarations
502
    */
503
   public void externalEntityDecl(String name, String publicId,
504
                        String systemId)
505
                        throws org.xml.sax.SAXException {
506 697 bojilova
//System.out.println("EXTERNENTITYDECL: " + name + " " + publicId
507
//                              + " " + systemId);
508 243 jones
     MetaCatUtil.debugMessage("EXTERNENTITYDECL: " + name + " " + publicId
509
                              + " " + systemId);
510 186 jones
   }
511
512 204 jones
   //
513
   // the next section implements the ErrorHandler interface
514
   //
515 186 jones
516 204 jones
   /**
517
    * SAX Handler that receives notification of fatal parsing errors
518
    */
519
   public void fatalError(SAXParseException exception) throws SAXException {
520
     MetaCatUtil.debugMessage("FATALERROR");
521
     throw (new SAXException("Fatal processing error.", exception));
522
   }
523
524
   /**
525
    * SAX Handler that receives notification of recoverable parsing errors
526
    */
527
   public void error(SAXParseException exception) throws SAXException {
528
     MetaCatUtil.debugMessage("ERROR");
529 660 bojilova
     throw (new SAXException("Processing error.", exception));
530 204 jones
   }
531
532
   /**
533
    * SAX Handler that receives notification of warnings
534
    */
535
   public void warning(SAXParseException exception) throws SAXException {
536 530 jones
     MetaCatUtil.debugMessage("WARNING");
537 660 bojilova
     throw (new SAXException("Warning.", exception));
538 204 jones
   }
539
540
   //
541
   // Helper, getter and setter methods
542
   //
543
544
   /**
545
    * get the document name
546
    */
547
   public String getDocname() {
548
     return docname;
549
   }
550
551
   /**
552
    * get the document processing state
553
    */
554 243 jones
   public boolean processingDTD() {
555
     return processingDTD;
556 204 jones
   }
557 17 jones
}