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: Jing Tao
8
 *
9
 *   '$Author: leinfelder $'
10
 *     '$Date: 2011-03-23 12:51:40 -0700 (Wed, 23 Mar 2011) $'
11
 * '$Revision: 6016 $'
12
 *
13
 * This program is free software; you can redistribute it and/or modify
14
 * it under the terms of the GNU General Public License as published by
15
 * the Free Software Foundation; either version 2 of the License, or
16
 * (at your option) any later version.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 * GNU General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU General Public License
24
 * along with this program; if not, write to the Free Software
25
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
 */
27

    
28
package edu.ucsb.nceas.metacat;
29

    
30
import java.io.BufferedReader;
31
import java.io.File;
32
import java.io.FileInputStream;
33
import java.io.FileOutputStream;
34
import java.io.IOException;
35
import java.io.InputStream;
36
import java.io.InputStreamReader;
37
import java.io.OutputStream;
38
import java.io.OutputStreamWriter;
39
import java.io.Reader;
40
import java.io.Writer;
41
import java.sql.PreparedStatement;
42
import java.sql.ResultSet;
43
import java.sql.SQLException;
44
import java.sql.Statement;
45
import java.util.EmptyStackException;
46
import java.util.Enumeration;
47
import java.util.Hashtable;
48
import java.util.Stack;
49
import java.util.Vector;
50

    
51
import org.apache.log4j.Logger;
52
import org.xml.sax.Attributes;
53
import org.xml.sax.SAXException;
54

    
55
import edu.ucsb.nceas.metacat.accesscontrol.AccessControlForSingleFile;
56
import edu.ucsb.nceas.metacat.accesscontrol.AccessControlInterface;
57
import edu.ucsb.nceas.metacat.accesscontrol.AccessRule;
58
import edu.ucsb.nceas.metacat.accesscontrol.AccessSection;
59
import edu.ucsb.nceas.metacat.accesscontrol.XMLAccessDAO;
60
import edu.ucsb.nceas.metacat.database.DBConnection;
61
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
62
import edu.ucsb.nceas.metacat.properties.PropertyService;
63
import edu.ucsb.nceas.metacat.util.AuthUtil;
64
import edu.ucsb.nceas.metacat.util.DocumentUtil;
65
import edu.ucsb.nceas.metacat.util.MetacatUtil;
66
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
67

    
68
/**
69
 * A database aware Class implementing callback methods for the SAX parser to
70
 * call when processing the XML stream and generating events
71
 * Here is the rules for user which has write permission in top level access
72
 * rules(he can update metadata but can't update access module) try to update
73
 * a document:
74
 * 1. Checking access part (both in top level and additional level, node by node)
75
 *    If something was modified, reject the document. Note: for additional part
76
 *    The access subtree startnode starts at "<describe>" rather than <access>.
77
 *    This is because make sure ids wouldn't be mess up by user.
78
 * 2. Checking ids in additional access part, if they points a distribution
79
 *    module with online or inline. If some ids don't, reject the documents.
80
 * 3. For inline distribution:
81
 *    If user can't read the inline data, the empty string in inline element
82
 *    will be send to user if he read this eml document(user has read access
83
 *    at top level - metadata, but user couldn't read inline data).
84
 *    Here is some interested scenario: If user does have read and write
85
 *    permission in top level access rules(for metadata)
86
 *    but 1) user doesn't have read and write permission in inline data block,
87
 *    so if user update the document with some inline data rather than
88
 *    empty string in same inline data block(same distribution inline id),
89
 *    this means user updated the inline data. So the document should be
90
 *    rejected.
91
 *    2) user doesn't have read permission, but has write permission in
92
 *    inline data block. If user send back inline data block with empty
93
 *    string, we will think user doesn't update inline data and we will
94
 *    copy old inline data back to the new one. If user send something
95
 *    else, we will overwrite the old inline data by new one.
96
 * 4. For online distribution:
97
 *    It is easy than inline distribution. When the user try to change a external
98
 *    document id, we will checking if the user have "all" permission to it.
99
 *    If don't have, reject the document. If have, delete the old rules and apply
100
 *    The new rules.
101
 *
102
 *
103
 * Here is some info about additional access rules ( data object rules):
104
 *  The data access rules format looks like:
105
 *  <additionalMetadata>
106
 *    <describes>100</describes>
107
 *    <describes>300</describes>
108
 *    <access>rulesone</access>
109
 *  </additionalMetadata>
110
 *  <additionalMetadata>
111
 *    <describes>200</describes>
112
 *    <access>rulesthree</access>
113
 *  </additionalMetadata>
114
 *  Because eml additionalMetadata is (describes+, any) and any ocurrence is 1.
115
 *  So following xml will be rejected by xerces.
116
 *  <additionalMetadata>
117
 *    <describes>100</describes>
118
 *    <describes>300</describes>
119
 *    <other>....</other>
120
 *    <access>rulesone</access>
121
 *  </additionalMetadata>
122
 */
123
public class Eml200SAXHandler extends DBSAXHandler implements
124
        AccessControlInterface
