Project

General

Profile

1
/**
2
 *  '$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
 *    Release: @release@
9
 *
10
 *   '$Author: tao $'
11
 *     '$Date: 2003-01-09 18:12:32 -0800 (Thu, 09 Jan 2003) $'
12
 * '$Revision: 1364 $'
13
 *
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
 */
28

    
29
package edu.ucsb.nceas.metacat;
30

    
31
import java.sql.*;
32
import java.io.StringReader;
33
import java.util.Stack;
34
import java.util.Vector;
35
import java.util.Hashtable;
36
import java.util.Enumeration;
37
import java.util.EmptyStackException;
38

    
39
import org.xml.sax.Attributes;
40
import org.xml.sax.SAXException;
41
import org.xml.sax.SAXParseException;
42
import org.xml.sax.ext.DeclHandler;
43
import org.xml.sax.ext.LexicalHandler;
44
import org.xml.sax.helpers.DefaultHandler;
45

    
46
/**
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
public class DBSAXHandler extends DefaultHandler
51
                          implements LexicalHandler, DeclHandler, Runnable {
52

    
53
   private boolean	atFirstElement;
54
   private boolean	processingDTD;
55
   private String 	docname = null;
56
   private String 	doctype;
57
   private String 	systemid;
58
   private boolean 	stackCreated = false;
59
   private Stack 	  nodeStack;
60
   private Vector   nodeIndex;
61
   private DBConnection	  connection = null;
62
   private DocumentImpl currentDocument;
63
   private DBSAXNode    rootNode;
64
   private String   action = null;
65
   private String   docid = null;
66
   private String   revision = null;
67
   private String   user = null;
68
   private String[] groups = null;
69
   private String   pub = null;
70
   private Thread   xmlIndex;
71
   private boolean endDocument = false;
72
   private int serverCode = 1;
73
   private Hashtable namespaces = new Hashtable();
74

    
75
   private static final int MAXDATACHARS = 4000;
76
// DOCTITLE attr cleared from the db
77
//   private static final int MAXTITLELEN = 1000;
78

    
79
   /** Construct an instance of the handler class
80
    *
81
    * @param conn the JDBC connection to which information is written
82
    */
83
   public DBSAXHandler(DBConnection conn) {
84
     this.connection = conn;
85
     this.atFirstElement = true;
86
     this.processingDTD = false;
87

    
88
     // Create the stack for keeping track of node context
89
     // if it doesn't already exist
90
     if (!stackCreated) {
91
       nodeStack = new Stack();
92
       nodeIndex = new Vector();
93
       stackCreated = true;
94
     }
95
   }
96

    
97
  /** Construct an instance of the handler class
98
    *
99
    * @param conn the JDBC connection to which information is written
100
    * @param action - "INSERT" or "UPDATE"
101
    * @param docid to be inserted or updated into JDBC connection
102
    * @param user the user connected to MetaCat servlet and owns the document
103
    * @param groups the groups to which user belongs
104
    * @param pub flag for public "read" access on document
105
    * @param serverCode the serverid from xml_replication on which this document
106
    *        resides.
107
    *
108
    */
109
   public DBSAXHandler(DBConnection conn, String action, String docid,
110
                      String user, String[] groups, String pub, int serverCode)
111
   {
112
     this(conn);
113
     this.action = action;
114
     this.docid = docid;
115
     this.user = user;
116
     this.groups = groups;
117
     this.pub = pub;
118
     this.serverCode = serverCode;
119
     this.xmlIndex = new Thread(this);
120
   }
121

    
122
  /** Construct an instance of the handler class
123
    * In this constructor, user can specify the version need to upadate
124
    *
125
    * @param conn the JDBC connection to which information is written
126
    * @param action - "INSERT" or "UPDATE"
127
    * @param docid to be inserted or updated into JDBC connection
128
    * @param revision, the user specified the revision need to be update
129
    * @param user the user connected to MetaCat servlet and owns the document
130
    * @param groups the groups to which user belongs
131
    * @param pub flag for public "read" access on document
132
    * @param serverCode the serverid from xml_replication on which this document
133
    *        resides.
134
    *
135
    */
