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: berkley $'
11
 *     '$Date: 2001-01-18 15:15:21 -0800 (Thu, 18 Jan 2001) $'
12
 * '$Revision: 675 $'
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   group = null;
67
   private Thread   xmlIndex;
68
   private boolean endDocument = false;
69
   private int serverCode = 1;
70

    
71
   private static final int MAXDATACHARS = 4000;
72
   private static final int MAXTITLELEN = 1000;
73

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

    
83
     // Create the stack for keeping track of node context
84
     // if it doesn't already exist
85
     if (!stackCreated) {
86
       nodeStack = new Stack();
87
       nodeIndex = new Vector();
88
       stackCreated = true;
89
     }
90
   }
91
   
92
   public DBSAXHandler(Connection conn, String action, String docid,
93
                       String user, String group, int serverCode)
94
   {
95
     this(conn);
96
     this.action = action;
97
     this.docid = docid;
98
     this.user = user;
99
     this.group = group;
100
     this.xmlIndex = new Thread(this);
101
     this.serverCode = serverCode;
102
   }
103
 
104
   /** Construct an instance of the handler class 
105
    *
106
    * @param conn the JDBC connection to which information is written
107
    * @param action - "INSERT" or "UPDATE"
108
    * @param docid to be inserted or updated into JDBC connection
109
    * @param user the user connected to MetaCat servlet
110
    */
111
   public DBSAXHandler(Connection conn,String action,String docid,
112
                       String user, String group)
113
   {
114
     this(conn);
115
     this.action = action;
116
     this.docid = docid;
117
     this.user = user;
118
     this.group = group;
119
     this.xmlIndex = new Thread(this);
120
     //this.xmlIndex.setPriority(Thread.MIN_PRIORITY);
121
   }
122

    
123
   /** SAX Handler that receives notification of beginning of the document */
124
   public void startDocument() throws SAXException {
125
     MetaCatUtil.debugMessage("start Document");
126

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

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

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

    
155
     DBSAXNode parentNode = null;
156
     DBSAXNode currentNode = null;
157

    
158
     // Get a reference to the parent node for the id
159
     try {
160
       parentNode = (DBSAXNode)nodeStack.peek();
161
     } catch (EmptyStackException e) {
162
       parentNode = null;
163
     }
164

    
165
     // Document representation that points to the root document node
166
     if (atFirstElement) {
167
       atFirstElement = false;
168
       // If no DOCTYPE declaration: docname = root element name 
169
       if (docname == null) {
170
         docname = localName;
171
         doctype = docname;
172
         MetaCatUtil.debugMessage("DOCNAME-a: " + docname);
173
         MetaCatUtil.debugMessage("DOCTYPE-a: " + doctype);
174
       } else if (doctype == null) {
175
         doctype = docname;
176
         MetaCatUtil.debugMessage("DOCTYPE-b: " + doctype);
177
       }
178
       rootNode.writeNodename(docname);
179
       try {
180
         currentDocument = new DocumentImpl(conn, rootNode.getNodeID(), 
181
                                       docname, doctype, docid, action, user, 
182
                                       this.serverCode);
183
       } catch (Exception ane) {
184
         throw (new SAXException("Error in DBSaxHandler.startElement " + 
185
                                 action, ane));
186
       }
187
       // not needed any more
188
       //rootNode.writeDocID(currentDocument.getDocID());
189
     }      
190

    
191
     // Create the current node representation
192
     currentNode = new DBSAXNode(conn, localName, parentNode,
193
                                 currentDocument.getRootNodeID(),docid,
194
                                 currentDocument.getDoctype());
195
                               
196
     // Add all of the attributes
197
     for (int i=0; i<atts.getLength(); i++) {
198
       currentNode.setAttribute(atts.getLocalName(i), atts.getValue(i), docid);
199
     }      
200

    
201
     // Add the node to the stack, so that any text data can be 
202
     // added as it is encountered
203
     nodeStack.push(currentNode);
204
     // Add the node to the vector used by thread for writing XML Index
205
     nodeIndex.addElement(currentNode);
206

    
207
  }
208
  
209
  // The run method of xmlIndex thread. It writes XML Index for the document.