125
{
126
	// Boolean that tells whether we are processing top level (document level) access.
127
	// this is true when we hit an 'access' element, and the grandparent element is 
128
	// 'eml' and we are not inside an 'additionalMetadata' section.  Set back to false
129
	// in endElement
130
    private boolean processTopLevelAccess = false;
131

    
132
    // now additionalAccess will be explained as distribution access control
133
    // - data file
134
    private boolean processAdditionalAccess = false;
135

    
136
	// Boolean that tells whether we are processing other access. This is true when 
137
    // we find an 'access' element inside an 'additionalMetadata' element.  Set back
138
    // to false in endElement
139
    private boolean processOtherAccess = false;
140

    
141
    // if we are inside an 'access' element, we use this object to hold the 
142
    // current access info
143
    private AccessSection accessObject = null;
144

    
145
    // for each 'access/allow' and 'access/deny' we create a new access Rule to hold
146
    // the info
147
    private AccessRule accessRule = null;
148

    
149
    private Vector describesId = new Vector(); // store the ids in
150
                                               //additionalmetadata/describes
151

    
152
    //store all distribution element id for online url. key is the distribution
153
    // id and  data  is url
154
    private Hashtable onlineURLDistributionIdList = new Hashtable();
155
    // distribution/oneline/url will store this vector if distribution doesn't
156
    // have a id.
157
    private Vector onlineURLDistributionListWithoutId = new Vector();
158

    
159
    //store all distribution element id for online other distribution, such as
160
    // connection or connectiondefination. key is the distribution id
161
    // and  data  is distribution id
162
    private Hashtable onlineOtherDistributionIdList = new Hashtable();
163

    
164
    //store all distribution element id for inline data.
165
    // key is the distribution id, data is the internal inline id
166
    private Hashtable inlineDistributionIdList = new Hashtable();
167

    
168
    //store all distribution element id for off line data.
169
    // key is the distribution id, data is the id too.
170
    private Hashtable offlineDistributionIdList = new Hashtable();
171

    
172
    // a hash to stored all distribution id, both key and value are id itself
173
    private Hashtable distributionAllIdList = new Hashtable();
174

    
175
    // temporarily store distribution id
176
    private String distributionId = null;
177

    
178
    // flag to indicate to handle distribution
179
    private boolean proccessDistribution = false;
180

    
181
    // a hash table to stored the distribution which is a reference and this
182
    // distribution has a id too. The key is itself id of this distribution,
183
    // the value is the referenced id.
184
    // So, we only stored the format like this:
185
    // <distribution id ="100"><reference>300</reference></distribution>
186
    // the reason is:
187
    // if not id in distribution, then the distribution couldn't be added
188
    // to additional access module. The distribution block which was referenced
189
    // id (300) will be stored in above distribution lists.
190
    private Hashtable distributionReferenceList = new Hashtable();
191

    
192
    // if the action is update and the user does not have ALL permission 
193
    // and the user is not an administrator, then we will need to compare 
194
    // access modules to make sure the user has update permissions
195
    private boolean needToCheckAccessModule = false;
196

    
197
    private AccessSection topAccessSubTreeFromDB = null;
198

    
199
    private Vector additionalAccessSubTreeListFromDB = new Vector();
200

    
201
    private Hashtable referencedAccessSubTreeListFromDB = new Hashtable();
202

    
203
    // this holds the top level (document level) access section.
204
    private AccessSection topAccessSection;
205

    
206
    private Vector additionalAccessVector = new Vector();
207

    
208
    // key is subtree id and value is accessSection object
209
    private Hashtable possibleReferencedAccessHash = new Hashtable();
210

    
211
    // we need an another stack to store the access node which we pull out just
212
    // from xml. If a reference happend, we can use the stack the compare nodes
213
    private Stack storedAccessNodeStack = new Stack();
214

    
215
    // vector stored the data file id which will be write into relation table
216
    private Vector onlineDataFileIdInRelationVector = new Vector();
217

    
218
    // Indicator of inline data
219
    private boolean handleInlineData = false;
220

    
221
    private Hashtable inlineDataNameSpace = null;
222

    
223
    private Writer inlineDataFileWriter = null;
224

    
225
    private String inlineDataFileName = null;
226

    
227
    private int inLineDataIndex = 0;
228

    
229
    private Vector inlineFileIDList = new Vector();
230

    
231
    private boolean inAdditionalMetaData = false;
232

    
233
    //user has unwritable inline data object when it updates a document
234
    private boolean unWritableInlineDataObject = false;
235
    //user has unreadable inline data when it updates a dcoument
236
    private boolean unReadableInlineDataObject = false;
237

    
238
    // the hashtable contains the info from xml_access table which
239
    // inline data the user can't read when user update a document.
240
    // The key in hashtable is subtree id and data is the inline data internal
241
    // file name.
242

    
243
    private Hashtable previousUnreadableInlineDataObjectHash = new Hashtable();
244

    
245
    // the hashtable contains the info from xml_access table which
246
    // inline data the user can't write when user update a document.
247
    // The key in hashtable is subtree id and data is the inline data internal
248
    // file name.
249
    private Hashtable previousUnwritableInlineDataObjectHash = new Hashtable();
250

    
251
    private Hashtable accessSubTreeAlreadyWriteDBList = new Hashtable();
252

    
253
    //This hashtable will stored the id which already has additional access
254
    // control. So the top level access control will ignore them.
255
    private Hashtable onlineURLIdHasadditionalAccess   = new Hashtable();
256

    
257
    // additional access module will be in additionalMetadata part. Its format
258
    // look like
259
    //<additionalMetadata>
260
    //   <describes>100</describes>
261
    //   <describes>300</describes>
262
    //   <access>rulesone</access>
263
    //</additionalMetadata>
264
    // If user couldn't have all permission to update access rules, he/she could
265
    // not update access module, but also couldn't update "describes".
266
    // So the start node id for additional access section is the first describes
267
    // element
268
    private boolean firstDescribesInAdditionalMetadata = true;
269
    private long    firstDescribesNodeId               = -1;
270

    
271
    private int numberOfHitUnWritableInlineData = 0;
272

    
273
    // Constant
274
    private static final String EML = "eml";
275

    
276
    private static final String DESCRIBES = "describes";
277

    
278
    private static final String ADDITIONALMETADATA = "additionalMetadata";
279

    
280
    private static final String ORDER = "order";
281

    
282
    private static final String ID = "id";
283

    
284
    private static final String REFERENCES = "references";
285

    
286
    public static final String INLINE = "inline";
287

    
288
    private static final String ONLINE = "online";
289

    
290
    private static final String OFFLINE = "offline";
291

    
292
    private static final String CONNECTION = "connection";
293

    
294
    private static final String CONNECTIONDEFINITION = "connectionDefinition";
295

    
296
    private static final String URL = "url";
297

    
298
    private static final String PERMISSIONERROR = "User tried to update a subtree "
299
        + "when they don't have write permission!";
300

    
301
    private static final String UPDATEACCESSERROR = "User tried to update an "
302
        + "access module when they don't have \"ALL\" permission!";
303

    
304
    public static final String TOPLEVEL = "top";
305

    
306
    public static final String DATAACCESSLEVEL = "dataAccess";
307

    
308
    // this level is for the access module which is not in top or additional
309
    // place, but it was referenced by top or additional
310
    private static final String REFERENCEDLEVEL = "referenced";
311

    
312
    private static final String RELATION = "Provides info for";
313

    
314
    private static final String DISTRIBUTION = "distribution";
315

    
316
    private Logger logMetacat = Logger.getLogger(Eml200SAXHandler.class);   	   	
317
    
318
    /**
319
     * Construct an instance of the handler class In this constructor, user can
320
     * specify the version need to upadate
321
     *
322
     * @param conn the JDBC connection to which information is written
323
     * @param action - "INSERT" or "UPDATE"
324
     * @param docid to be inserted or updated into JDBC connection
325
     * @param revision, the user specified the revision need to be update
326
     * @param user the user connected to MetaCat servlet and owns the document
327
     * @param groups the groups to which user belongs
328
     * @param pub flag for public "read" access on document
329
     * @param serverCode the serverid from xml_replication on which this
330
     *            document resides.
331
     *
332
     */
333
    public Eml200SAXHandler(DBConnection conn, String action, String docid,
334
            String revision, String user, String[] groups, String pub,
335
            int serverCode, String createDate, String updateDate) throws SAXException
336
    {
337
        super(conn, action, docid, revision, user, groups, pub, 
338
                serverCode, createDate, updateDate);
339
        // Get the unchangable subtrees (user doesn't have write permission)
340
        try
341
        {
342
            PermissionController control = new PermissionController(docid
343
                    + PropertyService.getProperty("document.accNumSeparator") + revision);
344
            //unChangableSubTreeHash = getUnchangableSubTree(control, user,
345
            // groups);
346

    
347
            // If the action is update and user doesn't have "ALL" permission
348
            // we need to check if user update access subtree
349
            if (action != null && action.equals("UPDATE")
350
                    && !control.hasPermission(user, groups,
351
                            AccessControlInterface.ALLSTRING) 
352
                            && !AuthUtil.isAdministrator(user, groups))
353
            {
354
                needToCheckAccessModule = true;
355
                topAccessSubTreeFromDB = getTopAccessSubTreeFromDB();
356
                additionalAccessSubTreeListFromDB =
357
                                         getAdditionalAccessSubTreeListFromDB();
358
                referencedAccessSubTreeListFromDB =
359
                                         getReferencedAccessSubTreeListFromDB();
360
            }
361

    
362
            //Here is for  data object checking.
363
            if (action != null && action.equals("UPDATE"))
364
            {
365
              //info about inline data object which user doesn't have read
366
              //permission the info come from xml_access table
367
              previousUnreadableInlineDataObjectHash = PermissionController.
368
                            getUnReadableInlineDataIdList(docid, user,
369
                                                          groups, true);
370

    
371
              //info about data object which user doesn't have write permission
372
              // the info come from xml_accesss table
373
              previousUnwritableInlineDataObjectHash = PermissionController.
374
                            getUnWritableInlineDataIdList(docid, user,
375
                                                          groups, true);
376

    
377
            }
378

    
379

    
380
        }
381
        catch (Exception e)
382
        {
383
            logMetacat.error("error in Eml200SAXHandler is " + e.getMessage());
384
            throw new SAXException(e.getMessage());
385
        }
386
    }
387

    
388
    /*
389
     * Get the top level access subtree info from xml_accesssubtree table.
390
     * If no top access subtree found, null will be return.
391
     */
392
     private AccessSection getTopAccessSubTreeFromDB()
393
                                                       throws SAXException
394
     {
395
       AccessSection topAccess = null;
396
       PreparedStatement pstmt = null;
397
       ResultSet rs = null;
398
       String sql = "SELECT subtreeid, startnodeid, endnodeid "
399
                + "FROM xml_accesssubtree WHERE docid like ? "
400
                + "AND controllevel like ?";
401

    
402

    
403
       try
404
       {
405
            pstmt = connection.prepareStatement(sql);
406
            // Increase DBConnection usage count
407
            connection.increaseUsageCount(1);
408
            // Bind the values to the query
409
            pstmt.setString(1, docid);
410
            pstmt.setString(2, TOPLEVEL);
411
            logMetacat.debug("Eml200SAXHandler.getTopAccessSubTreeFromDB - executing SQL: " + pstmt.toString());
412
            pstmt.execute();
413

    
414
            // Get result set
415
            rs = pstmt.getResultSet();
416
            if (rs.next())
417
            {
418
                String sectionId = rs.getString(1);
419
                long startNodeId = rs.getLong(2);
420
                long endNodeId = rs.getLong(3);
421
                // create a new access section
422
                topAccess = new AccessSection();
423
                topAccess.setControlLevel(TOPLEVEL);
424
                topAccess.setDocId(docid);
425
                topAccess.setSubTreeId(sectionId);
426
                topAccess.setStartNodeId(startNodeId);
427
                topAccess.setEndNodeId(endNodeId);
428
            }
429
            pstmt.close();
430
        }//try
431
        catch (SQLException e)
432
        {
433
            throw new SAXException(
434
                    "EMLSAXHandler.getTopAccessSubTreeFromDB(): "
435
                            + e.getMessage());
436
        }//catch
437
        finally
438
        {
439
            try
440
            {
441
                pstmt.close();
442
            }
443
            catch (SQLException ee)
444
            {
445
                throw new SAXException(
446
                        "EMLSAXHandler.getTopAccessSubTreeFromDB(): "
447
                                + ee.getMessage());
448
            }
449
        }//finally
450
        return topAccess;
451

    
452
     }
453

    
454
    /*
455
     * Get the subtree node info from xml_accesssubtree table
456
     */
457
    private Vector getAdditionalAccessSubTreeListFromDB() throws Exception
458
    {
459
        Vector result = new Vector();
460
        PreparedStatement pstmt = null;
461
        ResultSet rs = null;
462
        String sql = "SELECT subtreeid, startnodeid, endnodeid "
463
                + "FROM xml_accesssubtree WHERE docid like ? "
464
                + "AND controllevel like ? "
465
                + "ORDER BY startnodeid ASC";
466

    
467
        try
468
        {
469

    
470
            pstmt = connection.prepareStatement(sql);
471
            // Increase DBConnection usage count
472
            connection.increaseUsageCount(1);
473
            // Bind the values to the query
474
            pstmt.setString(1, docid);
475
            pstmt.setString(2, DATAACCESSLEVEL);
476
            pstmt.execute();
477

    
478
            // Get result set
479
            rs = pstmt.getResultSet();
480
            while (rs.next())
481
            {
482
                String sectionId = rs.getString(1);
483
                long startNodeId = rs.getLong(2);
484
                long endNodeId = rs.getLong(3);
485
                // create a new access section
486
                AccessSection accessObj = new AccessSection();
487
                accessObj.setControlLevel(DATAACCESSLEVEL);
488
                accessObj.setDocId(docid);
489
                accessObj.setSubTreeId(sectionId);
490
                accessObj.setStartNodeId(startNodeId);
491
                accessObj.setEndNodeId(endNodeId);
492
                // add this access obj into vector
493
                result.add(accessObj);
494

    
495
            }
496
            pstmt.close();
497
        }//try
498
        catch (SQLException e)
499
        {
500
            throw new SAXException(
501
                    "EMLSAXHandler.getadditionalAccessSubTreeListFromDB(): "
502
                            + e.getMessage());
503
        }//catch
504
        finally
505
        {
506
            try
507
            {
508
                pstmt.close();
509
            }
510
            catch (SQLException ee)
511
            {
512
                throw new SAXException(
513
                        "EMLSAXHandler.getAccessSubTreeListFromDB(): "
514
                                + ee.getMessage());
515
            }
516
        }//finally
517
        return result;
518
    }
519

    
520
   /*
521
    * Get the access subtree for referenced info from xml_accesssubtree table
522
    */
523
   private Hashtable getReferencedAccessSubTreeListFromDB() throws Exception
524
   {
525
       Hashtable result = new Hashtable();
526
       PreparedStatement pstmt = null;
527
       ResultSet rs = null;
528
       String sql = "SELECT subtreeid, startnodeid, endnodeid "
529
               + "FROM xml_accesssubtree WHERE docid like ? "
530
               + "AND controllevel like ? "
531
               + "ORDER BY startnodeid ASC";
532

    
533
       try
534
       {
535

    
536
           pstmt = connection.prepareStatement(sql);
537
           // Increase DBConnection usage count
538
           connection.increaseUsageCount(1);
539
           // Bind the values to the query
540
           pstmt.setString(1, docid);
541
           pstmt.setString(2, REFERENCEDLEVEL);
542
           pstmt.execute();
543

    
544
           // Get result set
545
           rs = pstmt.getResultSet();
546
           while (rs.next())
547
           {
548
               String sectionId = rs.getString(1);
549
               long startNodeId = rs.getLong(2);
550
               long endNodeId = rs.getLong(3);
551
               // create a new access section
552
               AccessSection accessObj = new AccessSection();
553
               accessObj.setControlLevel(DATAACCESSLEVEL);
554
               accessObj.setDocId(docid);
555
               accessObj.setSubTreeId(sectionId);
556
               accessObj.setStartNodeId(startNodeId);
557
               accessObj.setEndNodeId(endNodeId);
558
               // add this access obj into hastable
559
               if ( sectionId != null && !sectionId.trim().equals(""))
560
               {
561
                 result.put(sectionId, accessObj);
562
               }
563

    
564
           }
565
           pstmt.close();
566
       }//try
567
       catch (SQLException e)
568
       {
569
           throw new SAXException(
570
                   "EMLSAXHandler.getReferencedAccessSubTreeListFromDB(): "
571
                           + e.getMessage());
572
       }//catch
573
       finally
574
       {
575
           try
576
           {
577
               pstmt.close();
578
           }
579
           catch (SQLException ee)
580
           {
581
               throw new SAXException(
582
                       "EMLSAXHandler.getReferencedSubTreeListFromDB(): "
583
                               + ee.getMessage());
584
           }
585
       }//finally
586
       return result;
587
   }
588

    
589

    
590

    
591
    /** SAX Handler that is called at the start of each XML element */
592
    public void startElement(String uri, String localName, String qName,
593
            Attributes atts) throws SAXException
594
    {
595
        // for element <eml:eml...> qname is "eml:eml", local name is "eml"
596
        // for element <acl....> both qname and local name is "eml"
597
        // uri is namesapce
598
        logMetacat.debug("Start ELEMENT(qName) " + qName);
599
        logMetacat.debug("Start ELEMENT(localName) " + localName);
600
        logMetacat.debug("Start ELEMENT(uri) " + uri);
601

    
602
        DBSAXNode parentNode = null;
603
        DBSAXNode currentNode = null;
604
        // none inline part
605
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
606
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
607
        if (!handleInlineData)
608
        {
609
            // Get a reference to the parent node for the id
610
            try
611
            {
612
                parentNode = (DBSAXNode) nodeStack.peek();
613
            }
614
            catch (EmptyStackException e)
615
            {
616
                parentNode = null;
617
            }
618

    
619
            //start handle inline data
620
            //=====================================================
621
            if (qName.equals(INLINE) && !inAdditionalMetaData)
622
            {
623
                handleInlineData = true;
624
                inLineDataIndex++;
625
                //intitialize namespace hash for in line data
626
                inlineDataNameSpace = new Hashtable();
627
                //initialize file writer
628
                String docidWithoutRev = DocumentUtil.getDocIdFromString(docid);
629
                String seperator = ".";
630
                try {
631
					seperator = PropertyService.getProperty("document.accNumSeparator");
632
				} catch (PropertyNotFoundException pnfe) {
633
					logMetacat.error("Could not get property 'accNumSeparator'.  " 
634
							+ "Setting separator to '.': "+ pnfe.getMessage());
635
				}
636
                // the new file name will look like docid.rev.2
637
                inlineDataFileName = docidWithoutRev + seperator + revision
638
                        + seperator + inLineDataIndex;
639
                inlineDataFileWriter = createInlineDataFileWriter(inlineDataFileName, encoding);
640
                // put the inline file id into a vector. If upload failed,
641
                // metacat will
642
                // delete the inline data file
643
                inlineFileIDList.add(inlineDataFileName);
644

    
645
                // put distribution id and inline file id into a  hash
646
                if (distributionId != null)
647
                {
648
                  //check to see if this inline data is readable or writable to
649
                  // this user
650
                  if (!previousUnreadableInlineDataObjectHash.isEmpty() &&
651
                       previousUnreadableInlineDataObjectHash.containsKey(distributionId))
652
                  {
653
                      unReadableInlineDataObject = true;
654
                  }
655
                  if (!previousUnwritableInlineDataObjectHash.isEmpty() &&
656
                       previousUnwritableInlineDataObjectHash.containsKey(distributionId))
657
                  {
658
                     unWritableInlineDataObject = true;
659
                     numberOfHitUnWritableInlineData++;
660
                  }
661

    
662

    
663
                  // store the distributid and inlinedata filename into a hash
664
                  inlineDistributionIdList.put(distributionId, inlineDataFileName);
665
                }
666

    
667
            }
668
            //==============================================================
669

    
670

    
671
            // If hit a text node, we need write this text for current's parent
672
            // node
673
            // This will happend if the element is mixted
674
            //==============================================================
675
            if (hitTextNode && parentNode != null)
676
            {
677

    
678

    
679
                if (needToCheckAccessModule
680
                        && (processAdditionalAccess || processOtherAccess || processTopLevelAccess)) {
681
                    // stored the pull out nodes into storedNode stack
682
                    NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "TEXT",
683
                            null, null, MetacatUtil.normalize(textBuffer
684
                                    .toString()));
685
                    storedAccessNodeStack.push(nodeElement);
686

    
687
                }
688

    
689
                // write the textbuffer into db for parent node.
690
                endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer,
691
                        parentNode);
692
                // rest hitTextNode
693
                hitTextNode = false;
694
                // reset textbuffer
695
                textBuffer = null;
696
                textBuffer = new StringBuffer();
697

    
698
            }
699
            //==================================================================
700

    
701
            // Document representation that points to the root document node
702
            //==================================================================
703
            if (atFirstElement)
704
            {
705
                atFirstElement = false;
706
                // If no DOCTYPE declaration: docname = root element
707
                // doctype = root element name or name space
708
                if (docname == null) {
709
                    docname = localName;
710
                    // if uri isn't null doctype = uri(namespace)
711
                    // othewise root element
712
                    if (uri != null && !(uri.trim()).equals("")) {
713
                        doctype = uri;
714
                    } else {
715
                        doctype = docname;
716
                    }
717
                    logMetacat.info("DOCNAME-a: " + docname);
718
                    logMetacat.info("DOCTYPE-a: " + doctype);
719
                } else if (doctype == null) {
720
                    // because docname is not null and it is declared in dtd
721
                    // so could not be in schema, no namespace
722
                    doctype = docname;
723
                    logMetacat.info("DOCTYPE-b: " + doctype);
724
                }
725
                rootNode.writeNodename(docname);
726
                //System.out.println("here!!!!!!!!!!!!!!!!!!1");
727
                try {
728
                    // for validated XML Documents store a reference to XML DB
729
                    // Catalog
730
                    // Because this is select statement and it needn't to roll
731
                    // back if
732
                    // insert document action fialed.
733
                    // In order to decrease DBConnection usage count, we get a
734
                    // new
735
                    // dbconnection from pool
736
                    //String catalogid = null;
737
                    DBConnection dbConn = null;
738
                    int serialNumber = -1;
739

    
740
                    try {
741
                        // Get dbconnection
742
                        dbConn = DBConnectionPool
743
                                .getDBConnection("DBSAXHandler.startElement");
744
                        serialNumber = dbConn.getCheckOutSerialNumber();
745

    
746
                        Statement stmt = dbConn.createStatement();
747
                        ResultSet rs = stmt
748
                                .executeQuery("SELECT catalog_id FROM xml_catalog "
749
                                        + "WHERE entry_type = 'Schema' "
750
                                        + "AND public_id = '" + doctype + "'");
751
                        boolean hasRow = rs.next();
752
                        if (hasRow) {
753
                            catalogid = rs.getString(1);
754
                        }
755
                        stmt.close();
756
                        //System.out.println("here!!!!!!!!!!!!!!!!!!2");
757
                    }//try
758
                    finally {
759
                        // Return dbconnection
760
                        DBConnectionPool.returnDBConnection(dbConn,
761
                                serialNumber);
762
                    }//finally
763

    
764
                    //create documentImpl object by the constructor which can
765
                    // specify
766
                    //the revision
767
                    if (!super.getIsRevisionDoc())
768
                    {
769
                       logMetacat.debug("EML200SaxHandler.startElement - creating new DocumentImple for " + docid);
770
                       currentDocument = new DocumentImpl(connection, rootNode
771
                            .getNodeID(), docname, doctype, docid, revision,
772
                            action, user, this.pub, catalogid, this.serverCode, 
773
                            createDate, updateDate);
774
                    }
775
                   
776

    
777
                } catch (Exception ane) {
778
                    throw (new SAXException("EML200SaxHandler.startElement - error with action " + 
779
                    		action + " : " + ane.getMessage()));
780
                }
781
                
782
            }
783
            //==================================================================
784

    
785
            // node
786
            //==================================================================
787
            // Create the current node representation
788
            currentNode = new DBSAXNode(connection, qName, localName,
789
                    parentNode, rootNode.getNodeID(), docid,
790
                    doctype);
791
            // Use a local variable to store the element node id
792
            // If this element is a start point of subtree(section), it will be
793
            // stored
794
            // otherwise, it will be discated
795
            long startNodeId = currentNode.getNodeID();
796
            // Add all of the namespaces
797
            String prefix = null;
798
            String nsuri = null;
799
            Enumeration prefixes = namespaces.keys();
800
            while (prefixes.hasMoreElements())
801
            {
802
                prefix = (String) prefixes.nextElement();
803
                nsuri = (String) namespaces.get(prefix);
804
                endNodeId = currentNode.setNamespace(prefix, nsuri, docid);
805
            }
806

    
807
            //=================================================================
808
           // attributes
809
           // Add all of the attributes
810
          for (int i = 0; i < atts.getLength(); i++)
811
          {
812
              String attributeName = atts.getQName(i);
813
              String attributeValue = atts.getValue(i);
814
              endNodeId = currentNode.setAttribute(attributeName,
815
                      attributeValue, docid);
816

    
817
              // To handle name space and schema location if the attribute
818
              // name is
819
              // xsi:schemaLocation. If the name space is in not in catalog
820
              // table
821
              // it will be regeistered.
822
              if (attributeName != null
823
                      && attributeName
824
                              .indexOf(MetaCatServlet.SCHEMALOCATIONKEYWORD) != -1) {
825
                  SchemaLocationResolver resolver = new SchemaLocationResolver(
826
                          attributeValue);
827
                  resolver.resolveNameSpace();
828

    
829
              }
830
              else if (attributeName != null && attributeName.equals(ID) &&
831
                       currentNode.getTagName().equals(DISTRIBUTION) &&
832
                       !inAdditionalMetaData)
833
              {
834
                 // this is a distribution element and the id is distributionID
835
                 distributionId = attributeValue;
836
                 distributionAllIdList.put(distributionId, distributionId);
837

    
838
              }
839

    
840
          }//for
841

    
842

    
843
           //=================================================================
844

    
845
            // handle access stuff
846
            //==================================================================
847
            if (localName.equals(ACCESS))
848
            {
849
                //make sure the access is top level
850
                // this mean current node's parent's parent should be "eml"
851
                DBSAXNode tmpNode = (DBSAXNode) nodeStack.pop();// pop out
852
                                                                    // parent
853
                                                                    // node
854
                //peek out grandParentNode
855
                DBSAXNode grandParentNode = (DBSAXNode) nodeStack.peek();
856
                // put parent node back
857
                nodeStack.push(tmpNode);
858
                String grandParentTag = grandParentNode.getTagName();
859
                if (grandParentTag.equals(EML) && !inAdditionalMetaData)
860
                {
861
                  processTopLevelAccess = true;
862

    
863
                }
864
                else if ( !inAdditionalMetaData )
865
                {
866
                  // process other access embedded into resource level
867
                  // module
868
                  processOtherAccess = true;
869
                }
870
                else
871
                {
872
                  // this for access in additional data which doesn't have a
873
                  // described element. If it has a described element,
874
                  // this code won't hurt any thing
875
                  processAdditionalAccess = true;
876
                }
877

    
878

    
879
                // create access object
880
                accessObject = new AccessSection();
881
                // set permission order
882
                String permOrder = currentNode.getAttribute(ORDER);
883
                accessObject.setPermissionOrder(permOrder);
884
                // set access id
885
                String accessId = currentNode.getAttribute(ID);
886
                accessObject.setSubTreeId(accessId);
887
                // for additional access subtree, the  start of node id should
888
                // be describe element. We also stored the start access element
889
                // node id too.
890
                if (processAdditionalAccess)
891
                {
892
                  accessObject.setStartedDescribesNodeId(firstDescribesNodeId);
893
                  accessObject.setControlLevel(DATAACCESSLEVEL);
894
                }
895
                else if (processTopLevelAccess)
896
                {
897
                  accessObject.setControlLevel(TOPLEVEL);
898
                }
899
                else if (processOtherAccess)
900
                {
901
                  accessObject.setControlLevel(REFERENCEDLEVEL);
902
                }
903

    
904
                accessObject.setStartNodeId(startNodeId);
905
                accessObject.setDocId(docid);
906

    
907

    
908

    
909
            }
910
            // Set up a access rule for allow
911
            else if (parentNode.getTagName() != null
912
                    && (parentNode.getTagName()).equals(ACCESS)
913
                    && localName.equals(ALLOW))
914
           {
915

    
916
                accessRule = new AccessRule();
917

    
918
                //set permission type "allow"
919
                accessRule.setPermissionType(ALLOW);
920

    
921
            }
922
            // set up an access rule for den
923
            else if (parentNode.getTagName() != null
924
                    && (parentNode.getTagName()).equals(ACCESS)
925
                    && localName.equals(DENY))
926
           {
927
                accessRule = new AccessRule();
928
                //set permission type "allow"
929
                accessRule.setPermissionType(DENY);
930
            }
931

    
932
            //=================================================================
933
            // some other independ stuff
934

    
935
            // Add the node to the stack, so that any text data can be
936
            // added as it is encountered
937
            nodeStack.push(currentNode);
938
            // Add the node to the vector used by thread for writing XML Index
939
            nodeIndex.addElement(currentNode);
940

    
941
            // store access module element and attributes into stored stack
942
            if (needToCheckAccessModule
943
                    && (processAdditionalAccess || processOtherAccess || processTopLevelAccess))
944
            {
945
                // stored the pull out nodes into storedNode stack
946
                NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "ELEMENT",
947
                        localName, prefix, MetacatUtil.normalize(null));
948
                storedAccessNodeStack.push(nodeElement);
949
                for (int i = 0; i < atts.getLength(); i++) {
950
                    String attributeName = atts.getQName(i);
951
                    String attributeValue = atts.getValue(i);
952
                    NodeRecord nodeAttribute = new NodeRecord(-2, -2, -2,
953
                            "ATTRIBUTE", attributeName, null, MetacatUtil
954
                                    .normalize(attributeValue));
955
                    storedAccessNodeStack.push(nodeAttribute);
956
                }
957

    
958
            }
