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-10-03 12:55:28 -0700 (Tue, 03 Oct 2000) $'
12
 * '$Revision: 483 $'
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

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

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

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

    
92
   /** SAX Handler that receives notification of beginning of the document */
93
   public void startDocument() throws SAXException {
94
     MetaCatUtil.debugMessage("start Document");
95

    
96
     // Create the document node representation as root
97
     rootNode = new DBSAXNode(conn, this.docid);
98
     // Add the node to the stack, so that any text data can be 
99
     // added as it is encountered
100
     nodeStack.push(rootNode);
101
   }
102

    
103
   /** SAX Handler that receives notification of end of the document */
104
   public void endDocument() throws SAXException {
105
     MetaCatUtil.debugMessage("end Document");
106
     // Starting new thread for writing XML Index.
107
     // It calls the run method of the thread.
108
     try {
109
       xmlIndex.start();
110
     } catch (NullPointerException e) {
111
       xmlIndex = null;
112
       throw new 
113
       SAXException("Problem with starting thread for writing XML Index. " +
114
                    e.getMessage());
115
     }
116
   }
117

    
118
   /** SAX Handler that is called at the start of each XML element */
119
   public void startElement(String uri, String localName,
120
                            String qName, Attributes atts) 
121
               throws SAXException {
122
     MetaCatUtil.debugMessage("Start ELEMENT " + localName);
123

    
124
     DBSAXNode parentNode = null;
125
     DBSAXNode currentNode = null;
126

    
127
     // Get a reference to the parent node for the id
128
     try {
129
       parentNode = (DBSAXNode)nodeStack.peek();
130
     } catch (EmptyStackException e) {
131
       parentNode = null;
132
     }
133

    
134
     // Document representation that points to the root document node
135
     if (atFirstElement) {
136
       atFirstElement = false;
137
       // If no DOCTYPE declaration: docname = root element name 
138
       if (docname == null) {
139
         docname = localName;
140
         doctype = docname;
141
         MetaCatUtil.debugMessage("DOCNAME-a: " + docname);
142
         MetaCatUtil.debugMessage("DOCTYPE-a: " + doctype);
143
       } else if (doctype == null) {
144
         doctype = docname;
145
         MetaCatUtil.debugMessage("DOCTYPE-b: " + doctype);
146
       }
147
       rootNode.writeNodename(docname);
148
       try {
149
         currentDocument = new DocumentImpl(conn, rootNode.getNodeID(), 
150
                                       docname, doctype, docid, action, user);
151
       } catch (Exception ane) {
152
         throw (new SAXException("Error with " + action, ane));
153
       }
154
       // not needed any more
155
       //rootNode.writeDocID(currentDocument.getDocID());
156
     }      
157

    
158
     // Create the current node representation
159
     currentNode = new DBSAXNode(conn, localName, parentNode,
160
                                 currentDocument.getRootNodeID(),docid,
161
                                 currentDocument.getDoctype());
162
                               
163
     // Add all of the attributes
164
     for (int i=0; i<atts.getLength(); i++) {
165
       currentNode.setAttribute(atts.getLocalName(i), atts.getValue(i), docid);
166
     }      
167

    
168
     // Add the node to the stack, so that any text data can be 
169
     // added as it is encountered
170
     nodeStack.push(currentNode);
171
     // Add the node to the vector used by thread for writing XML Index
172
     nodeIndex.addElement(currentNode);
173

    
174
  }
175
  
176
  // The run method of xmlIndex thread. It writes XML Index for the document.
177
  public void run () {
178
    System.out.println("Jivka's thread is starting");
179
    DBSAXNode currNode = null;
180
    DBSAXNode prevNode = null;
181
    int step = 0;
182
    int counter = 0;
183

    
184
    try {
185
      // Opening separate db connection for writing XML Index
186
      MetaCatUtil util = new MetaCatUtil();
187
      Connection conn = util.getConnection();
188
      conn.setAutoCommit(false);
189

    
190
      // Going through the elements of the document and writing its Index
191
      Enumeration nodes = nodeIndex.elements();
192
      while ( nodes.hasMoreElements() ) {
193
        currNode = (DBSAXNode)nodes.nextElement();
194
        currNode.updateNodeIndex(conn, docid, currentDocument.getDoctype());
195
      }
196
    
197
      conn.commit();
198
      
199
      System.out.println("Jivka's thread is done executing");
200
      //if this is a package file then write the package info to 
201
      //the xml_relation table. relationHandler checks to see
202
      //if it is a package file so you don't have to do it here.
203
      DocumentImpl xmldoc = new DocumentImpl(conn, docid);
204
      System.out.println("starting chad's thread");
205
      relationHandler rth = new relationHandler(xmldoc, conn);
206
      
207
      util.returnConnection(conn);
208
      conn.close();
209
      //util.closeConnections();
210

    
211
    } catch (Exception e) {
212
      try {
213
        conn.rollback();
214
        conn.close();
215
      } catch (SQLException sqle) {}
216
      System.out.println("Error writing XML Index. " + e.getMessage()); 
217
    }      
218
  }
219

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

    
244
       // Write the content of the node to the database
245
       currentNode.writeChildNodeToDB("TEXT", null, data, docid);
246
     }
247

    
248
     // write the title of document if there are tag <title>
249
     if ( currentNode.getTagName().equals("title") ) {
250
       if ( leftover > MAXTITLELEN ) 
251
         currentDocument.setTitle(new String(cbuf, start, MAXTITLELEN));
252
       else
253
         currentDocument.setTitle(new String(cbuf, start, leftover));
254
     }
255
   }
256

    
257
   /** 
258
    * SAX Handler that is called for each XML text node that is Ignorable
259
    * white space
260
    */