210
  public void run () {
211
    DBSAXNode currNode = null;
212
    DBSAXNode prevNode = null;
213
    Connection dbconn = null;
214
    String doctype = currentDocument.getDoctype();
215
    int step = 0;
216
    int counter = 0;
217

    
218
    try {
219
      // Opening separate db connection for writing XML Index
220
      MetaCatUtil util = new MetaCatUtil();
221
      dbconn = util.openDBConnection();
222
      dbconn.setAutoCommit(false);
223

    
224
      // Going through the elements of the document and writing its Index
225
      Enumeration nodes = nodeIndex.elements();
226
      while ( nodes.hasMoreElements() ) {
227
        currNode = (DBSAXNode)nodes.nextElement();
228
        currNode.updateNodeIndex(dbconn, docid, doctype);
229
      }
230
    
231
      dbconn.commit();
232
         
233
      //if this is a package file then write the package info to 
234
      //the xml_relation table. RelationHandler checks to see
235
      //if it is a package file so you don't have to do it here.
236
      if ( doctype.equals(util.getOption("packagedoctype")) )
237
      {
238
        DocumentImpl xmldoc = new DocumentImpl(dbconn, docid);
239
        RelationHandler rth = new RelationHandler(xmldoc, dbconn);
240
      } 
241
      else if ( doctype.equals(util.getOption("accessdoctype")) ) 
242
      {
243
        DocumentImpl xmldoc = new DocumentImpl(dbconn, docid);
244
        String xml = xmldoc.toString();
245
        try {
246
          AccessControlList aclobj = 
247
          new AccessControlList(dbconn, docid, new StringReader(xml),
248
                                user, group);
249
          dbconn.commit();
250
        } catch (SAXException e) {
251
          try {
252
            dbconn.rollback();
253
            dbconn.close();
254
          } catch (SQLException sqle) {}
255
          System.out.println("Error writing ACL in DBSaxHandler.run " + 
256
                             e.getMessage()); 
257
        }
258
      }
259
      
260
      
261
      dbconn.close();
262

    
263
    } catch (Exception e) {
264
      try {
265
        dbconn.rollback();
266
        dbconn.close();
267
      } catch (SQLException sqle) {}
268
      System.out.println("Error writing XML Index in DBSaxHandler.run " + 
269
                         e.getMessage()); 
270
    }      
271
  }
272

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

    
297
       // Write the content of the node to the database
298
       currentNode.writeChildNodeToDB("TEXT", null, data, docid);
299
     }
300

    
301
     // write the title of document if there are tag <title>
302
     if ( currentNode.getTagName().equals("title") ) {
303
       if ( leftover > MAXTITLELEN ) 
304
         currentDocument.setTitle(new String(cbuf, start, MAXTITLELEN));
305
       else
306
         currentDocument.setTitle(new String(cbuf, start, leftover));
307
     }
308
   }
309

    
310
   /** 
311
    * SAX Handler that is called for each XML text node that is
312
    * Ignorable white space
313
    */
314
   public void ignorableWhitespace(char[] cbuf, int start, int len)
315
               throws SAXException {
316
     // When validation is turned "on", white spaces are reported here
317
     // When validation is turned "off" white spaces are not reported here,
318
     // but through characters() callback
319
     MetaCatUtil.debugMessage("IGNORABLEWHITESPACE");
320
     //System.out.println("Whitespace:" + len + "x" + new String(cbuf, start, len) + "x");
321

    
322
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
323
     String data = null;
324
     int leftover = len;
325
     int offset = start;
326
     boolean moredata = true;
327
     
328
     // This loop deals with the case where there are more characters 
329
     // than can fit in a single database text field (limit is 
330
     // MAXDATACHARS).  If the text to be inserted exceeds MAXDATACHARS,
331
     // write a series of nodes that are MAXDATACHARS long, and then the
332
     // final node contains the remainder
333
     while (moredata) {
334
       if (leftover > MAXDATACHARS) {
335
         data = new String(cbuf, offset, MAXDATACHARS);
336
         leftover -= MAXDATACHARS;
337
         offset += MAXDATACHARS;
338
       } else {
339
         data = new String(cbuf, offset, leftover);
340
         moredata = false;
341
       }
342

    
343
       // Write the content of the node to the database
344
       currentNode.writeChildNodeToDB("TEXT", null, data, docid);
345
     }
346
   }
347

    
348
   /** 
349
    * SAX Handler called once for each processing instruction found: 
350
    * node that PI may occur before or after the root element.
351
    */
352
   public void processingInstruction(String target, String data) 
353
          throws SAXException {
354
     MetaCatUtil.debugMessage("PI");
355
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
356
     currentNode.writeChildNodeToDB("PI", target, data, docid);
357
   }
358

    
359
   /** SAX Handler that is called at the end of each XML element */
360
   public void endElement(String uri, String localName,
361
                          String qName) throws SAXException {
362
     MetaCatUtil.debugMessage("End ELEMENT " + localName);
363

    
364
     // Get the node from the stack
365
     DBSAXNode currentNode = (DBSAXNode)nodeStack.pop();
366
   }
367

    
368
   //
369
   // the next section implements the LexicalHandler interface
370
   //
371

    
372
   /** SAX Handler that receives notification of DOCTYPE. Sets the DTD */
373
   public void startDTD(String name, String publicId, String systemId) 