959

    
960
            if (currentNode.getTagName().equals(ADDITIONALMETADATA))
961
            {
962
              inAdditionalMetaData = true;
963
            }
964
            else if (currentNode.getTagName().equals(DESCRIBES) &&
965
                     parentNode.getTagName().equals(ADDITIONALMETADATA) &&
966
                     firstDescribesInAdditionalMetadata)
967
            {
968
              // this is first decirbes element in additional metadata
969
              firstDescribesNodeId = startNodeId;
970
              // we started process additional access rules here
971
              // because access and describe couldn't be seperated
972
              NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "ELEMENT",
973
                        localName, prefix, MetacatUtil.normalize(null));
974
              storedAccessNodeStack.push(nodeElement);
975
              processAdditionalAccess = true;
976
              logMetacat.info("set processAdditonalAccess true when meet describe");
977
            }
978
            else if (inAdditionalMetaData && processAdditionalAccess &&
979
                     parentNode.getTagName().equals(ADDITIONALMETADATA) &&
980
                     !currentNode.getTagName().equals(DESCRIBES) &&
981
                     !currentNode.getTagName().equals(ACCESS))
982
            {
983
               // we start processadditionalAccess  module when first hit describes
984
               // in additionalMetadata. So this is possible, there are
985
               // "describes" but not "access". So here is try to terminate
986
               // processAddionalAccess. In this situation, there another element
987
               // rather than "describes" or "access" as a child of additionalMetadata
988
               // so this is impossible it will have access element now.
989
               // If additionalMetadata has access element, the flag will be
990
               // terminated in endElement
991
               processAdditionalAccess = false;
992
               logMetacat.warn("set processAddtionAccess false if the there is no access in additional");
993
            }
994
            else if (currentNode.getTagName().equals(DISTRIBUTION) &&
995
                     !inAdditionalMetaData)
996
            {
997
              proccessDistribution = true;
998
            }
999

    
1000

    
1001
             //==================================================================
1002
            // reset name space
1003
            namespaces = null;
1004
            namespaces = new Hashtable();
1005

    
1006
        }//not inline data
1007
        // inline data
1008
        //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1009
        //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1010
        else
1011
        {
1012
            // we don't buffer the inline data in characters() method
1013
            // so start character don't need to hand text node.
1014

    
1015
            // inline data may be the xml format.
1016
            StringBuffer inlineElements = new StringBuffer();
1017
            inlineElements.append("<").append(qName);
1018
            // append attributes
1019
            for (int i = 0; i < atts.getLength(); i++) {
1020
                String attributeName = atts.getQName(i);
1021
                String attributeValue = atts.getValue(i);
1022
                inlineElements.append(" ");
1023
                inlineElements.append(attributeName);
1024
                inlineElements.append("=\"");
1025
                inlineElements.append(attributeValue);
1026
                inlineElements.append("\"");
1027
            }
1028
            // append namespace
1029
            String prefix = null;
1030
            String nsuri = null;
1031
            Enumeration prefixes = inlineDataNameSpace.keys();
1032
            while (prefixes.hasMoreElements()) {
1033
                prefix = (String) prefixes.nextElement();
1034
                nsuri =  (String)  inlineDataNameSpace.get(prefix);
1035
                inlineElements.append(" ");
1036
                inlineElements.append("xmlns:");
1037
                inlineElements.append(prefix);
1038
                inlineElements.append("=\"");
1039
                inlineElements.append(nsuri);
1040
                inlineElements.append("\"");
1041
            }
1042
            inlineElements.append(">");
1043
            //reset inline data name space
1044
            inlineDataNameSpace = null;
1045
            inlineDataNameSpace = new Hashtable();
1046
            //write inline data into file
1047
            logMetacat.info("the inline element data is: "
1048
                    + inlineElements.toString());
1049
            writeInlineDataIntoFile(inlineDataFileWriter, inlineElements);
1050
        }//else
1051
        //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1052
        //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1053

    
1054
    }
1055

    
1056

    
1057
    /** SAX Handler that is called for each XML text node */
1058
    public void characters(char[] cbuf, int start, int len) throws SAXException
1059
    {
1060
        logMetacat.info("CHARACTERS");
1061
        if (!handleInlineData) {
1062
            // buffer all text nodes for same element. This is for text was
1063
            // splited
1064
            // into different nodes
1065
            textBuffer.append(new String(cbuf, start, len));
1066
            // set hittextnode true
1067
            hitTextNode = true;
1068
            // if text buffer .size is greater than max, write it to db.
1069
            // so we can save memory
1070
            if (textBuffer.length() >= MAXDATACHARS)
1071
            {
1072
                logMetacat.info("Write text into DB in charaters"
1073
                           + " when text buffer size is greater than maxmum number");
1074
                DBSAXNode currentNode = (DBSAXNode) nodeStack.peek();
1075
                endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer,
1076
                        currentNode);
1077
                if (needToCheckAccessModule
1078
                     && (processAdditionalAccess || processOtherAccess || processTopLevelAccess))
1079
                {
1080
                     // stored the pull out nodes into storedNode stack
1081
                     NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "TEXT",
1082
                       null, null, MetacatUtil.normalize(textBuffer
1083
                          .toString()));
1084
                     storedAccessNodeStack.push(nodeElement);
1085

    
1086
                }
1087
                textBuffer = null;
1088
                textBuffer = new StringBuffer();
1089
            }
1090
        }
1091
        else
1092
        {
1093
            // this is inline data and write file system directly
1094
            // we don't need to buffered it.
1095
            StringBuffer inlineText = new StringBuffer();
1096
            inlineText.append(new String(cbuf, start, len));
1097
            logMetacat.info(
1098
                    "The inline text data write into file system: "
1099
                            + inlineText.toString());
1100
            writeInlineDataIntoFile(inlineDataFileWriter, inlineText);
1101
        }
1102
    }
1103

    
1104
    /** SAX Handler that is called at the end of each XML element */
1105
    public void endElement(String uri, String localName, String qName)
1106
            throws SAXException
