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 826 bojilova
System.out.println("Start ELEMENT " + uri);
162
System.out.println("Start ELEMENT " + qName);
163
System.out.println("Start ELEMENT " + localName);
164 821 bojilova
165 203 jones
     DBSAXNode parentNode = null;
166
     DBSAXNode currentNode = null;
167 17 jones
168 203 jones
     // Get a reference to the parent node for the id
169
     try {
170
       parentNode = (DBSAXNode)nodeStack.peek();
171
     } catch (EmptyStackException e) {
172 408 jones
       parentNode = null;
173 203 jones
     }
174 18 jones
175 203 jones
     // Document representation that points to the root document node
176 204 jones
     if (atFirstElement) {
177
       atFirstElement = false;
178 203 jones
       // If no DOCTYPE declaration: docname = root element name
179
       if (docname == null) {
180
         docname = localName;
181 204 jones
         doctype = docname;
182
         MetaCatUtil.debugMessage("DOCNAME-a: " + docname);
183
         MetaCatUtil.debugMessage("DOCTYPE-a: " + doctype);
184 203 jones
       } else if (doctype == null) {
185 204 jones
         doctype = docname;
186
         MetaCatUtil.debugMessage("DOCTYPE-b: " + doctype);
187 203 jones
       }
188
       rootNode.writeNodename(docname);
189
       try {
190 697 bojilova
         // for validated XML Documents store a reference to XML DB Catalog
191
         String catalogid = null;
192
         if ( systemid != null ) {
193
           Statement stmt = conn.createStatement();
194
           ResultSet rs = stmt.executeQuery(
195
                          "SELECT catalog_id FROM xml_catalog " +
196
                          "WHERE entry_type = 'DTD' " +
197
                          "AND public_id = '" + doctype + "'");
198
           boolean hasRow = rs.next();
199
           if ( hasRow ) {
200
            catalogid = rs.getString(1);
201
           }
202
           stmt.close();
203
         }
204 396 jones
         currentDocument = new DocumentImpl(conn, rootNode.getNodeID(),
205 697 bojilova
                               docname, doctype, docid, action, user,
206
                               this.pub, catalogid, this.serverCode);
207 457 bojilova
       } catch (Exception ane) {
208 675 berkley
         throw (new SAXException("Error in DBSaxHandler.startElement " +
209
                                 action, ane));
210 408 jones
       }
211 203 jones
     }
212 135 jones
213 203 jones
     // Create the current node representation
214 826 bojilova
     currentNode = new DBSAXNode(conn, qName, localName, parentNode,
215 457 bojilova
                                 currentDocument.getRootNodeID(),docid,
216
                                 currentDocument.getDoctype());
217 471 bojilova
218 821 bojilova
     // Add all of the namespaces
219
     String prefix;
220
     String nsuri;
221
     Enumeration prefixes = namespaces.keys();
222
     while ( prefixes.hasMoreElements() ) {
223
       prefix = (String)prefixes.nextElement();
224
       nsuri = (String)namespaces.get(prefix);
225
       currentNode.setNamespace(prefix, nsuri, docid);
226
     }
227
     namespaces = null;
228
     namespaces = new Hashtable();
229
230 203 jones
     // Add all of the attributes
231
     for (int i=0; i<atts.getLength(); i++) {
232 821 bojilova
       currentNode.setAttribute(atts.getQName(i), atts.getValue(i), docid);
233 203 jones
     }
234 17 jones
235 203 jones
     // Add the node to the stack, so that any text data can be
236
     // added as it is encountered
237
     nodeStack.push(currentNode);
238 471 bojilova
     // Add the node to the vector used by thread for writing XML Index
239
     nodeIndex.addElement(currentNode);
240
241 203 jones
  }
242 471 bojilova
243 819 bojilova
  /* The run method of xmlIndex thread. It writes XML Index for the document. */
244 471 bojilova
  public void run () {
245
    DBSAXNode currNode = null;
246
    DBSAXNode prevNode = null;
247 513 bojilova
    Connection dbconn = null;
248 638 bojilova
    String doctype = currentDocument.getDoctype();
249 471 bojilova
    int step = 0;
250
    int counter = 0;
251 17 jones
252 471 bojilova
    try {
253
      // Opening separate db connection for writing XML Index
254
      MetaCatUtil util = new MetaCatUtil();
255 513 bojilova
      dbconn = util.openDBConnection();
256
      dbconn.setAutoCommit(false);
257 471 bojilova
258
      // Going through the elements of the document and writing its Index
259
      Enumeration nodes = nodeIndex.elements();
260
      while ( nodes.hasMoreElements() ) {
261
        currNode = (DBSAXNode)nodes.nextElement();
262 638 bojilova
        currNode.updateNodeIndex(dbconn, docid, doctype);
263 471 bojilova
      }
264
265 513 bojilova
      dbconn.commit();
266 638 bojilova
267 819 bojilova
      //if this is a package file
268 638 bojilova
      if ( doctype.equals(util.getOption("packagedoctype")) )
269
      {
270 819 bojilova
        // write the package info to xml_relation table
271 797 bojilova
        RelationHandler rth = new RelationHandler(docid, dbconn);
272 819 bojilova
        // from the relations get the access file id for that package
273
        String aclid = rth.getAccessFileID(docid);
274
        // if there are access file, write ACL for that package
275
        if ( aclid != null ) {
276
          runAccessControlList(dbconn, aclid);
277 645 bojilova
        }
278 638 bojilova
      }
279 819 bojilova
      // if it is an access file
280
      else if ( doctype.equals(util.getOption("accessdoctype")) )
281
      {
282
        // write ACL for the package
283
        runAccessControlList(dbconn, docid);
284
      }
285 483 berkley
286 513 bojilova
      dbconn.close();
287 483 berkley
288 471 bojilova
    } catch (Exception e) {
289
      try {
290 513 bojilova
        dbconn.rollback();
291
        dbconn.close();
292 471 bojilova
      } catch (SQLException sqle) {}
293 819 bojilova
      System.out.println("Error in DBSAXHandler.run " + e.getMessage());
294
      e.printStackTrace();
295 471 bojilova
    }
296
  }