136
   public DBSAXHandler(DBConnection conn, String action, String docid,
137
     String revision, String user, String[] groups, String pub, int serverCode)
138
   {
139
     this(conn);
140
     this.action = action;
141
     this.docid = docid;
142
     this.revision = revision;
143
     this.user = user;
144
     this.groups = groups;
145
     this.pub = pub;
146
     this.serverCode = serverCode;
147
     this.xmlIndex = new Thread(this);
148
   }
149

    
150
   /** SAX Handler that receives notification of beginning of the document */
151
   public void startDocument() throws SAXException {
152
     MetaCatUtil.debugMessage("start Document", 50);
153

    
154
     // Create the document node representation as root
155
     rootNode = new DBSAXNode(connection, this.docid);
156
     // Add the node to the stack, so that any text data can be
157
     // added as it is encountered
158
     nodeStack.push(rootNode);
159
   }
160

    
161
   /** SAX Handler that receives notification of end of the document */
162
   public void endDocument() throws SAXException {
163
     MetaCatUtil.debugMessage("end Document", 50);
164
     // Starting new thread for writing XML Index.
165
     // It calls the run method of the thread.
166
     try {
167
       xmlIndex.start();
168
     } catch (NullPointerException e) {
169
       xmlIndex = null;
170
       throw new
171
       SAXException("Problem with starting thread for writing XML Index. " +
172
                    e.getMessage());
173
     }
174
   }
175

    
176
   /** SAX Handler that is called at the start of Namespace */
177
   public void startPrefixMapping(String prefix, String uri)
178
                                          throws SAXException
179
   {
180
    MetaCatUtil.debugMessage("NAMESPACE", 50);
181

    
182
    namespaces.put(prefix, uri);
183
   }
184

    
185
   /** SAX Handler that is called at the start of each XML element */
186
   public void startElement(String uri, String localName,
187
                            String qName, Attributes atts)
