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-01-04 18:13:56 -0800 (Thu, 04 Jan 2001) $'
12
 * '$Revision: 640 $'
13
 */
14

    
15
package edu.ucsb.nceas.metacat;
16

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
205
      // Going through the elements of the document and writing its Index
206
      Enumeration nodes = nodeIndex.elements();
207
      while ( nodes.hasMoreElements() ) {
208
        currNode = (DBSAXNode)nodes.nextElement();
209
        currNode.updateNodeIndex(dbconn, docid, doctype);
210
      }
211
    
212
      dbconn.commit();
213
         
214
      //if this is a package file then write the package info to 
215
      //the xml_relation table. RelationHandler checks to see
216
      //if it is a package file so you don't have to do it here.
217
      if ( doctype.equals(util.getOption("packagedoctype")) )
218
      {
219
        DocumentImpl xmldoc = new DocumentImpl(dbconn, docid);
220
        RelationHandler rth = new RelationHandler(xmldoc, dbconn);
221
      } 
222
      else if ( doctype.equals(util.getOption("accessdoctype")) ) 
223
      {
224
        DocumentImpl xmldoc = new DocumentImpl(dbconn, docid);
225
        String xml = xmldoc.toString();
226
        AccessControlList aclobj = 
227
        new AccessControlList(dbconn,docid,null,new StringReader(xml));
228
        dbconn.commit();
229
      }
230
      
231
      
232
      dbconn.close();
233

    
234
    } catch (Exception e) {
235
      try {
236
        dbconn.rollback();
237
        dbconn.close();
238
      } catch (SQLException sqle) {}
239
      System.out.println("Error writing XML Index. " + e.getMessage()); 
240
    }      
241
  }
242

    
243
  /** SAX Handler that is called for each XML text node */
244
  public void characters(char[] cbuf, int start, int len) throws SAXException {
245
     MetaCatUtil.debugMessage("CHARACTERS");
246
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
247
     String data = null;
248
     int leftover = len;
249
     int offset = start;
250
     boolean moredata = true;
251
    
252
     // This loop deals with the case where there are more characters 
253
     // than can fit in a single database text field (limit is 
254
     // MAXDATACHARS).  If the text to be inserted exceeds MAXDATACHARS,
255
     // write a series of nodes that are MAXDATACHARS long, and then the
256
     // final node contains the remainder
257
     while (moredata) {
258
       if (leftover > MAXDATACHARS) {
259
         data = new String(cbuf, offset, MAXDATACHARS);
260
         leftover -= MAXDATACHARS;
261
         offset += MAXDATACHARS;
262
       } else {
263
         data = new String(cbuf, offset, leftover);
264
         moredata = false;
265
       }
266

    
267
       // Write the content of the node to the database
268
       currentNode.writeChildNodeToDB("TEXT", null, data, docid);
269
     }
270

    
271
     // write the title of document if there are tag <title>
272
     if ( currentNode.getTagName().equals("title") ) {
273
       if ( leftover > MAXTITLELEN ) 
274
         currentDocument.setTitle(new String(cbuf, start, MAXTITLELEN));
275
       else
276
         currentDocument.setTitle(new String(cbuf, start, leftover));
277
     }
278
   }
279

    
280
   /** 
281
    * SAX Handler that is called for each XML text node that is Ignorable
282
    * white space
283
    */
284
   public void ignorableWhitespace(char[] cbuf, int start, int len) {
285
     MetaCatUtil.debugMessage("IGNORABLEWHITESPACE");
286
   }
287

    
288
   /** 
289
    * SAX Handler called once for each processing instruction found: 
290
    * node that PI may occur before or after the root element.
291
    */
292
   public void processingInstruction(String target, String data) 
293
          throws SAXException {
294
     MetaCatUtil.debugMessage("PI");
295
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
296
     currentNode.writeChildNodeToDB("PI", target, data, docid);
297
   }
298

    
299
   /** SAX Handler that is called at the end of each XML element */
300
   public void endElement(String uri, String localName,
301
                          String qName) throws SAXException {
302
     MetaCatUtil.debugMessage("End ELEMENT " + localName);
303

    
304
     // Get the node from the stack
305
     DBSAXNode currentNode = (DBSAXNode)nodeStack.pop();
306
   }
307

    
308
   //
309
   // the next section implements the LexicalHandler interface
310
   //
311

    
312
   /** SAX Handler that receives notification of DOCTYPE. Sets the DTD */
313
   public void startDTD(String name, String publicId, String systemId) 