1107
    {
1108
        logMetacat.info("End ELEMENT " + qName);
1109

    
1110
        // when close inline element
1111
        //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1112
        //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1113
        if (localName.equals(INLINE) && handleInlineData)
1114
        {
1115
            // Get the node from the stack
1116
            DBSAXNode currentNode = (DBSAXNode) nodeStack.pop();
1117
            String currentTag = currentNode.getTagName();
1118
            logMetacat.info("End of inline data");
1119
            // close file writer
1120
            try
1121
            {
1122
                inlineDataFileWriter.close();
1123
                handleInlineData = false;
1124
            }
1125
            catch (IOException ioe)
1126
            {
1127
                throw new SAXException(ioe.getMessage());
1128
            }
1129

    
1130
            //check if user changed inine data or not if user doesn't have
1131
            // write permission for this inline block
1132
            // if some error happends, we would delete the inline data file here,
1133
            // we will call a method named deletedInlineFiles in DocumentImple
1134
            if (unWritableInlineDataObject)
1135
            {
1136
                if (unReadableInlineDataObject)
1137
                {
1138
                  // now user just got a empty string in linline part
1139
                  // so if the user send back a empty string is fine and we will
1140
                  // copy the old file to new file. If he send something else,
1141
                  // the document will be rejected
1142
                  if (inlineDataIsEmpty(inlineDataFileName))
1143
                  {
1144
                    copyInlineFile(distributionId, inlineDataFileName);
1145
                  }
1146
                  else
1147
                  {
1148
                    logMetacat.info(
1149
                               "inline data was changed by a user"
1150
                                       + " who doesn't have permission");
1151
                    throw new SAXException(PERMISSIONERROR);
1152

    
1153
                  }
1154
                }//if
1155
                else
1156
                {
1157
                  // user get the inline data
1158
                  if (modifiedInlineData(distributionId, inlineDataFileName))
1159
                  {
1160
                    logMetacat.info(
1161
                                "inline data was changed by a user"
1162
                                        + " who doesn't have permission");
1163
                    throw new SAXException(PERMISSIONERROR);
1164
                  }//if
1165
                }//else
1166
            }//if
1167
            else
1168
            {
1169
               //now user can update file.
1170
               if (unReadableInlineDataObject)
1171
               {
1172
                  // now user just got a empty string in linline part
1173
                  // so if the user send back a empty string is fine and we will
1174
                  // copy the old file to new file. If he send something else,
1175
                  // the new inline data will overwite the old one(here we need
1176
                  // do nothing because the new inline data already existed
1177
                  if (inlineDataIsEmpty(inlineDataFileName))
1178
                  {
1179
                    copyInlineFile(distributionId, inlineDataFileName);
1180
                  }
1181
                }//if
1182

    
1183
            }//else
1184
            // put inline data file name into text buffer (without path)
1185
            textBuffer = new StringBuffer(inlineDataFileName);
1186
            // write file name into db
1187
            endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer,
1188
                    currentNode);
1189
            // reset textbuff
1190
            textBuffer = null;
1191
            textBuffer = new StringBuffer();
1192
            // resetinlinedata file name
1193
            inlineDataFileName = null;
1194
            unWritableInlineDataObject = false;
1195
            unReadableInlineDataObject = false;
1196
            return;
1197
        }
1198
        //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1199
        //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1200

    
1201

    
1202

    
1203
        // close element which is not in inline data
1204
        //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1205
        //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1206
        if (!handleInlineData)
1207
        {
1208
            // Get the node from the stack
1209
            DBSAXNode currentNode = (DBSAXNode) nodeStack.pop();
1210
            String currentTag = currentNode.getTagName();
1211

    
1212
            // If before the end element, the parser hit text nodes and store
1213
            // them
1214
            // into the buffer, write the buffer to data base. The reason we
1215
            // put
1216
            // write database here is for xerces some time split text node
1217
            if (hitTextNode)
1218
            {
1219
                // get access value
1220
                String data = null;
1221
                // add principal
1222
                if (currentTag.equals(PRINCIPAL) && accessRule != null)
1223
                {
1224
                    data = (textBuffer.toString()).trim();
1225
                    accessRule.addPrincipal(data);
1226

    
1227
                }
1228
                else if (currentTag.equals(PERMISSION) && accessRule != null)
1229
                {
1230
                    data = (textBuffer.toString()).trim();
1231
                    // we conbine different a permission into one value
1232
                    int permission = accessRule.getPermission();
1233
                    // add permision
1234
                    if (data.toUpperCase().equals(READSTRING))
1235
                    {
1236
                        permission = permission | READ;
1237
                    }
1238
                    else if (data.toUpperCase().equals(WRITESTRING))
1239
                    {
1240
                        permission = permission | WRITE;
1241
                    }
1242
                    else if (data.toUpperCase().equals(CHMODSTRING))
1243
                    {
1244
                        permission = permission | CHMOD;
1245
                    }
1246
                    else if (data.toUpperCase().equals(ALLSTRING))
1247
                    {
1248
                        permission = permission | ALL;
1249
                    }
1250
                    accessRule.setPermission(permission);
1251
                }
1252
                // put additionalmetadata/describes into vector
1253
                else if (currentTag.equals(DESCRIBES))
1254
                {
1255
                    data = (textBuffer.toString()).trim();
1256
                    describesId.add(data);
1257
                    //firstDescribesInAdditionalMetadata = false;
1258
                    //firstDescribesNodeId = 0;
1259
                }
1260
                else if (currentTag.equals(REFERENCES)
1261
                        && (processTopLevelAccess || processAdditionalAccess || processOtherAccess))
1262
                {
1263
                    // get reference
1264
                    data = (textBuffer.toString()).trim();
1265
                    // put reference id into accessSection
1266
                    accessObject.setReferences(data);
1267

    
1268
                }
1269
                else if (currentTag.equals(REFERENCES) && proccessDistribution)
1270
                {
1271
                  // get reference for distribution
1272
                  data = (textBuffer.toString()).trim();
1273
                  // we only stored the distribution reference which itself
1274
                  // has a id
1275
                  if (distributionId != null)
1276
                  {
1277
                    distributionReferenceList.put(distributionId, data);
1278
                  }
1279

    
1280
                }
1281
                else if (currentTag.equals(URL) && !inAdditionalMetaData)
1282
                {
1283
                    //handle online data, make sure its'parent is online
1284
                    DBSAXNode parentNode = (DBSAXNode) nodeStack.peek();
1285
                    if (parentNode != null && parentNode.getTagName() != null
1286
                            && parentNode.getTagName().equals(ONLINE))
1287
                    {
1288
                        data = (textBuffer.toString()).trim();
1289
                        if (distributionId != null)
1290
                        {
1291
                          onlineURLDistributionIdList.put(distributionId, data);
1292
                        }
1293
                        else
1294
                        {
1295
                          onlineURLDistributionListWithoutId.add(data);
1296
                        }
1297
                    }//if
1298
                }//else if
1299
                // write text to db if it is not inline data
1300

    
1301
                logMetacat.info(
1302
                            "Write text into DB in End Element");
1303

    
1304
                 // write text node into db
1305
                 endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer,
1306
                            currentNode);
1307

    
1308
                if (needToCheckAccessModule
1309
                        && (processAdditionalAccess || processOtherAccess || processTopLevelAccess)) {
1310
                    // stored the pull out nodes into storedNode stack
1311
                    NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "TEXT",
1312
                            null, null, MetacatUtil.normalize(textBuffer
1313
                                    .toString()));
1314
                    storedAccessNodeStack.push(nodeElement);
1315

    
1316
                }
1317
            }//if handle text node
1318

    
1319

    
1320

    
1321
            //set hitText false
1322
            hitTextNode = false;
1323
            // reset textbuff
1324
            textBuffer = null;
1325
            textBuffer = new StringBuffer();
1326

    
1327

    
1328
            // access stuff
1329
            if (currentTag.equals(ALLOW) || currentTag.equals(DENY))
1330
            {
1331
                // finish parser a ccess rule and assign it to new one
1332
                AccessRule newRule = accessRule;
1333
                //add the new rule to access section object
1334
                accessObject.addAccessRule(newRule);
1335
                // reset access rule
1336
                accessRule = null;
1337
            }// ALLOW or DENY
1338
            else if (currentTag.equals(ACCESS))
1339
            {
1340
                // finish parse a access setction and stored them into different
1341
                // places
1342

    
1343
                accessObject.setEndNodeId(endNodeId);
1344
                AccessSection newAccessObject = accessObject;
1345
                newAccessObject.setStoredTmpNodeStack(storedAccessNodeStack);
1346
                if (newAccessObject != null)
1347
                {
1348

    
1349
                    if (processTopLevelAccess)
1350
                    {
1351
                       topAccessSection = newAccessObject;
1352

    
1353
                    }//if
1354
                    else if (processAdditionalAccess)
1355
                    {
1356
                        // for additional control
1357
                        // put discribesId into the accessobject and put this
1358
                        // access object into vector
1359
                        newAccessObject.setDescribedIdList(describesId);
1360
                        additionalAccessVector.add(newAccessObject);
1361

    
1362
                    }//if
1363
                    else if (processOtherAccess)
1364
                    {
1365
                      // we only stored the access object which has a id
1366
                      // because only the access object which has a id can
1367
                      // be reference
1368
                      if (newAccessObject.getSubTreeId() != null &&
1369
                          !newAccessObject.getSubTreeId().trim().equals(""))
1370
                      {
1371
                         possibleReferencedAccessHash.
1372
                           put(newAccessObject.getSubTreeId(), newAccessObject);
1373
                      }
1374
                    }
1375

    
1376
                }//if
1377
                //reset access section object
1378
                accessObject = null;
1379

    
1380
                // reset tmp stored node stack
1381
                storedAccessNodeStack = null;
1382
                storedAccessNodeStack = new Stack();
1383

    
1384
                // reset flag
1385
                processAdditionalAccess = false;
1386
                processTopLevelAccess = false;
1387
                processOtherAccess = false;
1388

    
1389
            }//access element
1390
            else if (currentTag.equals(ADDITIONALMETADATA))
1391
            {
1392
                //reset describesId
1393
                describesId = null;
1394
                describesId = new Vector();
1395
                inAdditionalMetaData = false;
1396
                firstDescribesNodeId = -1;
1397
                // reset tmp stored node stack
1398
                storedAccessNodeStack = null;
1399
                storedAccessNodeStack = new Stack();
1400

    
1401

    
1402
            }
1403
            else if (currentTag.equals(DISTRIBUTION) && !inAdditionalMetaData)
1404
            {
1405
               //reset distribution id
1406
               distributionId = null;
1407
               proccessDistribution = false;
1408
            }
1409
            else if (currentTag.equals(OFFLINE) && !inAdditionalMetaData)
1410
            {
1411
               if (distributionId != null)
1412
               {
1413
                 offlineDistributionIdList.put(distributionId, distributionId);
1414
               }
1415
            }
1416
            else if ((currentTag.equals(CONNECTION) || currentTag.equals(CONNECTIONDEFINITION))
1417
                     && !inAdditionalMetaData)
1418
            {
1419
              //handle online data, make sure its'parent is online
1420
                 DBSAXNode parentNode = (DBSAXNode) nodeStack.peek();
1421
                 if (parentNode != null && parentNode.getTagName() != null
1422
                         && parentNode.getTagName().equals(ONLINE))
1423
                 {
1424
                     if (distributionId != null)
1425
                     {
1426
                        onlineOtherDistributionIdList.put(distributionId, distributionId);
1427
                     }
1428
                 }//if
1429

    
1430
            }//else if
1431
            else if (currentTag.equals(DESCRIBES))
1432
            {
1433
                firstDescribesInAdditionalMetadata = false;
1434

    
1435
            }
1436

    
1437

    
1438

    
1439
        }
1440
        // close elements which are in inline data (inline data can be xml doc)
1441
        //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1442
        //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1443
        else
1444
        {
1445
            // this is in inline part
1446
            StringBuffer endElement = new StringBuffer();
1447
            endElement.append("</");
1448
            endElement.append(qName);
1449
            endElement.append(">");
1450
            logMetacat.info("inline endElement: "
1451
                    + endElement.toString());
1452
            writeInlineDataIntoFile(inlineDataFileWriter, endElement);
1453
        }
1454
        //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1455
        //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1456
    }
1457

    
1458

    
1459
    /*
1460
     * Method to check if the new line data is as same as the old one
1461
     */
1462
     private boolean modifiedInlineData(String inlineDistributionId,
1463
			String newInlineInternalFileName) throws SAXException {
1464
       boolean modified = true;
1465
       if (inlineDistributionId == null || newInlineInternalFileName == null)
1466
       {
1467
         return modified;
1468
       }
1469
       String oldInlineInternalFileName =
1470
            (String)previousUnwritableInlineDataObjectHash.get(inlineDistributionId);
1471
       if (oldInlineInternalFileName == null ||
1472
           oldInlineInternalFileName.trim().equals(""))
1473
       {
1474
         return modified;
1475
       }
1476
       logMetacat.info("in handle inline data");
1477
       logMetacat.info("the inline data file name from xml_access is: "
1478
                                    + oldInlineInternalFileName);
1479

    
1480
       try
1481
       {
1482
         if (!compareInlineDataFiles(oldInlineInternalFileName,
1483
                                     newInlineInternalFileName))
1484
         {
1485
           modified = true;
1486

    
1487
         }
1488
         else
1489
         {
1490
           modified = false;
1491
         }
1492
       }
1493
       catch(Exception e)
1494
       {
1495
         modified = true;
1496
       }
1497

    
1498
       // delete the inline data file already in file system
1499
       if (modified)
1500
       {
1501
         deleteInlineDataFile(newInlineInternalFileName);
1502

    
1503
       }
1504
       return modified;
1505
     }
1506

    
1507
     /*
1508
      * A method to check if a line file is empty
1509
      */
1510
     private boolean inlineDataIsEmpty(String fileName) throws SAXException
