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: bojilova $'
11
 *     '$Date: 2001-09-12 14:49:26 -0700 (Wed, 12 Sep 2001) $'
12
 * '$Revision: 826 $'
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 Connection	  conn = null;
62
   private DocumentImpl currentDocument;
63
   private DBSAXNode    rootNode;
64
   private String   action = null;
65
   private String   docid = null;
66
   private String   user = null;
67
   private String[] groups = null;
68
   private String   pub = null;
69
   private Thread   xmlIndex;
70
   private boolean endDocument = false;
71
   private int serverCode = 1;
72
   private Hashtable namespaces = new Hashtable();
73

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

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

    
87
     // Create the stack for keeping track of node context
88
     // if it doesn't already exist
89
     if (!stackCreated) {
90
       nodeStack = new Stack();
91
       nodeIndex = new Vector();
92
       stackCreated = true;
93
     }
94
   }
95
   
96
   /** Construct an instance of the handler class 
97
    *
98
    * @param conn the JDBC connection to which information is written
99
    * @param action - "INSERT" or "UPDATE"
100
    * @param docid to be inserted or updated into JDBC connection
101
    * @param user the user connected to MetaCat servlet and owns the document
102
    * @param groups the groups to which user belongs
103
    * @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
    */
108
   public DBSAXHandler(Connection conn, String action, String docid,
109
                       String user, String[] groups, String pub, int serverCode)
110
   {
111
     this(conn);
112
     this.action = action;
113
     this.docid = docid;
114
     this.user = user;
115
     this.groups = groups;
116
     this.pub = pub;
117
     this.serverCode = serverCode;
118
     this.xmlIndex = new Thread(this);
119
   }
120
 
121
   /** SAX Handler that receives notification of beginning of the document */
122
   public void startDocument() throws SAXException {
123
     MetaCatUtil.debugMessage("start Document");
124

    
125
     // Create the document node representation as root
126
     rootNode = new DBSAXNode(conn, this.docid);
127
     // Add the node to the stack, so that any text data can be 
128
     // added as it is encountered
129
     nodeStack.push(rootNode);
130
   }
131

    
132
   /** SAX Handler that receives notification of end of the document */
133
   public void endDocument() throws SAXException {
134
     MetaCatUtil.debugMessage("end Document");
135
     // 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
   }
146

    
147
   /** 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
   /** 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
     MetaCatUtil.debugMessage("Start ELEMENT " + qName);
161
System.out.println("Start ELEMENT " + uri);
162
System.out.println("Start ELEMENT " + qName);
163
System.out.println("Start ELEMENT " + localName);
164
 
165
     DBSAXNode parentNode = null;
166
     DBSAXNode currentNode = null;
167

    
168
     // Get a reference to the parent node for the id
169
     try {
170
       parentNode = (DBSAXNode)nodeStack.peek();
171
     } catch (EmptyStackException e) {
172
       parentNode = null;
173
     }
174

    
175
     // Document representation that points to the root document node
176
     if (atFirstElement) {
177
       atFirstElement = false;
178
       // If no DOCTYPE declaration: docname = root element name 
179
       if (docname == null) {
180
         docname = localName;
181
         doctype = docname;
182
         MetaCatUtil.debugMessage("DOCNAME-a: " + docname);
183
         MetaCatUtil.debugMessage("DOCTYPE-a: " + doctype);
184
       } else if (doctype == null) {
185
         doctype = docname;
186
         MetaCatUtil.debugMessage("DOCTYPE-b: " + doctype);
187
       }
188
       rootNode.writeNodename(docname);
189
       try {
190
         // 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
         currentDocument = new DocumentImpl(conn, rootNode.getNodeID(), 
205
                               docname, doctype, docid, action, user, 
206
                               this.pub, catalogid, this.serverCode);
207
       } catch (Exception ane) {
208
         throw (new SAXException("Error in DBSaxHandler.startElement " + 
209
                                 action, ane));
210
       }
211
     }      
212

    
213
     // Create the current node representation
214
     currentNode = new DBSAXNode(conn, qName, localName, parentNode,
215
                                 currentDocument.getRootNodeID(),docid,
216
                                 currentDocument.getDoctype());
217
                               
218
     // 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
     // Add all of the attributes
231
     for (int i=0; i<atts.getLength(); i++) {
232
       currentNode.setAttribute(atts.getQName(i), atts.getValue(i), docid);
233
     }      
234

    
235
     // Add the node to the stack, so that any text data can be 
236
     // added as it is encountered
237
     nodeStack.push(currentNode);
238
     // Add the node to the vector used by thread for writing XML Index
239
     nodeIndex.addElement(currentNode);
240

    
241
  }
242
  
243
  /* The run method of xmlIndex thread. It writes XML Index for the document. */
244
  public void run () {
245
    DBSAXNode currNode = null;
246
    DBSAXNode prevNode = null;
247
    Connection dbconn = null;
248
    String doctype = currentDocument.getDoctype();
249
    int step = 0;
250
    int counter = 0;
251

    
252
    try {
253
      // Opening separate db connection for writing XML Index
254
      MetaCatUtil util = new MetaCatUtil();
255
      dbconn = util.openDBConnection();
256
      dbconn.setAutoCommit(false);
257

    
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
        currNode.updateNodeIndex(dbconn, docid, doctype);
263
      }
264
    
265
      dbconn.commit();
266
         
267
      //if this is a package file
268
      if ( doctype.equals(util.getOption("packagedoctype")) )
269
      {
270
        // write the package info to xml_relation table
271
        RelationHandler rth = new RelationHandler(docid, dbconn);
272
        // 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
        }
278
      }