314
               throws SAXException {
315
     docname = name;
316
     doctype = publicId;
317
     systemid = systemId;
318

    
319
     MetaCatUtil.debugMessage("Start DTD");
320
     MetaCatUtil.debugMessage("DOCNAME: " + docname);
321
     MetaCatUtil.debugMessage("DOCTYPE: " + doctype);
322
     MetaCatUtil.debugMessage("  SYSID: " + systemid);
323
   }
324

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

    
332
   /** 
333
    * SAX Handler that receives notification of comments in the DTD
334
    */
335
   public void comment(char[] ch, int start, int length) throws SAXException {
336
     MetaCatUtil.debugMessage("COMMENT");
337
     DBSAXNode currentNode = (DBSAXNode)nodeStack.peek();
338
     currentNode.writeChildNodeToDB("COMMENT", null, new String(ch), docid);
339
   }
340

    
341
   /** 
342
    * SAX Handler that receives notification of the start of CDATA sections
343
    */
344
   public void startCDATA() throws SAXException {
345
     MetaCatUtil.debugMessage("start CDATA");
346
   }
347

    
348
   /** 
349
    * SAX Handler that receives notification of the end of CDATA sections
350
    */
351
   public void endCDATA() throws SAXException {
352
     MetaCatUtil.debugMessage("end CDATA");
353
   }
354

    
355
   /** 
356
    * SAX Handler that receives notification of the start of entities
357
    */
358
   public void startEntity(String name) throws SAXException {
359
     MetaCatUtil.debugMessage("start ENTITY: " + name);
360
     if (name.equals("[dtd]")) {
361
       processingDTD = true;
362
     }
363
   }
364

    
365
   /** 
366
    * SAX Handler that receives notification of the end of entities
367
    */
368
   public void endEntity(String name) throws SAXException {
369
     MetaCatUtil.debugMessage("end ENTITY: " + name);
370
     if (name.equals("[dtd]")) {
371
       processingDTD = false;
372
     }
373
   }
374

    
375
   /** 
376
    * SAX Handler that receives notification of element declarations
377
    */
378
   public void elementDecl(String name, String model)
379
                        throws org.xml.sax.SAXException {
380
     MetaCatUtil.debugMessage("ELEMENTDECL: " + name + " " + model);
381
   }
382

    
383
   /** 
384
    * SAX Handler that receives notification of attribute declarations
385
    */
386
   public void attributeDecl(String eName, String aName,
387
                        String type, String valueDefault, String value)
388
                        throws org.xml.sax.SAXException {
389
     MetaCatUtil.debugMessage("ATTRIBUTEDECL: " + eName + " " 
390
                        + aName + " " + type + " " + valueDefault + " "
391
                        + value);
392
   }
393

    
394
   /** 
395
    * SAX Handler that receives notification of internal entity declarations
396
    */
397
   public void internalEntityDecl(String name, String value)
398
                        throws org.xml.sax.SAXException {
399
     MetaCatUtil.debugMessage("INTERNENTITYDECL: " + name + " " + value);
400
   }
401

    
402
   /** 
403
    * SAX Handler that receives notification of external entity declarations
404
    */
405
   public void externalEntityDecl(String name, String publicId,
406
                        String systemId)
407
                        throws org.xml.sax.SAXException {
408
     MetaCatUtil.debugMessage("EXTERNENTITYDECL: " + name + " " + publicId 
409
                              + " " + systemId);
410
   }
411

    
412
   //
413
   // the next section implements the ErrorHandler interface
414
   //
415

    
416
   /** 
417
    * SAX Handler that receives notification of fatal parsing errors
418
    */
419
   public void fatalError(SAXParseException exception) throws SAXException {
420
     MetaCatUtil.debugMessage("FATALERROR");
421
     throw (new SAXException("Fatal processing error.", exception));
422
   }
423

    
424
   /** 
425
    * SAX Handler that receives notification of recoverable parsing errors
426
    */
427
   public void error(SAXParseException exception) throws SAXException {
428
     MetaCatUtil.debugMessage("ERROR");
429
   }
430

    
431
   /** 
432
    * SAX Handler that receives notification of warnings
433
    */
434
   public void warning(SAXParseException exception) throws SAXException {
435
     MetaCatUtil.debugMessage("WARNING");
436
   }
437

    
438
   // 
439
   // Helper, getter and setter methods
440
   //
441
   
442
   /**
443
    * get the document name
444
    */
445
   public String getDocname() {
446
     return docname;
447
   }
448

    
449
   /**
450
    * get the document processing state
451
    */
452
   public boolean processingDTD() {
453
     return processingDTD;
454
   }
455
}
(15-15/39)