1511
     {
1512
        boolean isEmpty = true;
1513
        if ( fileName == null)
1514
        {
1515
          throw new SAXException("The inline file name is null");
1516
        }
1517
        
1518
        try {
1519
			String path = PropertyService.getProperty("application.inlinedatafilepath");
1520
			// the new file name will look like path/docid.rev.2
1521
			File inlineDataDirectory = new File(path);
1522
			File inlineDataFile = new File(inlineDataDirectory, fileName);
1523

    
1524
			Reader inlineFileReader = new InputStreamReader(new FileInputStream(inlineDataFile), encoding);
1525
			BufferedReader inlineStringReader = new BufferedReader(inlineFileReader);
1526
			String string = inlineStringReader.readLine();
1527
			// at the end oldstring will be null
1528
			while (string != null) {
1529
				string = inlineStringReader.readLine();
1530
				if (string != null && !string.trim().equals("")) {
1531
					isEmpty = false;
1532
					break;
1533
				}
1534
			}
1535

    
1536
		} catch (Exception e) {
1537
			throw new SAXException(e.getMessage());
1538
		}
1539
		return isEmpty;
1540

    
1541
     }
1542

    
1543

    
1544
    /**
1545
	 * SAX Handler that receives notification of comments in the DTD
1546
	 */
1547
    public void comment(char[] ch, int start, int length) throws SAXException
1548
    {
1549
        logMetacat.info("COMMENT");
1550
        if (!handleInlineData) {
1551
            if (!processingDTD) {
1552
                DBSAXNode currentNode = (DBSAXNode) nodeStack.peek();
1553
                String str = new String(ch, start, length);
1554

    
1555
                //compare comment if need
1556
                /*if (startCriticalSubTree) {
1557
                    compareCommentNode(currentUnChangedableSubtreeNodeStack,
1558
                            str, PERMISSIONERROR);
1559
                }//if*/
1560
                //compare top level access module
1561
                if (processTopLevelAccess && needToCheckAccessModule) {
1562
                    /*compareCommentNode(currentUnchangableAccessModuleNodeStack,
1563
                            str, UPDATEACCESSERROR);*/
1564
                }
1565
                endNodeId = currentNode.writeChildNodeToDB("COMMENT", null,
1566
                        str, docid);
1567
                if (needToCheckAccessModule
1568
                        && (processAdditionalAccess || processOtherAccess || processTopLevelAccess)) {
1569
                    // stored the pull out nodes into storedNode stack
1570
                    NodeRecord nodeElement = new NodeRecord(-2, -2, -2,
1571
                            "COMMENT", null, null, MetacatUtil.normalize(str));
1572
                    storedAccessNodeStack.push(nodeElement);
1573

    
1574
                }
1575
            }
1576
        } else {
1577
            // inline data comment
1578
            StringBuffer inlineComment = new StringBuffer();
1579
            inlineComment.append("<!--");
1580
            inlineComment.append(new String(ch, start, length));
1581
            inlineComment.append("-->");
1582
            logMetacat.info("inline data comment: "
1583
                    + inlineComment.toString());
1584
            writeInlineDataIntoFile(inlineDataFileWriter, inlineComment);
1585
        }
1586
    }
1587

    
1588

    
1589

    
1590
    /**
1591
     * SAX Handler called once for each processing instruction found: node that
1592
     * PI may occur before or after the root element.
1593
     */
1594
    public void processingInstruction(String target, String data)
1595
            throws SAXException
1596
    {
1597
        logMetacat.info("PI");
1598
        if (!handleInlineData) {
1599
            DBSAXNode currentNode = (DBSAXNode) nodeStack.peek();
1600
            endNodeId = currentNode.writeChildNodeToDB("PI", target, data,
1601
                    docid);
1602
        } else {
1603
            StringBuffer inlinePI = new StringBuffer();
1604
            inlinePI.append("<?");
1605
            inlinePI.append(target);
1606
            inlinePI.append(" ");
1607
            inlinePI.append(data);
1608
            inlinePI.append("?>");
1609
            logMetacat.info("inline data pi is: "
1610
                    + inlinePI.toString());
1611
            writeInlineDataIntoFile(inlineDataFileWriter, inlinePI);
1612
        }
1613
    }
1614

    
1615
    /** SAX Handler that is called at the start of Namespace */
1616
    public void startPrefixMapping(String prefix, String uri)
1617
            throws SAXException
1618
    {
1619
        logMetacat.info("NAMESPACE");
1620
        logMetacat.info("NAMESPACE prefix "+prefix);
1621
        logMetacat.info("NAMESPACE uri "+uri);
1622
        if (!handleInlineData) {
1623
            namespaces.put(prefix, uri);
1624
        } else {
1625
            inlineDataNameSpace.put(prefix, uri);
1626
        }
1627
    }
1628

    
1629
    /**
1630
     * SAX Handler that is called for each XML text node that is Ignorable
1631
     * white space
1632
     */
1633
    public void ignorableWhitespace(char[] cbuf, int start, int len)
1634
            throws SAXException
1635
    {
1636
        // When validation is turned "on", white spaces are reported here
1637
        // When validation is turned "off" white spaces are not reported here,
1638
        // but through characters() callback
1639
        logMetacat.info("IGNORABLEWHITESPACE");
1640
        if (!handleInlineData) {
1641
            DBSAXNode currentNode = (DBSAXNode) nodeStack.peek();
1642
            String data = new String(cbuf, start, len);
1643

    
1644
                //compare whitespace in access top module
1645
                if (processTopLevelAccess && needToCheckAccessModule) {
1646
                    /*compareWhiteSpace(currentUnchangableAccessModuleNodeStack,
1647
                            data, UPDATEACCESSERROR);*/
1648
                }
1649
                // Write the content of the node to the database
1650
                if (needToCheckAccessModule
1651
                        && (processAdditionalAccess || processOtherAccess || processTopLevelAccess)) {
1652
                    // stored the pull out nodes into storedNode stack
1653
                    NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "TEXT",
1654
                            null, null, MetacatUtil.normalize(data));
1655
                    storedAccessNodeStack.push(nodeElement);
1656

    
1657
                }
1658
                endNodeId = currentNode.writeChildNodeToDB("TEXT", null, data,
1659
                        docid);
1660
        } else {
1661
            //This is inline data write to file directly
1662
            StringBuffer inlineWhiteSpace = new StringBuffer(new String(cbuf,
1663
                    start, len));
1664
            writeInlineDataIntoFile(inlineDataFileWriter, inlineWhiteSpace);
1665
        }
1666

    
1667
    }
1668

    
1669

    
1670
    /** SAX Handler that receives notification of end of the document */
1671
    public void endDocument() throws SAXException
1672
    {
1673
        logMetacat.info("end Document");
1674
        if (needToCheckAccessModule)
1675
        {
1676
          compareAllAccessModules();
1677
        }
1678

    
1679
        // user deleted some inline block which it counldn't delete
1680
        if (numberOfHitUnWritableInlineData !=
1681
            previousUnwritableInlineDataObjectHash.size())
1682
        {
1683
          throw new SAXException("user deleted some inline block it couldn't");
1684
        }
1685

    
1686
        if (!super.getIsRevisionDoc())
1687
        {
1688
          // write access rule to xml_access table which include both top level
1689
          // and additional level(data access level). It also write access subtree
1690
          // info into xml_accesssubtree about the top access, additional access
1691
          // and some third place access modules which are referenced by top
1692
          // level or additional level
1693
          writeAccessRuleToDB();
1694

    
1695
          //delete relation table
1696
          deleteRelations();
1697
          //write relations
1698
           for (int i = 0; i < onlineDataFileIdInRelationVector.size(); i++) {
1699
            String id = (String) onlineDataFileIdInRelationVector.elementAt(i);
1700
            writeOnlineDataFileIdIntoRelationTable(id);
1701
           }
1702
        }
1703

    
1704
        // clean the subtree record
1705
        accessSubTreeAlreadyWriteDBList = new Hashtable();
1706
    }
1707

    
1708

    
1709

    
1710
    /* The method will compare all access modules in eml document -
1711
     * topLevel, additionalLevel(data access) and referenced access module*/
1712
    private void compareAllAccessModules() throws SAXException
1713
    {
1714
      //compare top level
1715
      compareAccessSubtree(topAccessSubTreeFromDB, topAccessSection);
1716

    
1717
      //compare additional level
1718
      int oldSize = additionalAccessSubTreeListFromDB.size();
1719
      int newSize = additionalAccessVector.size();
1720
      // if size is different, use deleted or added rules, so throw a exception
1721
      if (oldSize != newSize)
1722
      {
1723
        throw new SAXException(UPDATEACCESSERROR);
1724
      }
1725
      //because access modules are both ordered in ASC in vectors, so we can
1726
      // compare one bye one
1727
      for ( int i = 0; i < newSize; i++)
1728
      {
1729
        AccessSection fromDB = (AccessSection)
1730
                          additionalAccessSubTreeListFromDB.elementAt(i);
1731
        AccessSection fromParser = (AccessSection)
1732
                                additionalAccessVector.elementAt(i);
1733
        compareAccessSubtree(fromDB, fromParser);
1734
      }
1735

    
1736
      //compare referenced level
1737
      Enumeration em = referencedAccessSubTreeListFromDB.keys();
1738
      while (em.hasMoreElements())
1739
      {
1740
        String id = (String)em.nextElement();
1741
        AccessSection fromDB = (AccessSection)
1742
                               referencedAccessSubTreeListFromDB.get(id);
1743
        AccessSection fromParser = (AccessSection)
1744
                               possibleReferencedAccessHash.get(id);
1745
        compareAccessSubtree(fromDB, fromParser);
1746
      }
1747
    }
1748

    
1749
    /* The method will compare two access subtree. Currently they compare to
1750
     * nodes one by one. It also can be changed to parse the node first, then
1751
     * compare the parsed result
1752
     */
1753
    private void compareAccessSubtree(AccessSection fromDBTable,
1754
                                       AccessSection fromParser)
1755
                                      throws SAXException
1756
    {
1757
       if (fromDBTable == null || fromParser == null)
1758
       {
1759
         throw new SAXException(UPDATEACCESSERROR);
1760
       }
1761
       Stack nodeStackFromDBTable = fromDBTable.getSubTreeNodeStack();
1762
       Stack nodeStackFromParser  = fromParser.getStoredTmpNodeStack();
1763

    
1764
       Stack tempStack = new Stack();
1765
       while(!nodeStackFromDBTable.isEmpty()){
1766
           tempStack.push(nodeStackFromDBTable.pop());
1767
       }
1768
       comparingNodeStacks(tempStack, nodeStackFromParser);
1769
    }
1770

    
1771
    /* Compare two node stacks to see if they are same */
1772
  private void comparingNodeStacks(Stack stack1, Stack stack2)
1773
          throws SAXException
1774
  {
1775
      // make sure stack1 and stack2 are not empty
1776
      if (stack1.isEmpty() || stack2.isEmpty()) {
1777
          logMetacat.info("Because stack is empty!");
1778
          throw new SAXException(UPDATEACCESSERROR);
1779
      }
1780
      // go throw two stacks and compare every element
1781
      while (!stack1.isEmpty()) {
1782
          // Pop an element from stack1
1783
          NodeRecord record1 = (NodeRecord) stack1.pop();
1784

    
1785
          // Pop an element from stack2(stack 2 maybe empty)
1786
          NodeRecord record2 = null;
1787
          try {
1788
              record2 = (NodeRecord) stack2.pop();
1789
          } catch (EmptyStackException ee) {
1790

    
1791
              logMetacat.error(
1792
                      "Node stack2 is empty but stack1 isn't!");
1793
              throw new SAXException(UPDATEACCESSERROR);
1794
          }
1795
          // if two records are not same throw a exception
1796
          if (!record1.contentEquals(record2)) {
1797
              logMetacat.info("Two records from new and old stack are not "
1798
                                      + "same!" + record1 + "--" +record2);
1799
              throw new SAXException(UPDATEACCESSERROR);
1800
          }//if
1801
      }//while
1802

    
1803
      // now stack1 is empty and we should make sure stack2 is empty too
1804
      if (!stack2.isEmpty()) {
1805

    
1806
          logMetacat.info(
1807
                  "stack2 still have some elements while stack1 "
1808
                          + "is empty! ");
1809
          throw new SAXException(UPDATEACCESSERROR);
1810
      }//if
1811
  }//comparingNodeStacks
1812

    
1813

    
1814
    /* The method to write all access rule into db */
1815
    private void writeAccessRuleToDB() throws SAXException
1816
    {
1817
        // delete xml_accesssubtree table record for this docid
1818
        deleteAccessSubTreeRecord(docid);
1819
        //write additional access rule, and old records in xml_access will be
1820
        //deleted too
1821
        //System.out.println("before write additional access rules");
1822
        writeadditionalAccessRuleToDB();
1823
        //System.out.println("after write additional access rules");
1824
        //write top leve access rule, and old records in xml_access will be
1825
        //deleted too
1826

    
1827
        if (topAccessSection != null){
1828
          writeTopLevelAccessRuleToDB();
1829
        }
1830
        //System.out.println("after write top access rules");
1831
    }//writeAccessRuleToDB
1832

    
1833

    
1834
    /* The method will figure out access reference for given access section -
1835
     * return a new AccessSection which contain access rules that be referenced.
1836
     * And will write the access subtree into xml_access table.
1837
     * this is a recursive method
1838
     */
1839
   private AccessSection resolveAccessRuleReference(AccessSection access)
1840
                                                    throws SAXException