188
               throws SAXException {
189
     MetaCatUtil.debugMessage("Start ELEMENT " + qName, 50);
190

    
191
     DBSAXNode parentNode = null;
192
     DBSAXNode currentNode = null;
193

    
194
     // Get a reference to the parent node for the id
195
     try {
196
       parentNode = (DBSAXNode)nodeStack.peek();
197
     } catch (EmptyStackException e) {
198
       parentNode = null;
199
     }
200

    
201
     // Document representation that points to the root document node
202
     if (atFirstElement) {
203
       atFirstElement = false;
204
       // If no DOCTYPE declaration: docname = root element name
205
       if (docname == null) {
206
         docname = localName;
207
         doctype = docname;
208
         MetaCatUtil.debugMessage("DOCNAME-a: " + docname, 50);
209
         MetaCatUtil.debugMessage("DOCTYPE-a: " + doctype, 50);
210
       } else if (doctype == null) {
211
         doctype = docname;
212
         MetaCatUtil.debugMessage("DOCTYPE-b: " + doctype, 50);
213
       }
214
       rootNode.writeNodename(docname);
215
       try {
216
         // for validated XML Documents store a reference to XML DB Catalog
217
         // Because this is select statement and it needn't to roll back if
218
         // insert document action fialed.
219
         // In order to decrease DBConnection usage count, we get a new
220
         // dbconnection from pool
221
         String catalogid = null;
222
         DBConnection dbConn = null;
223
         int serialNumber = -1;
224

    
225
         if ( systemid != null ) {
226
           try
227
           {
228
            // Get dbconnection
229
            dbConn=DBConnectionPool.getDBConnection
230
                                          ("DBSAXHandler.startElement");
231
            serialNumber=dbConn.getCheckOutSerialNumber();
232

    
233
            Statement stmt = dbConn.createStatement();
234
            ResultSet rs = stmt.executeQuery(
235
                          "SELECT catalog_id FROM xml_catalog " +
236
                          "WHERE entry_type = 'DTD' " +
237
                          "AND public_id = '" + doctype + "'");
238
            boolean hasRow = rs.next();
239
            if ( hasRow ) {
240
              catalogid = rs.getString(1);
241
            }
242
            stmt.close();
243
           }//try
244
           finally
245
           {
246
             // Return dbconnection
247
             DBConnectionPool.returnDBConnection(dbConn, serialNumber);
248
           }//finally
249
         }
250

    
251
         //create documentImpl object by the constructor which can specify
252
         //the revision
253
         currentDocument = new DocumentImpl(connection, rootNode.getNodeID(),
254
                               docname, doctype, docid, revision, action, user,
255
                               this.pub, catalogid, this.serverCode);
256

    
257

    
258
       } catch (Exception ane) {
259
         throw (new SAXException("Error in DBSaxHandler.startElement " +
260
                                 action, ane));
261
       }
262
     }
263

    
264
     // Create the current node representation
265
     currentNode = new DBSAXNode(connection, qName, localName, parentNode,
266
                                 currentDocument.getRootNodeID(),docid,
267
                                 currentDocument.getDoctype());
268

    
269
     // Add all of the namespaces
270
     String prefix;
271
     String nsuri;
272
     Enumeration prefixes = namespaces.keys();
273
     while ( prefixes.hasMoreElements() ) {
274
       prefix = (String)prefixes.nextElement();
275
       nsuri = (String)namespaces.get(prefix);
276
       currentNode.setNamespace(prefix, nsuri, docid);
277
     }
278
     namespaces = null;
279
     namespaces = new Hashtable();
280

    
281
     // Add all of the attributes
282
     for (int i=0; i<atts.getLength(); i++) {
283
       currentNode.setAttribute(atts.getQName(i), atts.getValue(i), docid);
284
     }
285

    
286
     // Add the node to the stack, so that any text data can be
287
     // added as it is encountered
288
     nodeStack.push(currentNode);
289
     // Add the node to the vector used by thread for writing XML Index
290
     nodeIndex.addElement(currentNode);
291

    
292
  }
293

    
294
  /* The run method of xmlIndex thread. It writes XML Index for the document. */