261
   public void ignorableWhitespace(char[] cbuf, int start, int len) {
262
     MetaCatUtil.debugMessage("IGNORABLEWHITESPACE");
263
   }
264

    
265
   /** 
266
    * SAX Handler called once for each processing instruction found: 
267
    * node that PI may occur before or after the root element.
268
    */
269
   public void processingInstruction(String target, String data) 
270
          throws SAXException {
271
     MetaCatUtil.debugMessage("PI");
272
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
273
     currentNode.writeChildNodeToDB("PI", target, data, docid);
274
   }
275

    
276
   /** SAX Handler that is called at the end of each XML element */
277
   public void endElement(String uri, String localName,
278
                          String qName) throws SAXException {
279
     MetaCatUtil.debugMessage("End ELEMENT " + localName);
280

    
281
     // Get the node from the stack
282
     DBSAXNode currentNode = (DBSAXNode)nodeStack.pop();
283
   }
284

    
285
   //
286
   // the next section implements the LexicalHandler interface
287
   //
288

    
289
   /** SAX Handler that receives notification of DOCTYPE. Sets the DTD */
290
   public void startDTD(String name, String publicId, String systemId) 
291
               throws SAXException {
292
     docname = name;
293
     doctype = publicId;
294
     systemid = systemId;
295

    
296
     MetaCatUtil.debugMessage("Start DTD");
297
     MetaCatUtil.debugMessage("DOCNAME: " + docname);
298
     MetaCatUtil.debugMessage("DOCTYPE: " + doctype);
299
     MetaCatUtil.debugMessage("  SYSID: " + systemid);
300
   }
301

    
302
   /** 
303
    * SAX Handler that receives notification of end of DTD 
304
    */
305
   public void endDTD() throws SAXException {
306
     MetaCatUtil.debugMessage("end DTD");
307
   }
308

    
309
   /** 
310
    * SAX Handler that receives notification of comments in the DTD
311
    */
312
   public void comment(char[] ch, int start, int length) throws SAXException {
313
     MetaCatUtil.debugMessage("COMMENT");
314
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
315
     currentNode.writeChildNodeToDB("COMMENT", null, new String(ch), docid);
316
   }
317

    
318
   /** 
319
    * SAX Handler that receives notification of the start of CDATA sections
320
    */
321
   public void startCDATA() throws SAXException {
322
     MetaCatUtil.debugMessage("start CDATA");
323
   }
324

    
325
   /** 
326
    * SAX Handler that receives notification of the end of CDATA sections
327
    */
328
   public void endCDATA() throws SAXException {
329
     MetaCatUtil.debugMessage("end CDATA");
330
   }
331

    
332
   /** 
333
    * SAX Handler that receives notification of the start of entities
334
    */
335
   public void startEntity(String name) throws SAXException {
336
     MetaCatUtil.debugMessage("start ENTITY: " + name);
337
     if (name.equals("[dtd]")) {
338
       processingDTD = true;
339
     }
340
   }
341

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

    
352
   /** 
353
    * SAX Handler that receives notification of element declarations
354
    */
355
   public void elementDecl(String name, String model)
356
                        throws org.xml.sax.SAXException {
357
     MetaCatUtil.debugMessage("ELEMENTDECL: " + name + " " + model);
358
   }
359

    
360
   /** 
361
    * SAX Handler that receives notification of attribute declarations
362
    */
363
   public void attributeDecl(String eName, String aName,
364
                        String type, String valueDefault, String value)
365
                        throws org.xml.sax.SAXException {
366
     MetaCatUtil.debugMessage("ATTRIBUTEDECL: " + eName + " " 
367
                        + aName + " " + type + " " + valueDefault + " "
368
                        + value);
369
   }
370

    
371
   /** 
372
    * SAX Handler that receives notification of internal entity declarations
373
    */
374
   public void internalEntityDecl(String name, String value)
375
                        throws org.xml.sax.SAXException {
376
     MetaCatUtil.debugMessage("INTERNENTITYDECL: " + name + " " + value);
377
   }
378

    
379
   /** 
380
    * SAX Handler that receives notification of external entity declarations
381
    */
382
   public void externalEntityDecl(String name, String publicId,
383
                        String systemId)
384
                        throws org.xml.sax.SAXException {
385
     MetaCatUtil.debugMessage("EXTERNENTITYDECL: " + name + " " + publicId 
386
                              + " " + systemId);
387
   }
388

    
389
   //
390
   // the next section implements the ErrorHandler interface
391
   //
392

    
393
   /** 
394
    * SAX Handler that receives notification of fatal parsing errors
395
    */
396
   public void fatalError(SAXParseException exception) throws SAXException {
397
     MetaCatUtil.debugMessage("FATALERROR");
398
     throw (new SAXException("Fatal processing error.", exception));
399
   }
400

    
401
   /** 
402
    * SAX Handler that receives notification of recoverable parsing errors
403
    */
404
   public void error(SAXParseException exception) throws SAXException {
405
     MetaCatUtil.debugMessage("ERROR");
406
   }
407

    
408
   /** 
409
    * SAX Handler that receives notification of warnings
410
    */
411
   public void warning(SAXParseException exception) throws SAXException {
412
     MetaCatUtil.debugMessage("FATALERROR");
413
   }
414

    
415
   // 
416
   // Helper, getter and setter methods
417
   //
418
   
419
   /**
420
    * get the document name
421
    */
422
   public String getDocname() {
423
     return docname;
424
   }
425

    
426
   /**
427
    * get the document processing state
428
    */
429
   public boolean processingDTD() {
430
     return processingDTD;
431
   }
432
}
(9-9/29)