1841
   {
1842
     if (access == null)
1843
     {
1844
       logMetacat.info("access module is null in " +
1845
                                "resolveAccessRulesReference");
1846
       throw new SAXException("An access modules is null");
1847
     }
1848
     String subTreeId = access.getSubTreeId();
1849
     if (subTreeId == null ||
1850
         (subTreeId != null && !accessSubTreeAlreadyWriteDBList.contains(subTreeId)))
1851
     {
1852
        // we should record this access subtree into accesssubtree table.
1853
        // subtreeId is null means it can't be referenced. So this tree couldn't
1854
        // be stored twise in the table. Subtree is not null, but it is in
1855
        // hash yet, so it is new one.
1856
        writeAccessSubTreeIntoDB(access);
1857
        if (subTreeId != null)
1858
        {
1859
          accessSubTreeAlreadyWriteDBList.put(subTreeId, subTreeId);
1860
        }
1861
     }
1862

    
1863
     String reference = access.getReferences();
1864
     if (reference != null)
1865
     {
1866
       // find the reference in top level
1867
       String topSubtreeId = topAccessSection.getSubTreeId();
1868
       if (topSubtreeId != null && topSubtreeId.equals(reference))
1869
       {
1870
          return resolveAccessRuleReference(topAccessSection);
1871
       }
1872
       else
1873
       {
1874
           // search it the additional access
1875
           for ( int i = 0; i <additionalAccessVector.size(); i++)
1876
           {
1877
             AccessSection additionalAccess = (AccessSection)
1878
                           additionalAccessVector.elementAt(i);
1879
             String additionId = additionalAccess.getSubTreeId();
1880
             if (additionId != null && additionId.equals(reference))
1881
             {
1882
               return resolveAccessRuleReference(additionalAccess);
1883
             }// if
1884
           }// for
1885

    
1886
           // search possible referenced access hashtable
1887
           if (possibleReferencedAccessHash.containsKey(reference))
1888
           {
1889
             AccessSection referenceAccess = (AccessSection)
1890
                         possibleReferencedAccessHash.get(reference);
1891
             return resolveAccessRuleReference(referenceAccess);
1892
           }
1893

    
1894
           // if hit here, this means you don't find any id match the reference
1895
           // throw a exception
1896
           throw new SAXException("No access module's id match the reference id");
1897
       }
1898
     }
1899
     else
1900
     {
1901
       // base line reference == null
1902
       AccessSection newAccessSection = new AccessSection();
1903
       access.copyPermOrderAndAccessRules(newAccessSection);
1904
       return newAccessSection;
1905
     }
1906
   }//resolveAccessRuleReference
1907

    
1908
   /* This method will return a id which points a real distribution block if
1909
    *  given a distribution id which is a reference. If the given id is a real
1910
    *  distribution the id itself will be returned.
1911
    *  Here is segment of eml
1912
    *   <distribution id ="100"><online><url>abc</url></online></distribution>
1913
    *   <distribution id ="200"><reference>100</reference></distribution>
1914
    * if the given value is 200, 100 will be returned.
1915
    * if the given value is 100, 100 will be returned.
1916
    */
1917
   private String resolveDistributionReference(String givenId)
1918
   {
1919
      if (givenId == null )
1920
      {
1921
        return null;
1922
      }
1923
      if (!distributionReferenceList.containsKey(givenId))
1924
      {
1925
        //this is not reference distribution block, return given id
1926
        return givenId;
1927
      }
1928
      else
1929
      {
1930
         String referencedId = (String) distributionReferenceList.get(givenId);
1931
         // search util the referenced id is not in dsitribtionReferenceList
1932
         while (distributionReferenceList.containsKey(referencedId))
1933
         {
1934
           referencedId = (String) distributionReferenceList.get(referencedId);
1935
         }
1936
         return referencedId;
1937
      }
1938
   }
1939

    
1940

    
1941
  /* The method to write top level access rule into db. The old rules will be
1942
   * deleted
1943
   * If no describedId in the access object, this access rules will be ingorned
1944
   */
1945
  private void writeadditionalAccessRuleToDB() throws SAXException
1946
  {
1947
     //System.out.println("in write additional");
1948
     // we should delete all inline access rules in xml_access if
1949
     // user has all permission
1950
     if (!needToCheckAccessModule)
1951
     {
1952
       deleteAllInlineDataAccessRules();
1953
     }
1954
     for (int i=0; i < additionalAccessVector.size(); i++)
1955
     {
1956
       //System.out.println("in for loop of write additional");
1957
       AccessSection access = (AccessSection)additionalAccessVector.elementAt(i);
1958
       Vector describeIdList = access.getDescribedIdList();
1959
       // if this access is a reference, a new access object will be created
1960
       // which contains the real access rules referenced. Also, each access tree
1961
       // will be write into xml_accesssubtee table
1962
       AccessSection newAccess = resolveAccessRuleReference(access);
1963
       String permOrder = newAccess.getPermissionOrder();
1964
       Vector accessRule = newAccess.getAccessRules();
1965

    
1966
       if (describeIdList == null || describeIdList.isEmpty())
1967
       {
1968
         continue;
1969
       }
1970

    
1971
       for (int j = 0; j < describeIdList.size(); j++)
1972
       {
1973
         String subreeid = (String)describeIdList.elementAt(j);
1974
         logMetacat.info("describe id in additional access " +
1975
                                  subreeid);
1976
         // we need to figure out the real id if this subreeid is points to
1977
         // a distribution reference.
1978
         subreeid = resolveDistributionReference(subreeid);
1979
         if (subreeid != null && !subreeid.trim().equals(""))
1980
         {
1981
           logMetacat.info("subtree id is "+ subreeid +
1982
                                    " after resolve reference id" );
1983
           // if this id is for line data, we need to delete the records first
1984
           // then add new records. The key for deleting is subtee id
1985
           if (inlineDistributionIdList.containsKey(subreeid))
1986
           {
1987
             String inlineFileName = (String)
1988
                                        inlineDistributionIdList.get(subreeid);
1989
             deleteSubtreeAccessRule(subreeid);
1990
             logMetacat.info("Write inline data access into " +
1991
                                   "xml_access table for"+ inlineFileName);
1992
             writeGivenAccessRuleIntoDB(permOrder, accessRule,
1993
                                        inlineFileName, subreeid);
1994
           }
1995
           else if (onlineURLDistributionIdList.containsKey(subreeid))
1996
           {
1997
             String url = (String)onlineURLDistributionIdList.get(subreeid);
1998
             //this method will extrace data file id from url. It also will
1999
             // check if user can change the access rules for the data file id.
2000
             // if couldn't, it will throw a exception. Morover, the docid will
2001
             // be to relation id vector too.
2002
             // for online data, the subtree id we set is null.
2003
             // So in xml_access, only the inline data subteeid is not null
2004
             String dataFileName = handleOnlineUrlDataFile(url);
2005
             logMetacat.info("The data fileName in online url " +
2006
                                      dataFileName);
2007
             if (dataFileName != null)
2008
             {
2009
               deletePermissionsInAccessTableForDoc(dataFileName);
2010
               writeGivenAccessRuleIntoDB(permOrder, accessRule,
2011
                                          dataFileName, null);
2012
               logMetacat.info("Write online data access into " +
2013
                                   "xml_access table for " + dataFileName);
2014
               // put the id into a hashtalbe. So when we run wirtetop level
2015
               // access, those id will be ignored because they already has
2016
               // additional access rules
2017
               onlineURLIdHasadditionalAccess.put(subreeid, subreeid);
2018
             }
2019
           }//elseif
2020
         }//if
2021
       }//for
2022
     }//for
2023

    
2024
   }//writeAdditonalLevelAccessRuletoDB
2025

    
2026

    
2027
    /* The method to write additional access rule into db. */
2028
    private void writeTopLevelAccessRuleToDB() throws SAXException
2029
    {
2030
       // if top access is reference, we need figure out the real access rules
2031
       // it points to
2032
       //System.out.println("permorder in top level" + topAccessSection.getPermissionOrder());
2033
       AccessSection newAccess = resolveAccessRuleReference(topAccessSection);
2034
       //System.out.println("permorder in new level" + newAccess.getPermissionOrder());
2035
       String permOrder = newAccess.getPermissionOrder();
2036
       Vector accessRule = newAccess.getAccessRules();
2037
       String subtree     = null;
2038
       // document itself
2039
       deletePermissionsInAccessTableForDoc(docid);
2040
       writeGivenAccessRuleIntoDB(permOrder, accessRule, docid, subtree);
2041
       // for online data, it includes with id and without id.
2042
       // 1. for the data with subtree id, we should ignore the ones already in
2043
       // the hash - onlineURLIdHasAddionalAccess.
2044
       // 2. for those without subreeid, it couldn't have additional access and we
2045
       // couldn't ignore it.
2046
       // 3. for inline data, we need do nothing because if it doesn't have
2047
       // additional access, it default value is the top one.
2048

    
2049
       // here is the online url with id
2050
       Enumeration em = onlineURLDistributionIdList.keys();
2051
       while (em.hasMoreElements())
2052
       {
2053
         String onlineSubtreeId = (String)em.nextElement();
2054
         if (!onlineURLIdHasadditionalAccess.containsKey(onlineSubtreeId))
2055
         {
2056
            String url =
2057
                       (String)onlineURLDistributionIdList.get(onlineSubtreeId);
2058
            String onlineDataId = handleOnlineUrlDataFile(url);
2059
            if (onlineDataId != null)
2060
            {
2061
              deletePermissionsInAccessTableForDoc(onlineDataId);
2062
              writeGivenAccessRuleIntoDB(permOrder, accessRule,
2063
                                         onlineDataId, subtree);
2064
            }
2065

    
2066
         }
2067
       }//while
2068

    
2069
       // here is the onlineURL without id
2070
       for (int i= 0; i < onlineURLDistributionListWithoutId.size(); i++)
2071
       {
2072
         String url = (String)onlineURLDistributionListWithoutId.elementAt(i);
2073
         String onlineDataId = handleOnlineUrlDataFile(url);
2074
         if (onlineDataId != null)
2075
         {
2076
           deletePermissionsInAccessTableForDoc(onlineDataId);
2077
           writeGivenAccessRuleIntoDB(permOrder, accessRule,
2078
                                         onlineDataId, subtree);
2079
         }
2080
       }//for
2081
    }//writeTopAccessRuletoDB
2082

    
2083
    /* Write a gaven access rule into db */
2084
    private void writeGivenAccessRuleIntoDB(String permOrder, Vector<AccessRule> accessRules,
2085
                     String dataId, String subTreeId) throws SAXException
2086
    {
2087
      if (permOrder == null || permOrder.trim().equals("") || dataId == null ||
2088
          dataId.trim().equals("") || accessRules == null ||
2089
          accessRules.isEmpty())
2090
      {
2091
        logMetacat.info("The access object is null and tried to " +
2092
                                  " write to xml_access table");
2093
        throw new SAXException("The access object is null");
2094
      }
2095
       try {
2096
           AccessControlForSingleFile acfsf = new AccessControlForSingleFile(dataId);
2097
           // go through every rule
2098
           for (int i = 0; i < accessRules.size(); i++) {
2099
               AccessRule rule = accessRules.elementAt(i);
2100
               String permType = rule.getPermissionType();
2101
               int permission = rule.getPermission();
2102
               logMetacat.info("permission in accesstable: " + permission);
2103
               logMetacat.info("Permtype in accesstable: " + permType);
2104
               // go through every principle in rule
2105
               Vector nameVector = rule.getPrincipal();
2106
               for (int j = 0; j < nameVector.size(); j++) {
2107
                   String prName = (String) nameVector.elementAt(j);
2108
					logMetacat.debug("Principal in accesstable: " + prName);
2109
					XMLAccessDAO dao = new XMLAccessDAO();
2110
					dao.setAccessFileId(docid);
2111
					dao.setDocId(dataId);
2112
					dao.setPermission(new Long(permission));
2113
					dao.setPermOrder(permOrder);
2114
					dao.setPermType(permType);
2115
					dao.setPrincipalName(prName);
2116
					dao.setSubTreeId(subTreeId);
2117
					// insert if it does not exist
2118
					if (!acfsf.accessControlExists(dao)) {
2119
						acfsf.insertPermissions(dao);
2120
					}
2121
               }//for
2122
           }//for
2123
           
2124
       }//try
2125
       catch (Exception e) {
2126
           throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
2127
                   + e.getMessage());
2128
       }//catch
2129

    
2130
    }//writeGivenAccessRuleIntoDB
2131

    
2132

    
2133
    /* Delete from db all permission for resources related to @docid if any. */
2134
    private void deletePermissionsInAccessTableForDoc(String docid)
2135
            throws SAXException
2136
    {
2137
        Statement stmt = null;
2138
        try {
2139
            // delete all acl records for resources related to @aclid if any
2140
            stmt = connection.createStatement();
2141
            // Increase DBConnection usage count
2142
            connection.increaseUsageCount(1);
2143
            stmt.execute("DELETE FROM xml_access WHERE docid = '"
2144
                    + docid + "'");
2145

    
2146
        } catch (SQLException e) {
2147
            throw new SAXException(e.getMessage());
2148
        } finally {
2149
            try {
2150
                stmt.close();
2151
            } catch (SQLException ee) {
2152
                throw new SAXException(ee.getMessage());
2153
            }
2154
        }
2155
    }//deletePermissionsInAccessTable
2156

    
2157
    /* Delete access rules from xml_access for a subtee id */
2158
    private void deleteSubtreeAccessRule(String subtreeid) throws SAXException
2159
    {
2160
      Statement stmt = null;
2161
       try
2162
       {
2163
           stmt = connection.createStatement();
2164
           // Increase DBConnection usage count
2165
           connection.increaseUsageCount(1);
2166
           stmt.execute("DELETE FROM xml_access WHERE accessfileid = '"
2167
                   + docid + "' AND subtreeid ='" + subtreeid +"'");
2168
       }
2169
       catch (SQLException e)
2170
       {
2171
           throw new SAXException(e.getMessage());
2172
       }
2173
       finally
2174
       {
2175
           try
2176
           {
2177
               stmt.close();
2178
           }
2179
           catch (SQLException ee)
2180
           {
2181
               throw new SAXException(ee.getMessage());
2182
           }
2183
       }
2184

    
2185
    }
2186

    
2187
    private void deleteAllInlineDataAccessRules() throws SAXException
2188
    {
2189
      Statement stmt = null;
2190
       try
2191
       {
2192
           stmt = connection.createStatement();
2193
           // Increase DBConnection usage count
2194
           connection.increaseUsageCount(1);
2195
           stmt.execute("DELETE FROM xml_access WHERE accessfileid = '"
2196
                   + docid + "' AND subtreeid IS NOT NULL");
2197
       }
2198
       catch (SQLException e)
2199
       {
2200
           throw new SAXException(e.getMessage());
2201
       }
2202
       finally
2203
       {
2204
           try
2205
           {
2206
               stmt.close();
2207
           }
2208
           catch (SQLException ee)
2209
           {
2210
               throw new SAXException(ee.getMessage());
2211
           }
2212
       }
2213

    
2214
    }