295
  public void run () {
296
    DBSAXNode currNode = null;
297
    DBSAXNode prevNode = null;
298
    DBConnection dbConn = null;
299
    int serialNumber = -1;
300
    String doctype = currentDocument.getDoctype();
301
    int step = 0;
302
    int counter = 0;
303

    
304
    try {
305

    
306
      // Opening separate db connection for writing XML Index
307
      dbConn=DBConnectionPool.getDBConnection("DBSAXHandler.run");
308
      serialNumber=dbConn.getCheckOutSerialNumber();
309
      dbConn.setAutoCommit(false);
310

    
311
      //the following while loop construct checks to make sure that the docid
312
      //of the document that we are trying to index is already
313
      //in the xml_documents table.  if this is not the case, the foreign
314
      //key relationship between xml_documents and xml_index is temporarily
315
      //broken causing multiple problems.
316
      boolean inxmldoc = false;
317
      while(!inxmldoc)
318
      {
319
        String xmlDocumentsCheck = "select distinct docid from xml_documents";
320
        PreparedStatement xmlDocCheck =
321
                                 dbConn.prepareStatement(xmlDocumentsCheck);
322
        // Increase usage count
323
        dbConn.increaseUsageCount(1);
324
        xmlDocCheck.execute();
325
        ResultSet doccheckRS = xmlDocCheck.getResultSet();
326
        boolean tableHasRows = doccheckRS.next();
327
        Vector docids = new Vector();
328
        while(tableHasRows)
329
        {
330
          docids.add(doccheckRS.getString(1).trim());
331
          tableHasRows = doccheckRS.next();
332
        }
333

    
334
        for(int i=0; i<docids.size(); i++)
335
        {
336
          String d = ((String)docids.elementAt(i)).trim();
337
          if(docid.trim().equals(d))
338
          {
339
            inxmldoc = true;
340
          }
341
        }
342
        xmlDocCheck.close();
343
      }
344

    
345
      // Going through the elements of the document and writing its Index
346
      Enumeration nodes = nodeIndex.elements();
347
      while ( nodes.hasMoreElements() ) {
348
        currNode = (DBSAXNode)nodes.nextElement();
349
        currNode.updateNodeIndex(dbConn, docid, doctype);
350
      }
351

    
352

    
353
      dbConn.commit();
354

    
355
      //if this is a package file
356
      String packagedoctype = MetaCatUtil.getOption("packagedoctype");
357
      Vector packagedoctypes = new Vector();
358

    
359
      packagedoctypes = MetaCatUtil.getOptionList(packagedoctype);
360

    
361
      if ( packagedoctypes.contains(doctype) )
362
      {
363
        // write the package info to xml_relation table
364
        RelationHandler rth = new RelationHandler(docid, dbConn);
365
        // from the relations get the access file id for that package
366
        String aclid = rth.getAccessFileID(docid);
367
        // if there are access file, write ACL for that package
368
        if ( aclid != null ) {
369
          runAccessControlList(dbConn, aclid);
370
        }
371

    
372
      }
373

    
374
      // if it is an access file
375
      else if ( MetaCatUtil.getOptionList(
376
                     MetaCatUtil.getOption("accessdoctype")).contains(doctype) )
377
      {
378
        // write ACL for the package
379
        //runAccessControlList(dbConn, docid);
380

    
381
      }
382

    
383

    
384
      //dbconn.close();
385

    
386
    } catch (Exception e) {
387
      try {
388
        dbConn.rollback();
389
        //dbconn.close();
390
      } catch (SQLException sqle) {}
391
      System.out.println("Error in DBSAXHandler.run " + e.getMessage());
392
      e.printStackTrace();
393
    }
394
    finally
395
    {
396
      DBConnectionPool.returnDBConnection(dbConn, serialNumber);
397
    }//finally
398
  }
399

    
400
  // It runs in xmlIndex thread. It writes ACL for a package.
401
  private void runAccessControlList (DBConnection conn, String aclid)
402
                                                throws Exception
403
  {
404
    // read the access file from xml_nodes
405
    // parse the access file and store the access info into xml_access
406
    AccessControlList aclobj =
407
    new AccessControlList(conn, aclid, //new StringReader(xml),
408
                          user, groups, serverCode);
409
    conn.commit();
410
  }
411

    
412

    
413
  /** SAX Handler that is called for each XML text node */
414
  public void characters(char[] cbuf, int start, int len) throws SAXException {
415
     MetaCatUtil.debugMessage("CHARACTERS", 50);
416
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
417
     String data = null;
418
     int leftover = len;
419
     int offset = start;
420
     boolean moredata = true;
421

    
422
     // This loop deals with the case where there are more characters
423
     // than can fit in a single database text field (limit is
424
     // MAXDATACHARS).  If the text to be inserted exceeds MAXDATACHARS,
425
     // write a series of nodes that are MAXDATACHARS long, and then the
426
     // final node contains the remainder
427
     while (moredata) {
428
       if (leftover > MAXDATACHARS) {
429
         data = new String(cbuf, offset, MAXDATACHARS);
430
         leftover -= MAXDATACHARS;
431
         offset += MAXDATACHARS;
432
       } else {
433
         data = new String(cbuf, offset, leftover);
434
         moredata = false;
435
       }
436

    
437
       // Write the content of the node to the database
438
       currentNode.writeChildNodeToDB("TEXT", null, data, docid);
439
     }
440
   }
441

    
442
   /**
443
    * SAX Handler that is called for each XML text node that is
444
    * Ignorable white space
445
    */
446
   public void ignorableWhitespace(char[] cbuf, int start, int len)