374
               throws SAXException {
375
     docname = name;
376
     doctype = publicId;
377
     systemid = systemId;
378

    
379
     MetaCatUtil.debugMessage("Start DTD");
380
     MetaCatUtil.debugMessage("DOCNAME: " + docname);
381
     MetaCatUtil.debugMessage("DOCTYPE: " + doctype);
382
     MetaCatUtil.debugMessage("  SYSID: " + systemid);
383
   }
384

    
385
   /** 
386
    * SAX Handler that receives notification of end of DTD 
387
    */
388
   public void endDTD() throws SAXException {
389
     MetaCatUtil.debugMessage("end DTD");
390
   }
391

    
392
   /** 
393
    * SAX Handler that receives notification of comments in the DTD
394
    */
395
   public void comment(char[] ch, int start, int length) throws SAXException {
396
     MetaCatUtil.debugMessage("COMMENT");
397
     if ( !processingDTD ) {
398
       DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
399
       currentNode.writeChildNodeToDB("COMMENT", null, new String(ch), docid);
400
     }
401
   }
402

    
403
   /** 
404
    * SAX Handler that receives notification of the start of CDATA sections
405
    */
406
   public void startCDATA() throws SAXException {
407
     MetaCatUtil.debugMessage("start CDATA");
408
   }
409

    
410
   /** 
411
    * SAX Handler that receives notification of the end of CDATA sections
412
    */
413
   public void endCDATA() throws SAXException {
414
     MetaCatUtil.debugMessage("end CDATA");
415
   }
416

    
417
   /** 
418
    * SAX Handler that receives notification of the start of entities
419
    */
420
   public void startEntity(String name) throws SAXException {
421
     MetaCatUtil.debugMessage("start ENTITY: " + name);
422
     if (name.equals("[dtd]")) {
423
       processingDTD = true;
424
     }
425
   }
426

    
427
   /** 
428
    * SAX Handler that receives notification of the end of entities
429
    */
430
   public void endEntity(String name) throws SAXException {
431
     MetaCatUtil.debugMessage("end ENTITY: " + name);
432
     if (name.equals("[dtd]")) {
433
       processingDTD = false;
434
     }
435
   }
436

    
437
   /** 
438
    * SAX Handler that receives notification of element declarations
439
    */
440
   public void elementDecl(String name, String model)
441
                        throws org.xml.sax.SAXException {
442
     MetaCatUtil.debugMessage("ELEMENTDECL: " + name + " " + model);
443
   }
444

    
445
   /** 
446
    * SAX Handler that receives notification of attribute declarations
447
    */
448
   public void attributeDecl(String eName, String aName,
449
                        String type, String valueDefault, String value)
450
                        throws org.xml.sax.SAXException {
451
     MetaCatUtil.debugMessage("ATTRIBUTEDECL: " + eName + " " 
452
                        + aName + " " + type + " " + valueDefault + " "
453
                        + value);
454
   }
455

    
456
   /** 
457
    * SAX Handler that receives notification of internal entity declarations
458
    */
459
   public void internalEntityDecl(String name, String value)
460
                        throws org.xml.sax.SAXException {
461
     MetaCatUtil.debugMessage("INTERNENTITYDECL: " + name + " " + value);
462
   }
463

    
464
   /** 
465
    * SAX Handler that receives notification of external entity declarations
466
    */
467
   public void externalEntityDecl(String name, String publicId,
468
                        String systemId)
469
                        throws org.xml.sax.SAXException {
470
     MetaCatUtil.debugMessage("EXTERNENTITYDECL: " + name + " " + publicId 
471
                              + " " + systemId);
472
   }
473

    
474
   //
475
   // the next section implements the ErrorHandler interface
476
   //
477

    
478
   /** 
479
    * SAX Handler that receives notification of fatal parsing errors
480
    */
481
   public void fatalError(SAXParseException exception) throws SAXException {
482
     MetaCatUtil.debugMessage("FATALERROR");
483
     throw (new SAXException("Fatal processing error.", exception));
484
   }
485

    
486
   /** 
487
    * SAX Handler that receives notification of recoverable parsing errors
488
    */
489
   public void error(SAXParseException exception) throws SAXException {
490
     MetaCatUtil.debugMessage("ERROR");
491
     throw (new SAXException("Processing error.", exception));
492
   }
493

    
494
   /** 
495
    * SAX Handler that receives notification of warnings
496
    */
497
   public void warning(SAXParseException exception) throws SAXException {
498
     MetaCatUtil.debugMessage("WARNING");
499
     throw (new SAXException("Warning.", exception));
500
   }
501

    
502
   // 
503
   // Helper, getter and setter methods
504
   //
505
   
506
   /**
507
    * get the document name
508
    */
509
   public String getDocname() {
510
     return docname;
511
   }
512

    
513
   /**
514
    * get the document processing state
515
    */
516
   public boolean processingDTD() {
517
     return processingDTD;
518
   }
519
}
(15-15/43)