2215

    
2216
    /*
2217
     * In order to make sure only usr has "all" permission can update access
2218
     * subtree in eml document we need to keep access subtree info in
2219
     * xml_accesssubtree table, such as docid, version, startnodeid, endnodeid
2220
     */
2221
    private void writeAccessSubTreeIntoDB(AccessSection accessSection)
2222
                                          throws SAXException
2223
    {
2224
        if (accessSection == null)
2225
        {
2226

    
2227
          logMetacat.info("Access object is null and tried to write "+
2228
                                   "into access subtree table");
2229
          throw new SAXException("The access object is null to write access " +
2230
                                 "sbutree");
2231
        }
2232

    
2233
        String sql = null;
2234
        PreparedStatement pstmt = null;
2235
        sql = "INSERT INTO xml_accesssubtree (docid, rev, controllevel, "
2236
                + "subtreeid, startnodeid, endnodeid) VALUES "
2237
                + " (?, ?, ?, ?, ?, ?)";
2238
        try
2239
        {
2240

    
2241
            pstmt = connection.prepareStatement(sql);
2242
            // Increase DBConnection usage count
2243
            connection.increaseUsageCount(1);
2244
            String level = accessSection.getControlLevel();
2245
            long startNodeId = -1;
2246
            if (level != null && level.equals(DATAACCESSLEVEL))
2247
            {
2248
              // for additional access module the start node id should be
2249
              // descirbes element id
2250
              startNodeId = accessSection.getStartedDescribesNodeId();
2251
              // if in additional access, there is not describes element,
2252
              // in this senario, describesNodeId will be -1. Then we should
2253
              // the start access element id
2254
              if (startNodeId == -1)
2255
              {
2256
                startNodeId = accessSection.getStartNodeId();
2257
              }
2258
            }
2259
            else
2260
            {
2261
                startNodeId = accessSection.getStartNodeId();
2262
            }
2263

    
2264
            long endNodeId = accessSection.getEndNodeId();
2265
            String sectionId = accessSection.getSubTreeId();
2266

    
2267
            if (startNodeId ==-1 || endNodeId == -1)
2268
            {
2269
              throw new SAXException("Don't find start node or end node id " +
2270
                                      "for the access subtee");
2271

    
2272
            }
2273

    
2274
            // Bind the values to the query
2275
            pstmt.setString(1, docid);
2276
            logMetacat.info("Docid in access-subtreetable: " + docid);
2277
            pstmt.setInt(2, (new Integer(revision)).intValue());
2278
            logMetacat.info("rev in accesssubtreetable: " + revision);
2279
            pstmt.setString(3, level);
2280
            logMetacat.info("contorl level in access-subtree table: "
2281
                    + level);
2282
            pstmt.setString(4, sectionId);
2283
            logMetacat.info("Subtree id in access-subtree table: "
2284
                    + sectionId);
2285
            pstmt.setLong(5, startNodeId);
2286
            logMetacat.info("Start node id is: " + startNodeId);
2287
            pstmt.setLong(6, endNodeId);
2288
            logMetacat.info("End node id is: " + endNodeId);
2289
            logMetacat.debug("Eml200SAXHandler.writeAccessSubTreeIntoDB - executing SQL: " + pstmt.toString());
2290
            pstmt.execute();
2291
            pstmt.close();
2292
        }//try
2293
        catch (SQLException e)
2294
        {
2295
            throw new SAXException("EMLSAXHandler.writeAccessSubTreeIntoDB(): "
2296
                    + e.getMessage());
2297
        }//catch
2298
        finally
2299
        {
2300
            try
2301
            {
2302
                pstmt.close();
2303
            }
2304
            catch (SQLException ee)
2305
            {
2306
                throw new SAXException(
2307
                        "EMLSAXHandler.writeAccessSubTreeIntoDB(): "
2308
                                + ee.getMessage());
2309
            }
2310
        }//finally
2311

    
2312
    }//writeAccessSubtreeIntoDB
2313

    
2314
    /* Delete every access subtree record from xml_accesssubtree. */
2315
    private void deleteAccessSubTreeRecord(String docId) throws SAXException
2316
    {
2317
        Statement stmt = null;
2318
        try {
2319
            // delete all acl records for resources related to @aclid if any
2320
            stmt = connection.createStatement();
2321
            // Increase DBConnection usage count
2322
            connection.increaseUsageCount(1);                   
2323
            logMetacat.debug("running sql: DELETE FROM xml_accesssubtree WHERE docid = '"
2324
                    + docId + "'");
2325
            stmt.execute("DELETE FROM xml_accesssubtree WHERE docid = '"
2326
                    + docId + "'");
2327

    
2328
        } catch (SQLException e) {
2329
            throw new SAXException(e.getMessage());
2330
        } finally {
2331
            try {
2332
                stmt.close();
2333
            } catch (SQLException ee) {
2334
                throw new SAXException(ee.getMessage());
2335
            }
2336
        }
2337
    }//deleteAccessSubTreeRecord
2338

    
2339
    // open a file writer for writing inline data to file
2340
    private Writer createInlineDataFileWriter(String fileName, String encoding)
2341
            throws SAXException
2342
    {
2343
        Writer writer = null;
2344
        String path;
2345
        try {
2346
        	 path = PropertyService.getProperty("application.inlinedatafilepath");
2347
        } catch (PropertyNotFoundException pnfe) {
2348
            throw new SAXException(pnfe.getMessage());
2349
        }
2350
        /*
2351
         * File inlineDataDirectory = new File(path);
2352
         */
2353
        String newFile = path + "/" + fileName;
2354
        logMetacat.info("inline file name: " + newFile);
2355
        try {
2356
            // true means append
2357
        	writer = new OutputStreamWriter(new FileOutputStream(newFile, true), encoding);
2358
        } catch (IOException ioe) {
2359
            throw new SAXException(ioe.getMessage());
2360
        }
2361
        return writer;
2362
    }
2363

    
2364
    // write inline data into file system and return file name(without path)
2365
    private void writeInlineDataIntoFile(Writer writer, StringBuffer data)
2366
            throws SAXException
2367
    {
2368
        try {
2369
            writer.write(data.toString());
2370
            writer.flush();
2371
        } catch (Exception e) {
2372
            throw new SAXException(e.getMessage());
2373
        }
2374
    }
2375

    
2376

    
2377

    
2378
    /*
2379
     * In eml2, the inline data wouldn't store in db, it store in file system
2380
     * The db stores file name(without path). We got the old file name from db
2381
     * and compare to the new in line data file
2382
     */
2383
    public boolean compareInlineDataFiles(String oldFileName, String newFileName)
2384
            throws McdbException
2385
    {
2386
        // this method need to be testing
2387
        boolean same = true;
2388
        String data = null;
2389
        try {
2390
        	String path = PropertyService.getProperty("application.inlinedatafilepath");
2391
			// the new file name will look like path/docid.rev.2
2392
			File inlineDataDirectory = new File(path);
2393
			File oldDataFile = new File(inlineDataDirectory, oldFileName);
2394
			File newDataFile = new File(inlineDataDirectory, newFileName);
2395

    
2396
            Reader oldFileReader = new InputStreamReader(new FileInputStream(oldDataFile), encoding);
2397
            BufferedReader oldStringReader = new BufferedReader(oldFileReader);
2398
            Reader newFileReader = new InputStreamReader(new FileInputStream(newDataFile), encoding);
2399
            BufferedReader newStringReader = new BufferedReader(newFileReader);
2400
            // read first line of data
2401
            String oldString = oldStringReader.readLine();
2402
            String newString = newStringReader.readLine();
2403

    
2404
            // at the end oldstring will be null
2405
            while (oldString != null) {
2406
                oldString = oldStringReader.readLine();
2407
                newString = newStringReader.readLine();
2408
                if (!oldString.equals(newString)) {
2409
                    same = false;
2410
                    break;
2411
                }
2412
            }
2413

    
2414
            // if oldString is null but newString is not null, they are same
2415
            if (same) {
2416
                if (newString != null) {
2417
                    same = false;
2418
                }
2419
            }
2420

    
2421
        } catch (Exception e) {
2422
            throw new McdbException(e.getMessage());
2423
        }
2424
        logMetacat.info("the inline data retrieve from file: " + data);
2425
        return same;
2426
    }
2427

    
2428
   /*
2429
	 * Copy a old line file to a new inline file
2430
	 */
2431
	public void copyInlineFile(String inlineDistributionId, String newFileName)
2432
			throws SAXException {
2433
		if (inlineDistributionId == null || newFileName == null) {
2434
			throw new SAXException("Could not copy inline file from old one to new "
2435
					+ "one!");
2436
		}
2437
		// get old file id from previousUnreadable data object
2438
		String oldInlineInternalFileName = (String) previousUnreadableInlineDataObjectHash
2439
				.get(inlineDistributionId);
2440

    
2441
		if (oldInlineInternalFileName == null
2442
				|| oldInlineInternalFileName.trim().equals("")) {
2443
			throw new SAXException("Could not copy inline file from old one to new "
2444
					+ "one because can't find old file name");
2445
		}
2446
		logMetacat.info("in handle inline data");
2447
		logMetacat.info("the inline data file name from xml_access is: "
2448
				+ oldInlineInternalFileName);
2449

    
2450
		InputStream oldFileReader = null;
2451
		OutputStream newFileWriter = null;
2452
		try {
2453
			String path = PropertyService.getProperty("application.inlinedatafilepath");
2454
			// the new file name will look like path/docid.rev.2
2455
			File inlineDataDirectory = new File(path);
2456
			File oldDataFile = new File(inlineDataDirectory, oldInlineInternalFileName);
2457
			File newDataFile = new File(inlineDataDirectory, newFileName);
2458

    
2459
			oldFileReader = new FileInputStream(oldDataFile);
2460
			newFileWriter = new FileOutputStream(newDataFile);
2461
			byte[] buf = new byte[4 * 1024]; // 4K buffer
2462
			int b = oldFileReader.read(buf);
2463
			while (b != -1) {
2464
				newFileWriter.write(buf, 0, b);
2465
				b = oldFileReader.read(buf);
2466
			}
2467
		} catch (Exception e) {
2468
			throw new SAXException(e.getMessage());
2469
		} finally {
2470
			if (oldFileReader != null) {
2471
				try {
2472
					oldFileReader.close();
2473
				} catch (Exception ee) {
2474
					throw new SAXException(ee.getMessage());
2475
				}
2476

    
2477
			}
2478
			if (newFileWriter != null) {
2479
				try {
2480
					newFileWriter.close();
2481
				} catch (Exception ee) {
2482
					throw new SAXException(ee.getMessage());
2483
				}
2484

    
2485
			}
2486
		}
2487
	}
2488

    
2489

    
2490
    // if xml file failed to upload, we need to call this method to delete
2491
    // the inline data already in file system
2492
    public void deleteInlineFiles() throws SAXException
2493
    {
2494
        if (!inlineFileIDList.isEmpty()) {
2495
            for (int i = 0; i < inlineFileIDList.size(); i++) {
2496
                String fileName = (String) inlineFileIDList.elementAt(i);
2497
                deleteInlineDataFile(fileName);
2498
            }
2499
        }
2500
    }
2501

    
2502
    /* delete the inline data file */
2503
    private void deleteInlineDataFile(String fileName) throws SAXException
2504
    {
2505
    	String path;
2506
    	try {
2507
    		path = PropertyService.getProperty("application.inlinedatafilepath");
2508
    	} catch (PropertyNotFoundException pnfe) {
2509
    		throw new SAXException ("Could not find inline data file path: " 
2510
    				+ pnfe.getMessage());
2511
    	}
2512
        File inlineDataDirectory = new File(path);
2513
        File newFile = new File(inlineDataDirectory, fileName);
2514
        newFile.delete();
2515

    
2516
    }
2517

    
2518
    /*
2519
	 * In eml2, the inline data wouldn't store in db, it store in file system
2520
	 * The db stores file name(without path).
2521
	 */
2522
	public static Reader readInlineDataFromFileSystem(String fileName, String encoding)
2523
			throws McdbException {
2524
		// BufferedReader stringReader = null;
2525
		Reader fileReader = null;
2526
		try {
2527
			String path = PropertyService.getProperty("application.inlinedatafilepath");
2528
			// the new file name will look like path/docid.rev.2
2529
			File inlineDataDirectory = new File(path);
2530
			File dataFile = new File(inlineDataDirectory, fileName);
2531

    
2532
			fileReader = new InputStreamReader(new FileInputStream(dataFile), encoding);
2533
			// stringReader = new BufferedReader(fileReader);
2534
		} catch (Exception e) {
2535
			throw new McdbException(e.getMessage());
2536
		}
2537
		// return stringReader;
2538
		return fileReader;
2539
	}
2540

    
2541
    /* Delete relations */
2542
    private void deleteRelations() throws SAXException
2543
    {
2544
        PreparedStatement pStmt = null;
2545
        String sql = "DELETE FROM xml_relation where docid =?";
2546
        try {
2547
            pStmt = connection.prepareStatement(sql);
2548
            //bind variable
2549
            pStmt.setString(1, docid);
2550
            //execute query
2551
            logMetacat.debug("Eml200SAXHandler.deleteRelations - executing SQL: " + pStmt.toString());
2552
            pStmt.execute();
2553
            pStmt.close();
2554
        }//try
2555
        catch (SQLException e) {
2556
            throw new SAXException("EMLSAXHandler.deleteRelations(): "
2557
                    + e.getMessage());
2558
        }//catch
2559
        finally {
2560
            try {
2561
                pStmt.close();
2562
            }//try
2563
            catch (SQLException ee) {
2564
                throw new SAXException("EMLSAXHandler.deleteRelations: "
2565
                        + ee.getMessage());
2566
            }//catch
2567
        }//finally
2568
    }
