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-08-08 12:56:35 -0700 (Wed, 08 Aug 2001) $'
12
 * '$Revision: 819 $'
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.Enumeration;
36
import java.util.EmptyStackException;
37

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

    
45
/** 
46
 * A database aware Class implementing callback bethods for the SAX parser to
47
 * call when processing the XML stream and generating events
48
 */
49
public class DBSAXHandler extends DefaultHandler 
50
                          implements LexicalHandler, DeclHandler, Runnable {
51

    
52
   private boolean	atFirstElement;
53
   private boolean	processingDTD;
54
   private String 	docname = null;
55
   private String 	doctype;
56
   private String 	systemid;
57
   private boolean 	stackCreated = false;
58
   private Stack 	  nodeStack;
59
   private Vector   nodeIndex;
60
   private Connection	  conn = null;
61
   private DocumentImpl currentDocument;
62
   private DBSAXNode    rootNode;
63
   private String   action = null;
64
   private String   docid = null;
65
   private String   user = null;
66
   private String[] groups = null;
67
   private String   pub = null;
68
   private Thread   xmlIndex;
69
   private boolean endDocument = false;
70
   private int serverCode = 1;
71

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

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

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

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

    
130
   /** SAX Handler that receives notification of end of the document */
131
   public void endDocument() throws SAXException {
132
     MetaCatUtil.debugMessage("end Document");
133
     // Starting new thread for writing XML Index.
134
     // It calls the run method of the thread.
135
     try {
136
       xmlIndex.start();
137
     } catch (NullPointerException e) {
138
       xmlIndex = null;
139
       throw new 
140
       SAXException("Problem with starting thread for writing XML Index. " +
141
                    e.getMessage());
142
     }
143
   }
144

    
145
   /** SAX Handler that is called at the start of each XML element */
146
   public void startElement(String uri, String localName,
147
                            String qName, Attributes atts) 
148
               throws SAXException {
149
     MetaCatUtil.debugMessage("Start ELEMENT " + localName);
150

    
151
     DBSAXNode parentNode = null;
152
     DBSAXNode currentNode = null;
153

    
154
     // Get a reference to the parent node for the id
155
     try {
156
       parentNode = (DBSAXNode)nodeStack.peek();
157
     } catch (EmptyStackException e) {
158
       parentNode = null;
159
     }
160

    
161
     // Document representation that points to the root document node
162
     if (atFirstElement) {
163
       atFirstElement = false;
164
       // If no DOCTYPE declaration: docname = root element name 
165
       if (docname == null) {
166
         docname = localName;
167
         doctype = docname;
168
         MetaCatUtil.debugMessage("DOCNAME-a: " + docname);
169
         MetaCatUtil.debugMessage("DOCTYPE-a: " + doctype);
170
       } else if (doctype == null) {
171
         doctype = docname;
172
         MetaCatUtil.debugMessage("DOCTYPE-b: " + doctype);
173
       }
174
       rootNode.writeNodename(docname);
175
       try {
176
         // for validated XML Documents store a reference to XML DB Catalog
177
         String catalogid = null;
178
         if ( systemid != null ) {
179
           Statement stmt = conn.createStatement();
180
           ResultSet rs = stmt.executeQuery(
181
                          "SELECT catalog_id FROM xml_catalog " +
182
                          "WHERE entry_type = 'DTD' " + 
183
                          "AND public_id = '" + doctype + "'");
184
           boolean hasRow = rs.next();
185
           if ( hasRow ) {
186
            catalogid = rs.getString(1);
187
           }
188
           stmt.close();
189
         }
190
         currentDocument = new DocumentImpl(conn, rootNode.getNodeID(), 
191
                               docname, doctype, docid, action, user, 
192
                               this.pub, catalogid, this.serverCode);
193
       } catch (Exception ane) {
194
         throw (new SAXException("Error in DBSaxHandler.startElement " + 
195
                                 action, ane));
196
       }
197
     }      
198

    
199
     // Create the current node representation
200
     currentNode = new DBSAXNode(conn, localName, parentNode,
201
                                 currentDocument.getRootNodeID(),docid,
202
                                 currentDocument.getDoctype());
203
                               
204
     // Add all of the attributes
205
     for (int i=0; i<atts.getLength(); i++) {
206
       currentNode.setAttribute(atts.getLocalName(i), atts.getValue(i), docid);
207
     }      
208

    
209
     // Add the node to the stack, so that any text data can be 
210
     // added as it is encountered
211
     nodeStack.push(currentNode);
212
     // Add the node to the vector used by thread for writing XML Index
213
     nodeIndex.addElement(currentNode);
214

    
215
  }
216
  
217
  /* The run method of xmlIndex thread. It writes XML Index for the document. */
218
  public void run () {
219
    DBSAXNode currNode = null;
220
    DBSAXNode prevNode = null;
221
    Connection dbconn = null;
222
    String doctype = currentDocument.getDoctype();
223
    int step = 0;
224
    int counter = 0;
225

    
226
    try {
227
      // Opening separate db connection for writing XML Index
228
      MetaCatUtil util = new MetaCatUtil();
229
      dbconn = util.openDBConnection();
230
      dbconn.setAutoCommit(false);
231

    
232
      // Going through the elements of the document and writing its Index
233
      Enumeration nodes = nodeIndex.elements();
234
      while ( nodes.hasMoreElements() ) {
235
        currNode = (DBSAXNode)nodes.nextElement();
236
        currNode.updateNodeIndex(dbconn, docid, doctype);
237
      }
238
    
239
      dbconn.commit();
240
         
241
      //if this is a package file
242
      if ( doctype.equals(util.getOption("packagedoctype")) )
243
      {
244
        // write the package info to xml_relation table
245
        RelationHandler rth = new RelationHandler(docid, dbconn);
246
        // from the relations get the access file id for that package
247
        String aclid = rth.getAccessFileID(docid);
248
        // if there are access file, write ACL for that package
249
        if ( aclid != null ) {
250
          runAccessControlList(dbconn, aclid);
251
        }
252
      }
253
      // if it is an access file
254
      else if ( doctype.equals(util.getOption("accessdoctype")) )
255
      {
256
        // write ACL for the package
257
        runAccessControlList(dbconn, docid);
258
      }
259
      
260
      dbconn.close();
261

    
262
    } catch (Exception e) {
263
      try {
264
        dbconn.rollback();
265
        dbconn.close();
266
      } catch (SQLException sqle) {}
267
      System.out.println("Error in DBSAXHandler.run " + e.getMessage());
268
      e.printStackTrace();
269
    }      
270
  }
271
  
272
  // It runs in xmlIndex thread. It writes ACL for a package.
273
  private void runAccessControlList (Connection conn, String aclid)
274
                                                throws Exception
275
  {
276
    // read the access file from xml_nodes
277
    // parse the access file and store the access info into xml_access
278
    AccessControlList aclobj = 
279
    new AccessControlList(conn, aclid, //new StringReader(xml),
280
                          user, groups, serverCode);
281
    conn.commit();
282
  }
283

    
284

    
285
  /** SAX Handler that is called for each XML text node */
286
  public void characters(char[] cbuf, int start, int len) throws SAXException {
287
     MetaCatUtil.debugMessage("CHARACTERS");
288
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
289
     String data = null;
290
     int leftover = len;
291
     int offset = start;
292
     boolean moredata = true;
293
    
294
     // This loop deals with the case where there are more characters 
295
     // than can fit in a single database text field (limit is 
296
     // MAXDATACHARS).  If the text to be inserted exceeds MAXDATACHARS,
297
     // write a series of nodes that are MAXDATACHARS long, and then the
298
     // final node contains the remainder
299
     while (moredata) {
300
       if (leftover > MAXDATACHARS) {
301
         data = new String(cbuf, offset, MAXDATACHARS);
302
         leftover -= MAXDATACHARS;
303
         offset += MAXDATACHARS;
304
       } else {
305
         data = new String(cbuf, offset, leftover);
306
         moredata = false;
307
       }
308

    
309
       // Write the content of the node to the database
310
       currentNode.writeChildNodeToDB("TEXT", null, data, docid);
311
     }
312

    
313
// DOCTITLE attr cleared from the db
314
//     // write the title of document if there are tag <title>
315
//     if ( currentNode.getTagName().equals("title") ) {
316
//       if ( leftover > MAXTITLELEN ) 
317
//         currentDocument.setTitle(new String(cbuf, start, MAXTITLELEN));
318
//       else
319
//         currentDocument.setTitle(new String(cbuf, start, leftover));
320
//     }
321
   }
322

    
323
   /** 
324
    * SAX Handler that is called for each XML text node that is
325
    * Ignorable white space
326
    */
327
   public void ignorableWhitespace(char[] cbuf, int start, int len)
328
               throws SAXException {
329
     // When validation is turned "on", white spaces are reported here
330
     // When validation is turned "off" white spaces are not reported here,
331
     // but through characters() callback
332
     MetaCatUtil.debugMessage("IGNORABLEWHITESPACE");
333
     //System.out.println("Whitespace:" + len + "x" + new String(cbuf, start, len) + "x");
334

    
335
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
336
     String data = null;
337
     int leftover = len;
338
     int offset = start;
339
     boolean moredata = true;
340
     
341
     // This loop deals with the case where there are more characters 
342
     // than can fit in a single database text field (limit is 
343
     // MAXDATACHARS).  If the text to be inserted exceeds MAXDATACHARS,
344
     // write a series of nodes that are MAXDATACHARS long, and then the
345
     // final node contains the remainder
346
     while (moredata) {
347
       if (leftover > MAXDATACHARS) {
348
         data = new String(cbuf, offset, MAXDATACHARS);
349
         leftover -= MAXDATACHARS;
350
         offset += MAXDATACHARS;
351
       } else {
352
         data = new String(cbuf, offset, leftover);
353
         moredata = false;
354
       }
355

    
356
       // Write the content of the node to the database
357
       currentNode.writeChildNodeToDB("TEXT", null, data, docid);
358
     }
359
   }
360

    
361
   /** 
362
    * SAX Handler called once for each processing instruction found: 
363
    * node that PI may occur before or after the root element.
364
    */
365
   public void processingInstruction(String target, String data) 
366
          throws SAXException {
367
     MetaCatUtil.debugMessage("PI");
368
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
369
     currentNode.writeChildNodeToDB("PI", target, data, docid);
370
   }
371

    
372
   /** SAX Handler that is called at the end of each XML element */
373
   public void endElement(String uri, String localName,
374
                          String qName) throws SAXException {
375
     MetaCatUtil.debugMessage("End ELEMENT " + localName);
376

    
377
     // Get the node from the stack
378
     DBSAXNode currentNode = (DBSAXNode)nodeStack.pop();
379
   }
380

    
381
   //
382
   // the next section implements the LexicalHandler interface
383
   //
384

    
385
   /** SAX Handler that receives notification of DOCTYPE. Sets the DTD */
386
   public void startDTD(String name, String publicId, String systemId) 
387
               throws SAXException {
388
     docname = name;
389
     doctype = publicId;
390
     systemid = systemId;
391

    
392
//System.out.println("Start DTD");
393
//System.out.println("DOCNAME: " + docname);
394
//System.out.println("DOCTYPE: " + doctype);
395
//System.out.println("  SYSID: " + systemid);
396

    
397
     MetaCatUtil.debugMessage("Start DTD");
398
     MetaCatUtil.debugMessage("DOCNAME: " + docname);
399
     MetaCatUtil.debugMessage("DOCTYPE: " + doctype);
400
     MetaCatUtil.debugMessage("  SYSID: " + systemid);
401
   }
402

    
403
   /** 
404
    * SAX Handler that receives notification of end of DTD 
405
    */
406
   public void endDTD() throws SAXException {
407
    
408
//System.out.println("end DTD");
409
     MetaCatUtil.debugMessage("end DTD");
410
   }
411

    
412
   /** 
413
    * SAX Handler that receives notification of comments in the DTD
414
    */
415
   public void comment(char[] ch, int start, int length) throws SAXException {
416
     MetaCatUtil.debugMessage("COMMENT");
417
     if ( !processingDTD ) {
418
       DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
419
       currentNode.writeChildNodeToDB("COMMENT", null, new String(ch), docid);
420
     }
421
   }
422

    
423
   /** 
424
    * SAX Handler that receives notification of the start of CDATA sections
425
    */
426
   public void startCDATA() throws SAXException {
427
     MetaCatUtil.debugMessage("start CDATA");
428
   }
429

    
430
   /** 
431
    * SAX Handler that receives notification of the end of CDATA sections
432
    */
433
   public void endCDATA() throws SAXException {
434
     MetaCatUtil.debugMessage("end CDATA");
435
   }
436

    
437
   /** 
438
    * SAX Handler that receives notification of the start of entities
439
    */
440
   public void startEntity(String name) throws SAXException {
441
     MetaCatUtil.debugMessage("start ENTITY: " + name);
442
//System.out.println("start ENTITY: " + name);
443
     if (name.equals("[dtd]")) {
444
       processingDTD = true;
445
     }
446
   }
447

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

    
459
   /** 
460
    * SAX Handler that receives notification of element declarations
461
    */
462
   public void elementDecl(String name, String model)
463
                        throws org.xml.sax.SAXException {
464
//System.out.println("ELEMENTDECL: " + name + " " + model);
465
     MetaCatUtil.debugMessage("ELEMENTDECL: " + name + " " + model);
466
   }
467

    
468
   /** 
469
    * SAX Handler that receives notification of attribute declarations
470
    */
471
   public void attributeDecl(String eName, String aName,
472
                        String type, String valueDefault, String value)
473
                        throws org.xml.sax.SAXException {
474
//System.out.println("ATTRIBUTEDECL: " + eName + " " 
475
//                        + aName + " " + type + " " + valueDefault + " "
476
//                        + value);
477
     MetaCatUtil.debugMessage("ATTRIBUTEDECL: " + eName + " " 
478
                        + aName + " " + type + " " + valueDefault + " "
479
                        + value);
480
   }
481

    
482
   /** 
483
    * SAX Handler that receives notification of internal entity declarations
484
    */
485
   public void internalEntityDecl(String name, String value)
486
                        throws org.xml.sax.SAXException {
487
//System.out.println("INTERNENTITYDECL: " + name + " " + value);
488
     MetaCatUtil.debugMessage("INTERNENTITYDECL: " + name + " " + value);
489
   }
490

    
491
   /** 
492
    * SAX Handler that receives notification of external entity declarations
493
    */
494
   public void externalEntityDecl(String name, String publicId,
495
                        String systemId)
496
                        throws org.xml.sax.SAXException {
497
//System.out.println("EXTERNENTITYDECL: " + name + " " + publicId 
498
//                              + " " + systemId);
499
     MetaCatUtil.debugMessage("EXTERNENTITYDECL: " + name + " " + publicId 
500
                              + " " + systemId);
501
   }
502

    
503
   //
504
   // the next section implements the ErrorHandler interface
505
   //
506

    
507
   /** 
508
    * SAX Handler that receives notification of fatal parsing errors
509
    */
510
   public void fatalError(SAXParseException exception) throws SAXException {
511
     MetaCatUtil.debugMessage("FATALERROR");
512
     throw (new SAXException("Fatal processing error.", exception));
513
   }
514

    
515
   /** 
516
    * SAX Handler that receives notification of recoverable parsing errors
517
    */
518
   public void error(SAXParseException exception) throws SAXException {
519
     MetaCatUtil.debugMessage("ERROR");
520
     throw (new SAXException("Processing error.", exception));
521
   }
522

    
523
   /** 
524
    * SAX Handler that receives notification of warnings
525
    */
526
   public void warning(SAXParseException exception) throws SAXException {
527
     MetaCatUtil.debugMessage("WARNING");
528
     throw (new SAXException("Warning.", exception));
529
   }
530

    
531
   // 
532
   // Helper, getter and setter methods
533
   //
534
   
535
   /**
536
    * get the document name
537
    */
538
   public String getDocname() {
539
     return docname;
540
   }
541

    
542
   /**
543
    * get the document processing state
544
    */
545
   public boolean processingDTD() {
546
     return processingDTD;
547
   }
548
}
(15-15/40)