297 819 bojilova
298
  // It runs in xmlIndex thread. It writes ACL for a package.
299
  private void runAccessControlList (Connection conn, String aclid)
300
                                                throws Exception
301
  {
302
    // read the access file from xml_nodes
303
    // parse the access file and store the access info into xml_access
304
    AccessControlList aclobj =
305
    new AccessControlList(conn, aclid, //new StringReader(xml),
306
                          user, groups, serverCode);
307
    conn.commit();
308
  }
309 471 bojilova
310 819 bojilova
311 460 bojilova
  /** SAX Handler that is called for each XML text node */
312
  public void characters(char[] cbuf, int start, int len) throws SAXException {
313 203 jones
     MetaCatUtil.debugMessage("CHARACTERS");
314 186 jones
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
315 220 jones
     String data = null;
316
     int leftover = len;
317
     int offset = start;
318
     boolean moredata = true;
319
320
     // This loop deals with the case where there are more characters
321
     // than can fit in a single database text field (limit is
322
     // MAXDATACHARS).  If the text to be inserted exceeds MAXDATACHARS,
323
     // write a series of nodes that are MAXDATACHARS long, and then the
324
     // final node contains the remainder
325
     while (moredata) {
326
       if (leftover > MAXDATACHARS) {
327
         data = new String(cbuf, offset, MAXDATACHARS);
328
         leftover -= MAXDATACHARS;
329
         offset += MAXDATACHARS;
330
       } else {
331
         data = new String(cbuf, offset, leftover);
332
         moredata = false;
333
       }
334 122 jones
335 220 jones
       // Write the content of the node to the database
336 457 bojilova
       currentNode.writeChildNodeToDB("TEXT", null, data, docid);
337 220 jones
     }
338 17 jones
   }
339
340 31 jones
   /**
341 660 bojilova
    * SAX Handler that is called for each XML text node that is
342
    * Ignorable white space
343 31 jones
    */
344 660 bojilova
   public void ignorableWhitespace(char[] cbuf, int start, int len)
345
               throws SAXException {
346
     // When validation is turned "on", white spaces are reported here
347
     // When validation is turned "off" white spaces are not reported here,
348
     // but through characters() callback
349 203 jones
     MetaCatUtil.debugMessage("IGNORABLEWHITESPACE");
350 660 bojilova
     //System.out.println("Whitespace:" + len + "x" + new String(cbuf, start, len) + "x");
351
352
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
353
     String data = null;
354
     int leftover = len;
355
     int offset = start;
356
     boolean moredata = true;
357
358
     // This loop deals with the case where there are more characters
359
     // than can fit in a single database text field (limit is
360
     // MAXDATACHARS).  If the text to be inserted exceeds MAXDATACHARS,
361
     // write a series of nodes that are MAXDATACHARS long, and then the
362
     // final node contains the remainder
363
     while (moredata) {
364
       if (leftover > MAXDATACHARS) {
365
         data = new String(cbuf, offset, MAXDATACHARS);
366
         leftover -= MAXDATACHARS;
367
         offset += MAXDATACHARS;
368
       } else {
369
         data = new String(cbuf, offset, leftover);
370
         moredata = false;
371
       }
372
373
       // Write the content of the node to the database
374
       currentNode.writeChildNodeToDB("TEXT", null, data, docid);
375
     }
376 17 jones
   }
377
378 122 jones
   /**
379
    * SAX Handler called once for each processing instruction found:
380
    * node that PI may occur before or after the root element.
381
    */
382
   public void processingInstruction(String target, String data)
383
          throws SAXException {
384 203 jones
     MetaCatUtil.debugMessage("PI");
385 186 jones
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
386 457 bojilova
     currentNode.writeChildNodeToDB("PI", target, data, docid);
387 92 bojilova
   }
388 72 bojilova
389 31 jones
   /** SAX Handler that is called at the end of each XML element */