2569

    
2570
    /* Write an online data file id into xml_relation table. The dataId shouldnot
2571
     * have the revision
2572
     */
2573
    private void writeOnlineDataFileIdIntoRelationTable(String dataId)
2574
            throws SAXException
2575
    {
2576
        PreparedStatement pStmt = null;
2577
        String sql = "INSERT into xml_relation (docid, packagetype, subject, "
2578
                + "relationship, object) values (?, ?, ?, ?, ?)";
2579
        try {
2580
            pStmt = connection.prepareStatement(sql);
2581
            //bind variable
2582
            pStmt.setString(1, docid);
2583
            pStmt.setString(2, doctype);
2584
            pStmt.setString(3, docid);
2585
            pStmt.setString(4, RELATION);
2586
            pStmt.setString(5, dataId);
2587
            //execute query
2588
            logMetacat.debug("Eml200SAXHandler.writeOnlineDataFileIdIntoRelationTable - executing SQL: " + pStmt.toString());
2589
            pStmt.execute();
2590
            pStmt.close();
2591
        }//try
2592
        catch (SQLException e) {
2593
            throw new SAXException(
2594
                    "EMLSAXHandler.writeOnlineDataFileIdIntoRelationTable(): "
2595
                            + e.getMessage());
2596
        }//catch
2597
        finally {
2598
            try {
2599
                pStmt.close();
2600
            }//try
2601
            catch (SQLException ee) {
2602
                throw new SAXException(
2603
                        "EMLSAXHandler.writeOnlineDataFileIdIntoRelationTable(): "
2604
                                + ee.getMessage());
2605
            }//catch
2606
        }//finally
2607

    
2608
    }//writeOnlineDataFileIdIntoRelationTable
2609

    
2610
    /*
2611
     * This method will handle data file in online url. If the data file is in
2612
     * ecogrid protocol, then the datafile identifier(without rev) be returned.
2613
     * otherwise, null will be returned.
2614
     * If the data file doesn't exsit in xml_documents or
2615
     * xml_revision table, or the user has all permission to the data file if
2616
     * the docid already existed, the data file id (without rev)will be returned
2617
     * NEED to do:
2618
     * We should also need to implement http and ftp. Those
2619
     * external files should be download and assign a data file id to it.
2620
     */
2621
    private String handleOnlineUrlDataFile(String url) throws SAXException
2622
    {
2623
      logMetacat.warn("The url is "+ url);
2624
      String docid = null;
2625
      // if the url is not a ecogrid protocol, null will be getten
2626
      String accessionNumber =
2627
    	  DocumentUtil.getAccessionNumberFromEcogridIdentifier(url);
2628
      if (accessionNumber != null)
2629
      {
2630
        // handle ecogrid protocol
2631
        // get rid of revision number to get the docid.
2632
        docid = DocumentUtil.getDocIdFromAccessionNumber(accessionNumber);
2633
        onlineDataFileIdInRelationVector.add(docid);
2634
        try
2635
        {
2636

    
2637
          if (!AccessionNumber.accNumberUsed(docid))
2638
          {
2639
            return docid;
2640
          }
2641
          PermissionController controller = new
2642
              PermissionController(accessionNumber);
2643
          if (controller.hasPermission(
2644
              user, groups, AccessControlInterface.ALLSTRING))
2645
          {
2646
            return docid;
2647
          }
2648
          else
2649
          {
2650
            throw new SAXException("User: " + user + " does not have permission to update " +
2651
                  "access rules for data file "+ docid);
2652
          }
2653
        }//try
2654
        catch(Exception e)
2655
        {
2656
          logMetacat.error("Error in " +
2657
                                "Eml200SAXHanlder.handleOnlineUrlDataFile is " +
2658
                                 e.getMessage());
2659
          throw new SAXException(e.getMessage());
2660
        }
2661
      }
2662
      return docid;
2663
    }
2664

    
2665
    private void compareElementNameSpaceAttributes(Stack unchangableNodeStack,
2666
            Hashtable nameSpaces, Attributes attributes, String localName,
2667
            String error) throws SAXException
2668
    {
2669
        //Get element subtree node stack (element node)
2670
        NodeRecord elementNode = null;
2671
        try {
2672
            elementNode = (NodeRecord) unchangableNodeStack.pop();
2673
        } catch (EmptyStackException ee) {
2674
            logMetacat.error("Node stack is empty for element data");
2675
            throw new SAXException(error);
2676
        }
2677
        logMetacat.info("current node type from xml is ELEMENT");
2678
        logMetacat.info("node type from stack: "
2679
                + elementNode.getNodeType());
2680
        logMetacat.info("node name from xml document: " + localName);
2681
        logMetacat.info("node name from stack: "
2682
                + elementNode.getNodeName());
2683
        logMetacat.info("node data from stack: "
2684
                + elementNode.getNodeData());
2685
        logMetacat.info("node id is: " + elementNode.getNodeId());
2686
        // if this node is not element or local name not equal or name space
2687
        // not
2688
        // equals, throw an exception
2689
        if (!elementNode.getNodeType().equals("ELEMENT")
2690
                || !localName.equals(elementNode.getNodeName()))
2691
        //  (uri != null && !uri.equals(elementNode.getNodePrefix())))
2692
        {
2693
            logMetacat.info("Inconsistence happend: ");
2694
            logMetacat.info("current node type from xml is ELEMENT");
2695
            logMetacat.info("node type from stack: "
2696
                    + elementNode.getNodeType());
2697
            logMetacat.info("node name from xml document: "
2698
                    + localName);
2699
            logMetacat.info("node name from stack: "
2700
                    + elementNode.getNodeName());
2701
            logMetacat.info("node data from stack: "
2702
                    + elementNode.getNodeData());
2703
            logMetacat.info("node id is: " + elementNode.getNodeId());
2704
            throw new SAXException(error);
2705
        }
2706

    
2707
        //compare namespace
2708
        Enumeration nameEn = nameSpaces.keys();
2709
        while (nameEn.hasMoreElements()) {
2710
            //Get namespacke node stack (element node)
2711
            NodeRecord nameNode = null;
2712
            try {
2713
                nameNode = (NodeRecord) unchangableNodeStack.pop();
2714
            } catch (EmptyStackException ee) {
2715
                logMetacat.error(
2716
                        "Node stack is empty for namespace data");
2717
                throw new SAXException(error);
2718
            }
2719

    
2720
            String prefixName = (String) nameEn.nextElement();
2721
            String nameSpaceUri = (String) nameSpaces.get(prefixName);
2722
            if (!nameNode.getNodeType().equals("NAMESPACE")
2723
                    || !prefixName.equals(nameNode.getNodeName())
2724
                    || !nameSpaceUri.equals(nameNode.getNodeData())) {
2725
                logMetacat.info("Inconsistence happend: ");
2726
                logMetacat.info(
2727
                        "current node type from xml is NAMESPACE");
2728
                logMetacat.info("node type from stack: "
2729
                        + nameNode.getNodeType());
2730
                logMetacat.info("current node name from xml is: "
2731
                        + prefixName);
2732
                logMetacat.info("node name from stack: "
2733
                        + nameNode.getNodeName());
2734
                logMetacat.info("current node data from xml is: "
2735
                        + nameSpaceUri);
2736
                logMetacat.info("node data from stack: "
2737
                        + nameNode.getNodeData());
2738
                logMetacat.info("node id is: " + nameNode.getNodeId());
2739
                throw new SAXException(error);
2740
            }
2741

    
2742
        }//while
2743

    
2744
        //compare attributes
2745
        for (int i = 0; i < attributes.getLength(); i++) {
2746
            NodeRecord attriNode = null;
2747
            try {
2748
                attriNode = (NodeRecord) unchangableNodeStack.pop();
2749

    
2750
            } catch (EmptyStackException ee) {
2751
                logMetacat.error(
2752
                        "Node stack is empty for attribute data");
2753
                throw new SAXException(error);
2754
            }
2755
            String attributeName = attributes.getQName(i);
2756
            String attributeValue = attributes.getValue(i);
2757
            logMetacat.info(
2758
                    "current node type from xml is ATTRIBUTE ");
2759
            logMetacat.info("node type from stack: "
2760
                    + attriNode.getNodeType());
2761
            logMetacat.info("current node name from xml is: "
2762
                    + attributeName);
2763
            logMetacat.info("node name from stack: "
2764
                    + attriNode.getNodeName());
2765
            logMetacat.info("current node data from xml is: "
2766
                    + attributeValue);
2767
            logMetacat.info("node data from stack: "
2768
                    + attriNode.getNodeData());
2769
            logMetacat.info("node id  is: " + attriNode.getNodeId());
2770

    
2771
            if (!attriNode.getNodeType().equals("ATTRIBUTE")
2772
                    || !attributeName.equals(attriNode.getNodeName())
2773
                    || !attributeValue.equals(attriNode.getNodeData())) {
2774
                logMetacat.info("Inconsistence happend: ");
2775
                logMetacat.info(
2776
                        "current node type from xml is ATTRIBUTE ");
2777
                logMetacat.info("node type from stack: "
2778
                        + attriNode.getNodeType());
2779
                logMetacat.info("current node name from xml is: "
2780
                        + attributeName);
2781
                logMetacat.info("node name from stack: "
2782
                        + attriNode.getNodeName());
2783
                logMetacat.info("current node data from xml is: "
2784
                        + attributeValue);
2785
                logMetacat.info("node data from stack: "
2786
                        + attriNode.getNodeData());
2787
                logMetacat.info("node is: " + attriNode.getNodeId());
2788
                throw new SAXException(error);
2789
            }
2790
        }//for
2791

    
2792
    }
2793

    
2794
    /* mehtod to compare current text node and node in db */
2795
    private void compareTextNode(Stack nodeStack, StringBuffer text,
2796
            String error) throws SAXException
2797
    {
2798
        NodeRecord node = null;
2799
        //get node from current stack
2800
        try {
2801
            node = (NodeRecord) nodeStack.pop();
2802
        } catch (EmptyStackException ee) {
2803
            logMetacat.error(
2804
                    "Node stack is empty for text data in startElement");
2805
            throw new SAXException(error);
2806
        }
2807
        logMetacat.info(
2808
                "current node type from xml is TEXT in start element");
2809
        logMetacat.info("node type from stack: " + node.getNodeType());
2810
        logMetacat.info("current node data from xml is: "
2811
                + text.toString());
2812
        logMetacat.info("node data from stack: " + node.getNodeData());
2813
        logMetacat.info("node name from stack: " + node.getNodeName());
2814
        logMetacat.info("node is: " + node.getNodeId());
2815
        if (!node.getNodeType().equals("TEXT")
2816
                || !(text.toString()).equals(node.getNodeData())) {
2817
            logMetacat.info("Inconsistence happend: ");
2818
            logMetacat.info(
2819
                    "current node type from xml is TEXT in start element");
2820
            logMetacat.info("node type from stack: "
2821
                    + node.getNodeType());
2822
            logMetacat.info("current node data from xml is: "
2823
                    + text.toString());
2824
            logMetacat.info("node data from stack: "
2825
                    + node.getNodeData());
2826
            logMetacat.info("node name from stack: "
2827
                    + node.getNodeName());
2828
            logMetacat.info("node is: " + node.getNodeId());
2829
            throw new SAXException(error);
2830
        }//if
2831
    }
2832

    
2833
    /* Comparet comment from xml and db */
2834
    private void compareCommentNode(Stack nodeStack, String string, String error)
2835
            throws SAXException
2836
    {
2837
        NodeRecord node = null;
2838
        try {
2839
            node = (NodeRecord) nodeStack.pop();
2840
        } catch (EmptyStackException ee) {
2841
            logMetacat.error("the stack is empty for comment data");
2842
            throw new SAXException(error);
2843
        }
2844
        logMetacat.info("current node type from xml is COMMENT");
2845
        logMetacat.info("node type from stack: " + node.getNodeType());
2846
        logMetacat.info("current node data from xml is: " + string);
2847
        logMetacat.info("node data from stack: " + node.getNodeData());
2848
        logMetacat.info("node is from stack: " + node.getNodeId());
2849
        // if not consistent terminate program and throw a exception
2850
        if (!node.getNodeType().equals("COMMENT")
2851
                || !string.equals(node.getNodeData())) {
2852
            logMetacat.info("Inconsistence happend: ");
2853
            logMetacat.info("current node type from xml is COMMENT");
2854
            logMetacat.info("node type from stack: "
2855
                    + node.getNodeType());
2856
            logMetacat.info(
2857
                    "current node data from xml is: " + string);
2858
            logMetacat.info("node data from stack: "
2859
                    + node.getNodeData());
2860
            logMetacat.info("node is from stack: " + node.getNodeId());
2861
            throw new SAXException(error);
2862
        }//if
2863
    }
2864

    
2865
    /* Compare whitespace from xml and db */
2866
   private void compareWhiteSpace(Stack nodeStack, String string, String error)
2867
           throws SAXException
2868
   {
2869
       NodeRecord node = null;
2870
       try {
2871
           node = (NodeRecord) nodeStack.pop();
2872
       } catch (EmptyStackException ee) {
2873
           logMetacat.error("the stack is empty for whitespace data");
2874
           throw new SAXException(error);
2875
       }
2876
       if (!node.getNodeType().equals("TEXT")
2877
               || !string.equals(node.getNodeData())) {
2878
           logMetacat.info("Inconsistence happend: ");
2879
           logMetacat.info(
2880
                   "current node type from xml is WHITESPACE TEXT");
2881
           logMetacat.info("node type from stack: "
2882
                   + node.getNodeType());
2883
           logMetacat.info(
2884
                   "current node data from xml is: " + string);
2885
           logMetacat.info("node data from stack: "
2886
                   + node.getNodeData());
2887
           logMetacat.info("node is from stack: " + node.getNodeId());
2888
           throw new SAXException(error);
2889
       }//if
2890
   }
2891

    
2892

    
2893

    
2894
}
(32-32/65)