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: 2000-11-16 15:26:00 -0800 (Thu, 16 Nov 2000) $'
12
 * '$Revision: 549 $'
13
 */
14

    
15
package edu.ucsb.nceas.metacat;
16

    
17
import java.sql.*;
18
import java.util.Stack;
19
import java.util.Vector;
20
import java.util.Enumeration;
21
import java.util.EmptyStackException;
22

    
23
import org.xml.sax.Attributes;
24
import org.xml.sax.SAXException;
25
import org.xml.sax.SAXParseException;
26
import org.xml.sax.ext.DeclHandler;
27
import org.xml.sax.ext.LexicalHandler;
28
import org.xml.sax.helpers.DefaultHandler;
29

    
30
/** 
31
 * A database aware Class implementing callback bethods for the SAX parser to
32
 * call when processing the XML stream and generating events
33
 */
34
public class DBSAXHandler extends DefaultHandler 
35
                          implements LexicalHandler, DeclHandler, Runnable {
36

    
37
   private boolean	atFirstElement;
38
   private boolean	processingDTD;
39
   private String 	docname = null;
40
   private String 	doctype;
41
   private String 	systemid;
42
   private boolean 	stackCreated = false;
43
   private Stack 	  nodeStack;
44
   private Vector   nodeIndex;
45
   private Connection	  conn = null;
46
   private DocumentImpl currentDocument;
47
   private DBSAXNode    rootNode;
48
   private String   action = null;
49
   private String   docid = null;
50
   private String   user = null;
51
   private Thread   xmlIndex;
52
   private boolean endDocument = false;
53
   private int serverCode = 1;
54

    
55
   private static final int MAXDATACHARS = 4000;
56
   private static final int MAXTITLELEN = 1000;
57

    
58
   /** Construct an instance of the handler class 
59
    *
60
    * @param conn the JDBC connection to which information is written
61
    */
62
   public DBSAXHandler(Connection conn) {
63
     this.conn = conn;
64
     this.atFirstElement = true;
65
     this.processingDTD = false;
66

    
67
     // Create the stack for keeping track of node context
68
     // if it doesn't already exist
69
     if (!stackCreated) {
70
       nodeStack = new Stack();
71
       nodeIndex = new Vector();
72
       stackCreated = true;
73
     }
74
   }
75
   
76
   public DBSAXHandler(Connection conn,String action,String docid,String user,
77
                       int serverCode)
78
   {
79
     this(conn);
80
     this.action = action;
81
     this.docid = docid;
82
     this.user = user;
83
     this.xmlIndex = new Thread(this);
84
     this.serverCode = serverCode;
85
   }
86
 
87
   /** Construct an instance of the handler class 
88
    *
89
    * @param conn the JDBC connection to which information is written
90
    * @param action - "INSERT" or "UPDATE"
91
    * @param docid to be inserted or updated into JDBC connection
92
    * @param user the user connected to MetaCat servlet
93
    */
94
   public DBSAXHandler(Connection conn,String action,String docid,String user)
95
   {
96
     this(conn);
97
     this.action = action;
98
     this.docid = docid;
99
     this.user = user;
100
     this.xmlIndex = new Thread(this);
101
     //this.xmlIndex.setPriority(Thread.MIN_PRIORITY);
102
   }
103

    
104
   /** SAX Handler that receives notification of beginning of the document */
105
   public void startDocument() throws SAXException {
106
     MetaCatUtil.debugMessage("start Document");
107

    
108
     // Create the document node representation as root
109
     rootNode = new DBSAXNode(conn, this.docid);
110
     // Add the node to the stack, so that any text data can be 
111
     // added as it is encountered
112
     nodeStack.push(rootNode);
113
   }
114

    
115
   /** SAX Handler that receives notification of end of the document */
116
   public void endDocument() throws SAXException {
117
     MetaCatUtil.debugMessage("end Document");
118
     // Starting new thread for writing XML Index.
119
     // It calls the run method of the thread.
120
     try {
121
       xmlIndex.start();
122
     } catch (NullPointerException e) {
123
       xmlIndex = null;
124
       throw new 
125
       SAXException("Problem with starting thread for writing XML Index. " +
126
                    e.getMessage());
127
     }
128
   }
129

    
130
   /** SAX Handler that is called at the start of each XML element */
131
   public void startElement(String uri, String localName,
132
                            String qName, Attributes atts) 
133
               throws SAXException {
134
     MetaCatUtil.debugMessage("Start ELEMENT " + localName);
135

    
136
     DBSAXNode parentNode = null;
137
     DBSAXNode currentNode = null;
138

    
139
     // Get a reference to the parent node for the id
140
     try {
141
       parentNode = (DBSAXNode)nodeStack.peek();
142
     } catch (EmptyStackException e) {
143
       parentNode = null;
144
     }
145

    
146
     // Document representation that points to the root document node
147
     if (atFirstElement) {
148
       atFirstElement = false;
149
       // If no DOCTYPE declaration: docname = root element name 
150
       if (docname == null) {
151
         docname = localName;
152
         doctype = docname;
153
         MetaCatUtil.debugMessage("DOCNAME-a: " + docname);
154
         MetaCatUtil.debugMessage("DOCTYPE-a: " + doctype);
155
       } else if (doctype == null) {
156
         doctype = docname;
157
         MetaCatUtil.debugMessage("DOCTYPE-b: " + doctype);
158
       }
159
       rootNode.writeNodename(docname);
160
       try {
161
         currentDocument = new DocumentImpl(conn, rootNode.getNodeID(), 
162
                                       docname, doctype, docid, action, user, 
163
                                       this.serverCode);
164
       } catch (Exception ane) {
165
         throw (new SAXException("Error with " + action, ane));
166
       }
167
       // not needed any more
168
       //rootNode.writeDocID(currentDocument.getDocID());
169
     }      
170

    
171
     // Create the current node representation
172
     currentNode = new DBSAXNode(conn, localName, parentNode,
173
                                 currentDocument.getRootNodeID(),docid,
174
                                 currentDocument.getDoctype());
175
                               
176
     // Add all of the attributes
177
     for (int i=0; i<atts.getLength(); i++) {
178
       currentNode.setAttribute(atts.getLocalName(i), atts.getValue(i), docid);
179
     }      
180

    
181
     // Add the node to the stack, so that any text data can be 
182
     // added as it is encountered
183
     nodeStack.push(currentNode);
184
     // Add the node to the vector used by thread for writing XML Index
185
     nodeIndex.addElement(currentNode);
186

    
187
  }
188
  
189
  // The run method of xmlIndex thread. It writes XML Index for the document.
190
  public void run () {
191
    DBSAXNode currNode = null;
192
    DBSAXNode prevNode = null;
193
    Connection dbconn = null;
194
    int step = 0;
195
    int counter = 0;
196

    
197
    try {
198
      // Opening separate db connection for writing XML Index
199
      MetaCatUtil util = new MetaCatUtil();
200
      dbconn = util.openDBConnection();
201
      dbconn.setAutoCommit(false);
202

    
203
      // Going through the elements of the document and writing its Index
204
      Enumeration nodes = nodeIndex.elements();
205
      while ( nodes.hasMoreElements() ) {
206
        currNode = (DBSAXNode)nodes.nextElement();
207
        currNode.updateNodeIndex(dbconn, docid, currentDocument.getDoctype());
208
      }
209
    
210
      dbconn.commit();
211
      
212
      //if this is a package file then write the package info to 
213
      //the xml_relation table. RelationHandler checks to see
214
      //if it is a package file so you don't have to do it here.
215
      DocumentImpl xmldoc = new DocumentImpl(dbconn, docid);
216
      RelationHandler rth = new RelationHandler(xmldoc, dbconn);
217
      
218
      dbconn.close();
219

    
220
    } catch (Exception e) {
221
      try {
222
        dbconn.rollback();
223
        dbconn.close();
224
      } catch (SQLException sqle) {}
225
      System.out.println("Error writing XML Index. " + e.getMessage()); 
226
    }      
227
  }
228

    
229
  /** SAX Handler that is called for each XML text node */
230
  public void characters(char[] cbuf, int start, int len) throws SAXException {
231
     MetaCatUtil.debugMessage("CHARACTERS");
232
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
233
     String data = null;
234
     int leftover = len;
235
     int offset = start;
236
     boolean moredata = true;
237
    
238
     // This loop deals with the case where there are more characters 
239
     // than can fit in a single database text field (limit is 
240
     // MAXDATACHARS).  If the text to be inserted exceeds MAXDATACHARS,
241
     // write a series of nodes that are MAXDATACHARS long, and then the
242
     // final node contains the remainder
243
     while (moredata) {
244
       if (leftover > MAXDATACHARS) {
245
         data = new String(cbuf, offset, MAXDATACHARS);
246
         leftover -= MAXDATACHARS;
247
         offset += MAXDATACHARS;
248
       } else {
249
         data = new String(cbuf, offset, leftover);
250
         moredata = false;
251
       }
252

    
253
       // Write the content of the node to the database
254
       currentNode.writeChildNodeToDB("TEXT", null, data, docid);
255
     }
256

    
257
     // write the title of document if there are tag <title>
258
     if ( currentNode.getTagName().equals("title") ) {
259
       if ( leftover > MAXTITLELEN ) 
260
         currentDocument.setTitle(new String(cbuf, start, MAXTITLELEN));
261
       else
262
         currentDocument.setTitle(new String(cbuf, start, leftover));
263
     }
264
   }
265

    
266
   /** 
267
    * SAX Handler that is called for each XML text node that is Ignorable
268
    * white space
269
    */
270
   public void ignorableWhitespace(char[] cbuf, int start, int len) {
271
     MetaCatUtil.debugMessage("IGNORABLEWHITESPACE");
272
   }
273

    
274
   /** 
275
    * SAX Handler called once for each processing instruction found: 
276
    * node that PI may occur before or after the root element.
277
    */
278
   public void processingInstruction(String target, String data) 
279
          throws SAXException {
280
     MetaCatUtil.debugMessage("PI");
281
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
282
     currentNode.writeChildNodeToDB("PI", target, data, docid);
283
   }
284

    
285
   /** SAX Handler that is called at the end of each XML element */
286
   public void endElement(String uri, String localName,
287
                          String qName) throws SAXException {
288
     MetaCatUtil.debugMessage("End ELEMENT " + localName);
289

    
290
     // Get the node from the stack
291
     DBSAXNode currentNode = (DBSAXNode)nodeStack.pop();
292
   }
293

    
294
   //
295
   // the next section implements the LexicalHandler interface
296
   //
297

    
298
   /** SAX Handler that receives notification of DOCTYPE. Sets the DTD */
299
   public void startDTD(String name, String publicId, String systemId) 
300
               throws SAXException {
301
     docname = name;
302
     doctype = publicId;
303
     systemid = systemId;
304

    
305
     MetaCatUtil.debugMessage("Start DTD");
306
     MetaCatUtil.debugMessage("DOCNAME: " + docname);
307
     MetaCatUtil.debugMessage("DOCTYPE: " + doctype);
308
     MetaCatUtil.debugMessage("  SYSID: " + systemid);
309
   }
310

    
311
   /** 
312
    * SAX Handler that receives notification of end of DTD 
313
    */
314
   public void endDTD() throws SAXException {
315
     MetaCatUtil.debugMessage("end DTD");
316
   }
317

    
318
   /** 
319
    * SAX Handler that receives notification of comments in the DTD
320
    */
321
   public void comment(char[] ch, int start, int length) throws SAXException {
322
     MetaCatUtil.debugMessage("COMMENT");
323
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
324
     currentNode.writeChildNodeToDB("COMMENT", null, new String(ch), docid);
325
   }
326

    
327
   /** 
328
    * SAX Handler that receives notification of the start of CDATA sections
329
    */
330
   public void startCDATA() throws SAXException {
331
     MetaCatUtil.debugMessage("start CDATA");
332
   }
333

    
334
   /** 
335
    * SAX Handler that receives notification of the end of CDATA sections
336
    */
337
   public void endCDATA() throws SAXException {
338
     MetaCatUtil.debugMessage("end CDATA");
339
   }
340

    
341
   /** 
342
    * SAX Handler that receives notification of the start of entities
343
    */
344
   public void startEntity(String name) throws SAXException {
345
     MetaCatUtil.debugMessage("start ENTITY: " + name);
346
     if (name.equals("[dtd]")) {
347
       processingDTD = true;
348
     }
349
   }
350

    
351
   /** 
352
    * SAX Handler that receives notification of the end of entities
353
    */
354
   public void endEntity(String name) throws SAXException {
355
     MetaCatUtil.debugMessage("end ENTITY: " + name);
356
     if (name.equals("[dtd]")) {
357
       processingDTD = false;
358
     }
359
   }
360

    
361
   /** 
362
    * SAX Handler that receives notification of element declarations
363
    */
364
   public void elementDecl(String name, String model)
365
                        throws org.xml.sax.SAXException {
366
     MetaCatUtil.debugMessage("ELEMENTDECL: " + name + " " + model);
367
   }
368

    
369
   /** 
370
    * SAX Handler that receives notification of attribute declarations
371
    */
372
   public void attributeDecl(String eName, String aName,
373
                        String type, String valueDefault, String value)
374
                        throws org.xml.sax.SAXException {
375
     MetaCatUtil.debugMessage("ATTRIBUTEDECL: " + eName + " " 
376
                        + aName + " " + type + " " + valueDefault + " "
377
                        + value);
378
   }
379

    
380
   /** 
381
    * SAX Handler that receives notification of internal entity declarations
382
    */
383
   public void internalEntityDecl(String name, String value)
384
                        throws org.xml.sax.SAXException {
385
     MetaCatUtil.debugMessage("INTERNENTITYDECL: " + name + " " + value);
386
   }
387

    
388
   /** 
389
    * SAX Handler that receives notification of external entity declarations
390
    */
391
   public void externalEntityDecl(String name, String publicId,
392
                        String systemId)
393
                        throws org.xml.sax.SAXException {
394
     MetaCatUtil.debugMessage("EXTERNENTITYDECL: " + name + " " + publicId 
395
                              + " " + systemId);
396
   }
397

    
398
   //
399
   // the next section implements the ErrorHandler interface
400
   //
401

    
402
   /** 
403
    * SAX Handler that receives notification of fatal parsing errors
404
    */
405
   public void fatalError(SAXParseException exception) throws SAXException {
406
     MetaCatUtil.debugMessage("FATALERROR");
407
     throw (new SAXException("Fatal processing error.", exception));
408
   }
409

    
410
   /** 
411
    * SAX Handler that receives notification of recoverable parsing errors
412
    */
413
   public void error(SAXParseException exception) throws SAXException {
414
     MetaCatUtil.debugMessage("ERROR");
415
   }
416

    
417
   /** 
418
    * SAX Handler that receives notification of warnings
419
    */
420
   public void warning(SAXParseException exception) throws SAXException {
421
     MetaCatUtil.debugMessage("WARNING");
422
   }
423

    
424
   // 
425
   // Helper, getter and setter methods
426
   //
427
   
428
   /**
429
    * get the document name
430
    */
431
   public String getDocname() {
432
     return docname;
433
   }
434

    
435
   /**
436
    * get the document processing state
437
    */
438
   public boolean processingDTD() {
439
     return processingDTD;
440
   }
441
}
(14-14/36)