390 185 jones
   public void endElement(String uri, String localName,
391
                          String qName) throws SAXException {
392 821 bojilova
     MetaCatUtil.debugMessage("End ELEMENT " + qName);
393 17 jones
394 185 jones
     // Get the node from the stack
395
     DBSAXNode currentNode = (DBSAXNode)nodeStack.pop();
396 17 jones
   }
397
398 185 jones
   //
399
   // the next section implements the LexicalHandler interface
400
   //
401
402
   /** SAX Handler that receives notification of DOCTYPE. Sets the DTD */
403
   public void startDTD(String name, String publicId, String systemId)
404
               throws SAXException {
405
     docname = name;
406
     doctype = publicId;
407
     systemid = systemId;
408
409 204 jones
     MetaCatUtil.debugMessage("Start DTD");
410 203 jones
     MetaCatUtil.debugMessage("DOCNAME: " + docname);
411
     MetaCatUtil.debugMessage("DOCTYPE: " + doctype);
412
     MetaCatUtil.debugMessage("  SYSID: " + systemid);
413 185 jones
   }
414
415
   /**
416
    * SAX Handler that receives notification of end of DTD
417
    */
418
   public void endDTD() throws SAXException {
419 697 bojilova
420 204 jones
     MetaCatUtil.debugMessage("end DTD");
421 185 jones
   }
422
423
   /**
424
    * SAX Handler that receives notification of comments in the DTD
425
    */
426
   public void comment(char[] ch, int start, int length) throws SAXException {
427 203 jones
     MetaCatUtil.debugMessage("COMMENT");
428 660 bojilova
     if ( !processingDTD ) {
429
       DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
430
       currentNode.writeChildNodeToDB("COMMENT", null, new String(ch), docid);
431
     }
432 185 jones
   }
433
434
   /**
435
    * SAX Handler that receives notification of the start of CDATA sections
436
    */
437
   public void startCDATA() throws SAXException {
438 203 jones
     MetaCatUtil.debugMessage("start CDATA");
439 185 jones
   }
440
441
   /**
442
    * SAX Handler that receives notification of the end of CDATA sections
443
    */
444
   public void endCDATA() throws SAXException {
445 203 jones
     MetaCatUtil.debugMessage("end CDATA");
446 185 jones
   }
447
448
   /**
449
    * SAX Handler that receives notification of the start of entities
450
    */
451
   public void startEntity(String name) throws SAXException {
452 243 jones
     MetaCatUtil.debugMessage("start ENTITY: " + name);
453 697 bojilova
//System.out.println("start ENTITY: " + name);
454 243 jones
     if (name.equals("[dtd]")) {
455
       processingDTD = true;
456
     }
457 185 jones
   }
458
459
   /**
460
    * SAX Handler that receives notification of the end of entities
461
    */
462
   public void endEntity(String name) throws SAXException {
463 243 jones
     MetaCatUtil.debugMessage("end ENTITY: " + name);
464 697 bojilova
//System.out.println("end ENTITY: " + name);
465 243 jones
     if (name.equals("[dtd]")) {
466
       processingDTD = false;
467
     }
468 185 jones
   }
469 186 jones
470
   /**
471
    * SAX Handler that receives notification of element declarations
472
    */
473
   public void elementDecl(String name, String model)
474
                        throws org.xml.sax.SAXException {
475 697 bojilova
//System.out.println("ELEMENTDECL: " + name + " " + model);
476 243 jones
     MetaCatUtil.debugMessage("ELEMENTDECL: " + name + " " + model);
477 186 jones
   }
478
479
   /**
480
    * SAX Handler that receives notification of attribute declarations
481
    */
482
   public void attributeDecl(String eName, String aName,
483
                        String type, String valueDefault, String value)
484
                        throws org.xml.sax.SAXException {
485 821 bojilova
486 697 bojilova
//System.out.println("ATTRIBUTEDECL: " + eName + " "
487
//                        + aName + " " + type + " " + valueDefault + " "
488
//                        + value);
489 243 jones
     MetaCatUtil.debugMessage("ATTRIBUTEDECL: " + eName + " "
490
                        + aName + " " + type + " " + valueDefault + " "
491
                        + value);
492 186 jones
   }
493
494
   /**
495
    * SAX Handler that receives notification of internal entity declarations
496
    */
497
   public void internalEntityDecl(String name, String value)
498
                        throws org.xml.sax.SAXException {
499 697 bojilova
//System.out.println("INTERNENTITYDECL: " + name + " " + value);
500 243 jones
     MetaCatUtil.debugMessage("INTERNENTITYDECL: " + name + " " + value);
501 186 jones
   }
502
503
   /**
504
    * SAX Handler that receives notification of external entity declarations
505
    */
506
   public void externalEntityDecl(String name, String publicId,
507
                        String systemId)
508
                        throws org.xml.sax.SAXException {
509 697 bojilova
//System.out.println("EXTERNENTITYDECL: " + name + " " + publicId
510
//                              + " " + systemId);
511 243 jones
     MetaCatUtil.debugMessage("EXTERNENTITYDECL: " + name + " " + publicId
512
                              + " " + systemId);
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
}