447
               throws SAXException {
448
     // When validation is turned "on", white spaces are reported here
449
     // When validation is turned "off" white spaces are not reported here,
450
     // but through characters() callback
451
     MetaCatUtil.debugMessage("IGNORABLEWHITESPACE", 50);
452

    
453

    
454
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
455
     String data = null;
456
     int leftover = len;
457
     int offset = start;
458
     boolean moredata = true;
459

    
460
     // This loop deals with the case where there are more characters
461
     // than can fit in a single database text field (limit is
462
     // MAXDATACHARS).  If the text to be inserted exceeds MAXDATACHARS,
463
     // write a series of nodes that are MAXDATACHARS long, and then the
464
     // final node contains the remainder
465
     while (moredata) {
466
       if (leftover > MAXDATACHARS) {
467
         data = new String(cbuf, offset, MAXDATACHARS);
468
         leftover -= MAXDATACHARS;
469
         offset += MAXDATACHARS;
470
       } else {
471
         data = new String(cbuf, offset, leftover);
472
         moredata = false;
473
       }
474

    
475
       // Write the content of the node to the database
476
       currentNode.writeChildNodeToDB("TEXT", null, data, docid);
477
     }
478
   }
479

    
480
   /**
481
    * SAX Handler called once for each processing instruction found:
482
    * node that PI may occur before or after the root element.
483
    */
484
   public void processingInstruction(String target, String data)
485
          throws SAXException {
486
     MetaCatUtil.debugMessage("PI", 50);
487
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
488
     currentNode.writeChildNodeToDB("PI", target, data, docid);
489
   }
490

    
491
   /** SAX Handler that is called at the end of each XML element */
492
   public void endElement(String uri, String localName,
493
                          String qName) throws SAXException {
494
     MetaCatUtil.debugMessage("End ELEMENT " + qName, 50);
495

    
496
     // Get the node from the stack
497
     DBSAXNode currentNode = (DBSAXNode)nodeStack.pop();
498
   }
499

    
500
   //
501
   // the next section implements the LexicalHandler interface
502
   //
503

    
504
   /** SAX Handler that receives notification of DOCTYPE. Sets the DTD */
505
   public void startDTD(String name, String publicId, String systemId)
506
               throws SAXException {
507
     docname = name;
508
     doctype = publicId;
509
     systemid = systemId;
510

    
511
     processingDTD = true;
512

    
513
     MetaCatUtil.debugMessage("Start DTD", 50);
514
     MetaCatUtil.debugMessage("Setting processingDTD to true", 50);
515
     MetaCatUtil.debugMessage("DOCNAME: " + docname, 50);
516
     MetaCatUtil.debugMessage("DOCTYPE: " + doctype, 50);
517
     MetaCatUtil.debugMessage("  SYSID: " + systemid, 50);
518
   }
519

    
520
   /**
521
    * SAX Handler that receives notification of end of DTD
522
    */
523
   public void endDTD() throws SAXException {
524

    
525
     processingDTD = false;
526
     MetaCatUtil.debugMessage("Setting processingDTD to false", 50);
527
     MetaCatUtil.debugMessage("end DTD", 50);
528
   }
529

    
530
   /**
531
    * SAX Handler that receives notification of comments in the DTD
532
    */
533
   public void comment(char[] ch, int start, int length) throws SAXException {
534
     MetaCatUtil.debugMessage("COMMENT", 50);
535
     if ( !processingDTD ) {
536
       DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
537
       currentNode.writeChildNodeToDB("COMMENT", null, new String(ch), docid);
538
     }
539
   }
540

    
541
   /**
542
    * SAX Handler that receives notification of the start of CDATA sections
543
    */
544
   public void startCDATA() throws SAXException {
545
     MetaCatUtil.debugMessage("start CDATA", 50);
546
   }
547

    
548
   /**
549
    * SAX Handler that receives notification of the end of CDATA sections
550
    */
551
   public void endCDATA() throws SAXException {
552
     MetaCatUtil.debugMessage("end CDATA", 50);
553
   }
554

    
555
   /**
556
    * SAX Handler that receives notification of the start of entities
557
    */