279
      // 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
      
286
      dbconn.close();
287

    
288
    } catch (Exception e) {
289
      try {
290
        dbconn.rollback();
291
        dbconn.close();
292
      } catch (SQLException sqle) {}
293
      System.out.println("Error in DBSAXHandler.run " + e.getMessage());
294
      e.printStackTrace();
295
    }      
296
  }
297
  
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

    
310

    
311
  /** SAX Handler that is called for each XML text node */
312
  public void characters(char[] cbuf, int start, int len) throws SAXException {
313
     MetaCatUtil.debugMessage("CHARACTERS");
314
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
315
     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

    
335
       // Write the content of the node to the database
336
       currentNode.writeChildNodeToDB("TEXT", null, data, docid);
337
     }
338
   }
339

    
340
   /** 
341
    * SAX Handler that is called for each XML text node that is
342
    * Ignorable white space
343
    */
344
   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
     MetaCatUtil.debugMessage("IGNORABLEWHITESPACE");
350
     //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
   }
377

    
378
   /** 
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
     MetaCatUtil.debugMessage("PI");
385
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
386
     currentNode.writeChildNodeToDB("PI", target, data, docid);
387
   }
388

    
389
   /** SAX Handler that is called at the end of each XML element */
390
   public void endElement(String uri, String localName,
391
                          String qName) throws SAXException {
392
     MetaCatUtil.debugMessage("End ELEMENT " + qName);
393

    
394
     // Get the node from the stack
395
     DBSAXNode currentNode = (DBSAXNode)nodeStack.pop();
396
   }
397

    
398
   //
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
     MetaCatUtil.debugMessage("Start DTD");
410
     MetaCatUtil.debugMessage("DOCNAME: " + docname);
411
     MetaCatUtil.debugMessage("DOCTYPE: " + doctype);
412
     MetaCatUtil.debugMessage("  SYSID: " + systemid);
413
   }
414

    
415
   /** 
416
    * SAX Handler that receives notification of end of DTD 
417
    */
418
   public void endDTD() throws SAXException {
419
    
420
     MetaCatUtil.debugMessage("end DTD");
421
   }
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
     MetaCatUtil.debugMessage("COMMENT");
428
     if ( !processingDTD ) {
429
       DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
430
       currentNode.writeChildNodeToDB("COMMENT", null, new String(ch), docid);
431
     }
432
   }
433

    
434
   /** 
435
    * SAX Handler that receives notification of the start of CDATA sections
436
    */
437
   public void startCDATA() throws SAXException {
438
     MetaCatUtil.debugMessage("start CDATA");
439
   }
440

    
441
   /** 
442
    * SAX Handler that receives notification of the end of CDATA sections
443
    */
444
   public void endCDATA() throws SAXException {
445
     MetaCatUtil.debugMessage("end CDATA");
446
   }
447

    
448
   /** 
449
    * SAX Handler that receives notification of the start of entities
450
    */
451
   public void startEntity(String name) throws SAXException {
452
     MetaCatUtil.debugMessage("start ENTITY: " + name);
453
//System.out.println("start ENTITY: " + name);
454
     if (name.equals("[dtd]")) {
455
       processingDTD = true;
456
     }
457
   }
458

    
459
   /** 
460
    * SAX Handler that receives notification of the end of entities
461
    */
462
   public void endEntity(String name) throws SAXException {
463
     MetaCatUtil.debugMessage("end ENTITY: " + name);
464
//System.out.println("end ENTITY: " + name);
465
     if (name.equals("[dtd]")) {
466
       processingDTD = false;
467
     }
468
   }
469

    
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
//System.out.println("ELEMENTDECL: " + name + " " + model);
476
     MetaCatUtil.debugMessage("ELEMENTDECL: " + name + " " + model);
477
   }
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

    
486
//System.out.println("ATTRIBUTEDECL: " + eName + " " 
487
//                        + aName + " " + type + " " + valueDefault + " "
488
//                        + value);
489
     MetaCatUtil.debugMessage("ATTRIBUTEDECL: " + eName + " " 
490
                        + aName + " " + type + " " + valueDefault + " "
491
                        + value);
492
   }
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
//System.out.println("INTERNENTITYDECL: " + name + " " + value);
500
     MetaCatUtil.debugMessage("INTERNENTITYDECL: " + name + " " + value);
501
   }
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
//System.out.println("EXTERNENTITYDECL: " + name + " " + publicId 
510
//                              + " " + systemId);
511
     MetaCatUtil.debugMessage("EXTERNENTITYDECL: " + name + " " + publicId 
512
                              + " " + systemId);
513
   }
514

    
515
   //
516
   // the next section implements the ErrorHandler interface
517
   //
518

    
519
   /** 
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
     throw (new SAXException("Processing error.", exception));
533
   }
534

    
535
   /** 
536
    * SAX Handler that receives notification of warnings
537
    */
538
   public void warning(SAXParseException exception) throws SAXException {
539
     MetaCatUtil.debugMessage("WARNING");
540
     throw (new SAXException("Warning.", exception));
541
   }
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
   public boolean processingDTD() {
558
     return processingDTD;
559
   }
560
}
(15-15/40)