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