558
   public void startEntity(String name) throws SAXException {
559
     MetaCatUtil.debugMessage("start ENTITY: " + name, 50);
560
//System.out.println("start ENTITY: " + name);
561
     if (name.equals("[dtd]")) {
562
       processingDTD = true;
563
     }
564
   }
565

    
566
   /**
567
    * SAX Handler that receives notification of the end of entities
568
    */
569
   public void endEntity(String name) throws SAXException {
570
     MetaCatUtil.debugMessage("end ENTITY: " + name, 50);
571
//System.out.println("end ENTITY: " + name);
572
     if (name.equals("[dtd]")) {
573
       processingDTD = false;
574
     }
575
   }
576

    
577
   /**
578
    * SAX Handler that receives notification of element declarations
579
    */
580
   public void elementDecl(String name, String model)
581
                        throws org.xml.sax.SAXException {
582
//System.out.println("ELEMENTDECL: " + name + " " + model);
583
     MetaCatUtil.debugMessage("ELEMENTDECL: " + name + " " + model, 50);
584
   }
585

    
586
   /**
587
    * SAX Handler that receives notification of attribute declarations
588
    */
589
   public void attributeDecl(String eName, String aName,
590
                        String type, String valueDefault, String value)
591
                        throws org.xml.sax.SAXException {
592

    
593
//System.out.println("ATTRIBUTEDECL: " + eName + " "
594
//                        + aName + " " + type + " " + valueDefault + " "
595
//                        + value);
596
     MetaCatUtil.debugMessage("ATTRIBUTEDECL: " + eName + " "
597
                        + aName + " " + type + " " + valueDefault + " "
598
                        + value, 50);
599
   }
600

    
601
   /**
602
    * SAX Handler that receives notification of internal entity declarations
603
    */
604
   public void internalEntityDecl(String name, String value)
605
                        throws org.xml.sax.SAXException {
606
//System.out.println("INTERNENTITYDECL: " + name + " " + value);
607
     MetaCatUtil.debugMessage("INTERNENTITYDECL: " + name + " " + value, 50);
608
   }
609

    
610
   /**
611
    * SAX Handler that receives notification of external entity declarations
612
    */
613
   public void externalEntityDecl(String name, String publicId,
614
                        String systemId)
615
                        throws org.xml.sax.SAXException {
616
//System.out.println("EXTERNENTITYDECL: " + name + " " + publicId
617
//                              + " " + systemId);
618
     MetaCatUtil.debugMessage("EXTERNENTITYDECL: " + name + " " + publicId
619
                              + " " + systemId, 50);
620
     // it processes other external entity, not the DTD;
621
     // it doesn't signal for the DTD here
622
     processingDTD = false;
623
   }
624

    
625
   //
626
   // the next section implements the ErrorHandler interface
627
   //
628

    
629
   /**
630
    * SAX Handler that receives notification of fatal parsing errors
631
    */
632
   public void fatalError(SAXParseException exception) throws SAXException {
633
     MetaCatUtil.debugMessage("FATALERROR", 50);
634
     throw (new SAXException("Fatal processing error.", exception));
635
   }
636

    
637
   /**
638
    * SAX Handler that receives notification of recoverable parsing errors
639
    */
640
   public void error(SAXParseException exception) throws SAXException {
641
     MetaCatUtil.debugMessage("ERROR", 50);
642
     throw (new SAXException("Processing error.", exception));
643
   }
644

    
645
   /**
646
    * SAX Handler that receives notification of warnings
647
    */
648
   public void warning(SAXParseException exception) throws SAXException {
649
     MetaCatUtil.debugMessage("WARNING", 50);
650
     throw (new SAXException("Warning.", exception));
651
   }
652

    
653
   //
654
   // Helper, getter and setter methods
655
   //
656

    
657
   /**
658
    * get the document name
659
    */
660
   public String getDocname() {
661
     return docname;
662
   }
663

    
664
   /**
665
    * get the document processing state
666
    */
667
   public boolean processingDTD() {
668
     return processingDTD;
669
   }
670
}
(19-19/48)