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-12-07 12:18:24 -0800 (Wed, 07 Dec 2011) $'
11
 * '$Revision: 6744 $'
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.util.Date;
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.AccessControlInterface;
56
import edu.ucsb.nceas.metacat.accesscontrol.AccessRule;
57
import edu.ucsb.nceas.metacat.accesscontrol.AccessSection;
58
import edu.ucsb.nceas.metacat.database.DBConnection;
59
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
60
import edu.ucsb.nceas.metacat.properties.PropertyService;
61
import edu.ucsb.nceas.metacat.util.AuthUtil;
62
import edu.ucsb.nceas.metacat.util.DocumentUtil;
63
import edu.ucsb.nceas.metacat.util.MetacatUtil;
64
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
65

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

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

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

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

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

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

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

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

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

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

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

    
173
    // temporarily store distribution id
174
    private String distributionId = null;
175

    
176
    // flag to indicate to handle distribution
177
    private boolean proccessDistribution = false;
178

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

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

    
195
    private AccessSection topAccessSubTreeFromDB = null;
196

    
197
    private Vector additionalAccessSubTreeListFromDB = new Vector();
198

    
199
    private Hashtable referencedAccessSubTreeListFromDB = new Hashtable();
200

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

    
204
    private Vector additionalAccessVector = new Vector();
205

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

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

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

    
216
    // Indicator of inline data
217
    private boolean handleInlineData = false;
218

    
219
    private Hashtable inlineDataNameSpace = null;
220

    
221
    private Writer inlineDataFileWriter = null;
222

    
223
    private String inlineDataFileName = null;
224

    
225
    private int inLineDataIndex = 0;
226

    
227
    private Vector inlineFileIDList = new Vector();
228

    
229
    private boolean inAdditionalMetaData = false;
230

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

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

    
241
    private Hashtable previousUnreadableInlineDataObjectHash = new Hashtable();
242

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

    
249
    private Hashtable accessSubTreeAlreadyWriteDBList = new Hashtable();
250

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

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

    
269
    private int numberOfHitUnWritableInlineData = 0;
270

    
271
    // Constant
272
    private static final String EML = "eml";
273

    
274
    private static final String DESCRIBES = "describes";
275

    
276
    private static final String ADDITIONALMETADATA = "additionalMetadata";
277

    
278
    private static final String ORDER = "order";
279

    
280
    private static final String ID = "id";
281

    
282
    private static final String REFERENCES = "references";
283

    
284
    public static final String INLINE = "inline";
285

    
286
    private static final String ONLINE = "online";
287

    
288
    private static final String OFFLINE = "offline";
289

    
290
    private static final String CONNECTION = "connection";
291

    
292
    private static final String CONNECTIONDEFINITION = "connectionDefinition";
293

    
294
    private static final String URL = "url";
295

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

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

    
302
    public static final String TOPLEVEL = "top";
303

    
304
    public static final String DATAACCESSLEVEL = "dataAccess";
305

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

    
310
    private static final String RELATION = "Provides info for";
311

    
312
    private static final String DISTRIBUTION = "distribution";
313

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

    
341
            //Here is for  data object checking.
342
            if (action != null && action.equals("UPDATE")) {
343
            	
344
    			// we need to check if user can update access subtree			
345
    			int latestRevision = DBUtil.getLatestRevisionInDocumentTable(docid);
346
    			String previousDocid = 
347
    				docid + PropertyService.getProperty("document.accNumSeparator") + latestRevision;
348
    			
349
    			PermissionController control = new PermissionController(previousDocid);
350
            	
351
            	 // If the action is update and user doesn't have "ALL" permission
352
                // we need to check if user update access subtree
353
            	if ( !control.hasPermission(user, groups, AccessControlInterface.ALLSTRING) 
354
                        && !AuthUtil.isAdministrator(user, groups)) {
355
                		
356
                    needToCheckAccessModule = true;
357
                    topAccessSubTreeFromDB = getTopAccessSubTreeFromDB();
358
                    additionalAccessSubTreeListFromDB =
359
                                             getAdditionalAccessSubTreeListFromDB();
360
                    referencedAccessSubTreeListFromDB =
361
                                             getReferencedAccessSubTreeListFromDB();
362
            	}
363
            	
364
              //info about inline data object which user doesn't have read
365
              //permission the info come from xml_access table
366
              previousUnreadableInlineDataObjectHash = PermissionController.
367
                            getUnReadableInlineDataIdList(previousDocid, user, groups);
368

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

    
375
            }
376

    
377

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

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

    
400

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

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

    
450
     }
451

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

    
465
        try
466
        {
467

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

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

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

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

    
531
       try
532
       {
533

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

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

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

    
587

    
588

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

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

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

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

    
660

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

    
665
            }
666
            //==============================================================
667

    
668

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

    
676

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

    
685
                }
686

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

    
696
            }
697
            //==================================================================
698

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

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

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

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

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

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

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

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

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

    
837
              }
838

    
839
          }//for
840

    
841

    
842
           //=================================================================
843

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

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

    
877

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

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

    
906

    
907

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

    
915
                accessRule = new AccessRule();
916

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

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

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

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

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

    
957
            }
958

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

    
999

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

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

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

    
1053
    }
1054

    
1055

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

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

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

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

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

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

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

    
1200

    
1201

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

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

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

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

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

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

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

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

    
1315
                }
1316
            }//if handle text node
1317

    
1318

    
1319

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

    
1326

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

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

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

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

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

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

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

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

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

    
1400

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

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

    
1434
            }
1435

    
1436

    
1437

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

    
1457

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

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

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

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

    
1502
       }
1503
       return modified;
1504
     }
1505

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

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

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

    
1540
     }
1541

    
1542

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

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

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

    
1587

    
1588

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

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

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

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

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

    
1666
    }
1667

    
1668

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

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

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

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

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

    
1707

    
1708

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

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

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

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

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

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

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

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

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

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

    
1812

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

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

    
1832

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

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

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

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

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

    
1939

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

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

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

    
2023
   }//writeAdditonalLevelAccessRuletoDB
2024

    
2025

    
2026
    /* The method to write additional access rule into db. */
2027
    private void writeTopLevelAccessRuleToDB() throws SAXException
2028
    {
2029
       // if top access is reference, we need figure out the real access rules
2030
       // it points to
2031
       //System.out.println("permorder in top level" + topAccessSection.getPermissionOrder());
2032
       AccessSection newAccess = resolveAccessRuleReference(topAccessSection);
2033
       //System.out.println("permorder in new level" + newAccess.getPermissionOrder());
2034
       String permOrder = newAccess.getPermissionOrder();
2035
       Vector accessRule = newAccess.getAccessRules();
2036
       String subtree     = null;
2037
       
2038
       // document itself
2039
       // use GUID
2040
       String guid = null;
2041
		try {
2042
			guid = IdentifierManager.getInstance().getGUID(docid, Integer.valueOf(revision));
2043
		} catch (NumberFormatException e) {
2044
			throw new SAXException(e.getMessage(), e);
2045
		} catch (McdbDocNotFoundException e) {
2046
			// register the default mapping now
2047
			guid = docid + "." + revision;
2048
			IdentifierManager.getInstance().createMapping(guid, guid);
2049
		}
2050
       deletePermissionsInAccessTableForDoc(guid);
2051
       writeGivenAccessRuleIntoDB(permOrder, accessRule, guid, subtree);
2052
       
2053
       // for online data, it includes with id and without id.
2054
       // 1. for the data with subtree id, we should ignore the ones already in
2055
       // the hash - onlineURLIdHasAddionalAccess.
2056
       // 2. for those without subreeid, it couldn't have additional access and we
2057
       // couldn't ignore it.
2058
       // 3. for inline data, we need do nothing because if it doesn't have
2059
       // additional access, it default value is the top one.
2060

    
2061
       // here is the online url with id
2062
       Enumeration em = onlineURLDistributionIdList.keys();
2063
       while (em.hasMoreElements())
2064
       {
2065
         String onlineSubtreeId = (String)em.nextElement();
2066
         if (!onlineURLIdHasadditionalAccess.containsKey(onlineSubtreeId))
2067
         {
2068
            String url =
2069
                       (String)onlineURLDistributionIdList.get(onlineSubtreeId);
2070
            String onlineDataId = handleOnlineUrlDataFile(url);
2071
            if (onlineDataId != null)
2072
            {
2073
              deletePermissionsInAccessTableForDoc(onlineDataId);
2074
              writeGivenAccessRuleIntoDB(permOrder, accessRule,
2075
                                         onlineDataId, subtree);
2076
            }
2077

    
2078
         }
2079
       }//while
2080

    
2081
       // here is the onlineURL without id
2082
       for (int i= 0; i < onlineURLDistributionListWithoutId.size(); i++)
2083
       {
2084
         String url = (String)onlineURLDistributionListWithoutId.elementAt(i);
2085
         String onlineDataId = handleOnlineUrlDataFile(url);
2086
         if (onlineDataId != null)
2087
         {
2088
           deletePermissionsInAccessTableForDoc(onlineDataId);
2089
           writeGivenAccessRuleIntoDB(permOrder, accessRule,
2090
                                         onlineDataId, subtree);
2091
         }
2092
       }//for
2093
    }//writeTopAccessRuletoDB
2094

    
2095
    /* Write a gaven access rule into db */
2096
    private void writeGivenAccessRuleIntoDB(String permOrder, Vector accessRules,
2097
                     String dataId, String subTreeId) throws SAXException
2098
    {
2099
      if (permOrder == null || permOrder.trim().equals("") || dataId == null ||
2100
          dataId.trim().equals("") || accessRules == null ||
2101
          accessRules.isEmpty())
2102
      {
2103
        logMetacat.info("The access object is null and tried to " +
2104
                                  " write to xml_access table");
2105
        throw new SAXException("The access object is null");
2106
      }
2107
      
2108
      // geet the guid, not the docid alone
2109
      String guid = null;
2110
		try {
2111
			guid = IdentifierManager.getInstance().getGUID(docid, Integer.valueOf(revision));
2112
		} catch (NumberFormatException e) {
2113
			throw new SAXException(e.getMessage(), e);
2114
		} catch (McdbDocNotFoundException e) {
2115
			// register the default mapping now
2116
			guid = docid + "." + revision;
2117
			IdentifierManager.getInstance().createMapping(guid, guid);
2118
		}
2119
      
2120
       String sql = null;
2121
       PreparedStatement pstmt = null;
2122
       sql = "INSERT INTO xml_access (guid, principal_name, permission, "
2123
               + "perm_type, perm_order, accessfileid, subtreeid) VALUES "
2124
               + " (?, ?, ?, ?, ?, ?, ?)";
2125

    
2126
       try
2127
       {
2128

    
2129
           pstmt = connection.prepareStatement(sql);
2130
           // Increase DBConnection usage count
2131
           connection.increaseUsageCount(1);
2132
           // Bind the values to the query
2133
           pstmt.setString(1, dataId);
2134
           logMetacat.info("guid in accesstable: " + dataId);
2135
           pstmt.setString(6, guid);
2136
           logMetacat.info("Accessfileid in accesstable: " + guid);
2137
           pstmt.setString(5, permOrder);
2138
           logMetacat.info("PermOder in accesstable: " + permOrder);
2139
           pstmt.setString(7, subTreeId);
2140
           logMetacat.info("subtree id in accesstable: " + subTreeId);
2141
           // if it is not top level, set s id
2142

    
2143
           //Vector accessRules = accessSection.getAccessRules();
2144
           // go through every rule
2145
           for (int i = 0; i < accessRules.size(); i++)
2146
           {
2147
               AccessRule rule = (AccessRule) accessRules.elementAt(i);
2148
               String permType = rule.getPermissionType();
2149
               int permission = rule.getPermission();
2150
               pstmt.setInt(3, permission);
2151
               logMetacat.info("permission in accesstable: "
2152
                       + permission);
2153
               pstmt.setString(4, permType);
2154
               logMetacat.info(
2155
                       "Permtype in accesstable: " + permType);
2156
               // go through every principle in rule
2157
               Vector nameVector = rule.getPrincipal();
2158
               for (int j = 0; j < nameVector.size(); j++)
2159
               {
2160
                   String prName = (String) nameVector.elementAt(j);
2161
                   pstmt.setString(2, prName);
2162
                   logMetacat.info("Principal in accesstable: "
2163
                           + prName);
2164
                   logMetacat.debug("running sql: " + pstmt.toString());
2165
                   pstmt.execute();
2166
               }//for
2167
           }//for
2168
           pstmt.close();
2169
       }//try
2170
       catch (SQLException e)
2171
       {
2172
           throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
2173
                   + e.getMessage());
2174
       }//catch
2175
       finally
2176
       {
2177
           try
2178
           {
2179
               pstmt.close();
2180
           }
2181
           catch (SQLException ee)
2182
           {
2183
               throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
2184
                       + ee.getMessage());
2185
           }
2186
       }//finally
2187

    
2188
    }//writeGivenAccessRuleIntoDB
2189

    
2190

    
2191
    /* Delete from db all permission for resources related guid, if any. */
2192
    private void deletePermissionsInAccessTableForDoc(String guid)
2193
            throws SAXException
2194
    {
2195
        PreparedStatement pstmt = null;
2196
        try {
2197
        	String sql = "DELETE FROM xml_access WHERE guid = ? ";
2198
            // delete all acl records for resources related to guid if any
2199
            pstmt = connection.prepareStatement(sql);
2200
            pstmt.setString(1, guid);
2201
            // Increase DBConnection usage count
2202
            connection.increaseUsageCount(1);
2203
            pstmt.execute();
2204

    
2205
        } catch (SQLException e) {
2206
            throw new SAXException(e.getMessage());
2207
        } finally {
2208
            try {
2209
                pstmt.close();
2210
            } catch (SQLException ee) {
2211
                throw new SAXException(ee.getMessage());
2212
            }
2213
        }
2214
    }//deletePermissionsInAccessTable
2215

    
2216
    /* Delete access rules from xml_access for a subtee id */
2217
    private void deleteSubtreeAccessRule(String subtreeid) throws SAXException
2218
    {
2219
      PreparedStatement pstmt = null;
2220
       try
2221
       {
2222
    	   String sql = 
2223
    		   "DELETE FROM xml_access " +
2224
    		   "WHERE accessfileid IN (SELECT guid from identifier where docid = ? and rev = ?) " +
2225
               "AND subtreeid = ?";
2226
           pstmt = connection.prepareStatement(sql);
2227
           pstmt.setString(1, docid);
2228
           pstmt.setInt(2, Integer.valueOf(revision));
2229
           pstmt.setString(3, subtreeid);
2230
           // Increase DBConnection usage count
2231
           connection.increaseUsageCount(1);
2232
           pstmt.execute();
2233
       }
2234
       catch (SQLException e)
2235
       {
2236
           throw new SAXException(e.getMessage());
2237
       }
2238
       finally
2239
       {
2240
           try
2241
           {
2242
               pstmt.close();
2243
           }
2244
           catch (SQLException ee)
2245
           {
2246
               throw new SAXException(ee.getMessage());
2247
           }
2248
       }
2249

    
2250
    }
2251

    
2252
    private void deleteAllInlineDataAccessRules() throws SAXException
2253
    {
2254
      PreparedStatement pstmt = null;
2255
       try
2256
       {
2257
    	   String sql = 
2258
    		   "DELETE FROM xml_access " +
2259
    		   "WHERE accessfileid IN (SELECT guid from identifier where docid = ? and rev = ?) " +
2260
    		   "AND subtreeid IS NOT NULL";
2261
           pstmt = connection.prepareStatement(sql);
2262
           pstmt.setString(1, docid);
2263
           pstmt.setInt(2, Integer.valueOf(revision));
2264
           // Increase DBConnection usage count
2265
           connection.increaseUsageCount(1);
2266
           pstmt.execute();
2267
       }
2268
       catch (SQLException e)
2269
       {
2270
           throw new SAXException(e.getMessage());
2271
       }
2272
       finally
2273
       {
2274
           try
2275
           {
2276
               pstmt.close();
2277
           }
2278
           catch (SQLException ee)
2279
           {
2280
               throw new SAXException(ee.getMessage());
2281
           }
2282
       }
2283

    
2284
    }
2285

    
2286
    /*
2287
     * In order to make sure only usr has "all" permission can update access
2288
     * subtree in eml document we need to keep access subtree info in
2289
     * xml_accesssubtree table, such as docid, version, startnodeid, endnodeid
2290
     */
2291
    private void writeAccessSubTreeIntoDB(AccessSection accessSection)
2292
                                          throws SAXException
2293
    {
2294
        if (accessSection == null)
2295
        {
2296

    
2297
          logMetacat.info("Access object is null and tried to write "+
2298
                                   "into access subtree table");
2299
          throw new SAXException("The access object is null to write access " +
2300
                                 "sbutree");
2301
        }
2302

    
2303
        String sql = null;
2304
        PreparedStatement pstmt = null;
2305
        sql = "INSERT INTO xml_accesssubtree (docid, rev, controllevel, "
2306
                + "subtreeid, startnodeid, endnodeid) VALUES "
2307
                + " (?, ?, ?, ?, ?, ?)";
2308
        try
2309
        {
2310

    
2311
            pstmt = connection.prepareStatement(sql);
2312
            // Increase DBConnection usage count
2313
            connection.increaseUsageCount(1);
2314
            String level = accessSection.getControlLevel();
2315
            long startNodeId = -1;
2316
            if (level != null && level.equals(DATAACCESSLEVEL))
2317
            {
2318
              // for additional access module the start node id should be
2319
              // descirbes element id
2320
              startNodeId = accessSection.getStartedDescribesNodeId();
2321
              // if in additional access, there is not describes element,
2322
              // in this senario, describesNodeId will be -1. Then we should
2323
              // the start access element id
2324
              if (startNodeId == -1)
2325
              {
2326
                startNodeId = accessSection.getStartNodeId();
2327
              }
2328
            }
2329
            else
2330
            {
2331
                startNodeId = accessSection.getStartNodeId();
2332
            }
2333

    
2334
            long endNodeId = accessSection.getEndNodeId();
2335
            String sectionId = accessSection.getSubTreeId();
2336

    
2337
            if (startNodeId ==-1 || endNodeId == -1)
2338
            {
2339
              throw new SAXException("Don't find start node or end node id " +
2340
                                      "for the access subtee");
2341

    
2342
            }
2343

    
2344
            // Bind the values to the query
2345
            pstmt.setString(1, docid);
2346
            logMetacat.info("Docid in access-subtreetable: " + docid);
2347
            pstmt.setInt(2, (new Integer(revision)).intValue());
2348
            logMetacat.info("rev in accesssubtreetable: " + revision);
2349
            pstmt.setString(3, level);
2350
            logMetacat.info("contorl level in access-subtree table: "
2351
                    + level);
2352
            pstmt.setString(4, sectionId);
2353
            logMetacat.info("Subtree id in access-subtree table: "
2354
                    + sectionId);
2355
            pstmt.setLong(5, startNodeId);
2356
            logMetacat.info("Start node id is: " + startNodeId);
2357
            pstmt.setLong(6, endNodeId);
2358
            logMetacat.info("End node id is: " + endNodeId);
2359
            logMetacat.debug("Eml200SAXHandler.writeAccessSubTreeIntoDB - executing SQL: " + pstmt.toString());
2360
            pstmt.execute();
2361
            pstmt.close();
2362
        }//try
2363
        catch (SQLException e)
2364
        {
2365
            throw new SAXException("EMLSAXHandler.writeAccessSubTreeIntoDB(): "
2366
                    + e.getMessage());
2367
        }//catch
2368
        finally
2369
        {
2370
            try
2371
            {
2372
                pstmt.close();
2373
            }
2374
            catch (SQLException ee)
2375
            {
2376
                throw new SAXException(
2377
                        "EMLSAXHandler.writeAccessSubTreeIntoDB(): "
2378
                                + ee.getMessage());
2379
            }
2380
        }//finally
2381

    
2382
    }//writeAccessSubtreeIntoDB
2383

    
2384
    /* Delete every access subtree record from xml_accesssubtree. */
2385
    private void deleteAccessSubTreeRecord(String docId) throws SAXException
2386
    {
2387
        PreparedStatement pstmt = null;
2388
        try {
2389
        	String sql = "DELETE FROM xml_accesssubtree WHERE docid = ?";
2390
            // delete all acl records for resources related to @aclid if any
2391
            pstmt = connection.prepareStatement(sql);
2392
            pstmt.setString(1, docId);
2393
            // Increase DBConnection usage count
2394
            connection.increaseUsageCount(1);                   
2395
            logMetacat.debug("running sql: " + sql);
2396
            pstmt.execute();
2397

    
2398
        } catch (SQLException e) {
2399
            throw new SAXException(e.getMessage());
2400
        } finally {
2401
            try {
2402
                pstmt.close();
2403
            } catch (SQLException ee) {
2404
                throw new SAXException(ee.getMessage());
2405
            }
2406
        }
2407
    }//deleteAccessSubTreeRecord
2408

    
2409
    // open a file writer for writing inline data to file
2410
    private Writer createInlineDataFileWriter(String fileName, String encoding)
2411
            throws SAXException
2412
    {
2413
        Writer writer = null;
2414
        String path;
2415
        try {
2416
        	 path = PropertyService.getProperty("application.inlinedatafilepath");
2417
        } catch (PropertyNotFoundException pnfe) {
2418
            throw new SAXException(pnfe.getMessage());
2419
        }
2420
        /*
2421
         * File inlineDataDirectory = new File(path);
2422
         */
2423
        String newFile = path + "/" + fileName;
2424
        logMetacat.info("inline file name: " + newFile);
2425
        try {
2426
            // true means append
2427
        	writer = new OutputStreamWriter(new FileOutputStream(newFile, true), encoding);
2428
        } catch (IOException ioe) {
2429
            throw new SAXException(ioe.getMessage());
2430
        }
2431
        return writer;
2432
    }
2433

    
2434
    // write inline data into file system and return file name(without path)
2435
    private void writeInlineDataIntoFile(Writer writer, StringBuffer data)
2436
            throws SAXException
2437
    {
2438
        try {
2439
            writer.write(data.toString());
2440
            writer.flush();
2441
        } catch (Exception e) {
2442
            throw new SAXException(e.getMessage());
2443
        }
2444
    }
2445

    
2446

    
2447

    
2448
    /*
2449
     * In eml2, the inline data wouldn't store in db, it store in file system
2450
     * The db stores file name(without path). We got the old file name from db
2451
     * and compare to the new in line data file
2452
     */
2453
    public boolean compareInlineDataFiles(String oldFileName, String newFileName)
2454
            throws McdbException
2455
    {
2456
        // this method need to be testing
2457
        boolean same = true;
2458
        String data = null;
2459
        try {
2460
        	String path = PropertyService.getProperty("application.inlinedatafilepath");
2461
			// the new file name will look like path/docid.rev.2
2462
			File inlineDataDirectory = new File(path);
2463
			File oldDataFile = new File(inlineDataDirectory, oldFileName);
2464
			File newDataFile = new File(inlineDataDirectory, newFileName);
2465

    
2466
            Reader oldFileReader = new InputStreamReader(new FileInputStream(oldDataFile), encoding);
2467
            BufferedReader oldStringReader = new BufferedReader(oldFileReader);
2468
            Reader newFileReader = new InputStreamReader(new FileInputStream(newDataFile), encoding);
2469
            BufferedReader newStringReader = new BufferedReader(newFileReader);
2470
            // read first line of data
2471
            String oldString = oldStringReader.readLine();
2472
            String newString = newStringReader.readLine();
2473

    
2474
            // at the end oldstring will be null
2475
            while (oldString != null) {
2476
                oldString = oldStringReader.readLine();
2477
                newString = newStringReader.readLine();
2478
                if (!oldString.equals(newString)) {
2479
                    same = false;
2480
                    break;
2481
                }
2482
            }
2483

    
2484
            // if oldString is null but newString is not null, they are same
2485
            if (same) {
2486
                if (newString != null) {
2487
                    same = false;
2488
                }
2489
            }
2490

    
2491
        } catch (Exception e) {
2492
            throw new McdbException(e.getMessage());
2493
        }
2494
        logMetacat.info("the inline data retrieve from file: " + data);
2495
        return same;
2496
    }
2497

    
2498
   /*
2499
	 * Copy a old line file to a new inline file
2500
	 */
2501
	public void copyInlineFile(String inlineDistributionId, String newFileName)
2502
			throws SAXException {
2503
		if (inlineDistributionId == null || newFileName == null) {
2504
			throw new SAXException("Could not copy inline file from old one to new "
2505
					+ "one!");
2506
		}
2507
		// get old file id from previousUnreadable data object
2508
		String oldInlineInternalFileName = (String) previousUnreadableInlineDataObjectHash
2509
				.get(inlineDistributionId);
2510

    
2511
		if (oldInlineInternalFileName == null
2512
				|| oldInlineInternalFileName.trim().equals("")) {
2513
			throw new SAXException("Could not copy inline file from old one to new "
2514
					+ "one because can't find old file name");
2515
		}
2516
		logMetacat.info("in handle inline data");
2517
		logMetacat.info("the inline data file name from xml_access is: "
2518
				+ oldInlineInternalFileName);
2519

    
2520
		InputStream oldFileReader = null;
2521
		OutputStream newFileWriter = null;
2522
		try {
2523
			String path = PropertyService.getProperty("application.inlinedatafilepath");
2524
			// the new file name will look like path/docid.rev.2
2525
			File inlineDataDirectory = new File(path);
2526
			File oldDataFile = new File(inlineDataDirectory, oldInlineInternalFileName);
2527
			File newDataFile = new File(inlineDataDirectory, newFileName);
2528

    
2529
			oldFileReader = new FileInputStream(oldDataFile);
2530
			newFileWriter = new FileOutputStream(newDataFile);
2531
			byte[] buf = new byte[4 * 1024]; // 4K buffer
2532
			int b = oldFileReader.read(buf);
2533
			while (b != -1) {
2534
				newFileWriter.write(buf, 0, b);
2535
				b = oldFileReader.read(buf);
2536
			}
2537
		} catch (Exception e) {
2538
			throw new SAXException(e.getMessage());
2539
		} finally {
2540
			if (oldFileReader != null) {
2541
				try {
2542
					oldFileReader.close();
2543
				} catch (Exception ee) {
2544
					throw new SAXException(ee.getMessage());
2545
				}
2546

    
2547
			}
2548
			if (newFileWriter != null) {
2549
				try {
2550
					newFileWriter.close();
2551
				} catch (Exception ee) {
2552
					throw new SAXException(ee.getMessage());
2553
				}
2554

    
2555
			}
2556
		}
2557
	}
2558

    
2559

    
2560
    // if xml file failed to upload, we need to call this method to delete
2561
    // the inline data already in file system
2562
    public void deleteInlineFiles() throws SAXException
2563
    {
2564
        if (!inlineFileIDList.isEmpty()) {
2565
            for (int i = 0; i < inlineFileIDList.size(); i++) {
2566
                String fileName = (String) inlineFileIDList.elementAt(i);
2567
                deleteInlineDataFile(fileName);
2568
            }
2569
        }
2570
    }
2571

    
2572
    /* delete the inline data file */
2573
    private void deleteInlineDataFile(String fileName) throws SAXException
2574
    {
2575
    	String path;
2576
    	try {
2577
    		path = PropertyService.getProperty("application.inlinedatafilepath");
2578
    	} catch (PropertyNotFoundException pnfe) {
2579
    		throw new SAXException ("Could not find inline data file path: " 
2580
    				+ pnfe.getMessage());
2581
    	}
2582
        File inlineDataDirectory = new File(path);
2583
        File newFile = new File(inlineDataDirectory, fileName);
2584
        newFile.delete();
2585

    
2586
    }
2587

    
2588
    /*
2589
	 * In eml2, the inline data wouldn't store in db, it store in file system
2590
	 * The db stores file name(without path).
2591
	 */
2592
	public static Reader readInlineDataFromFileSystem(String fileName, String encoding)
2593
			throws McdbException {
2594
		// BufferedReader stringReader = null;
2595
		Reader fileReader = null;
2596
		try {
2597
			String path = PropertyService.getProperty("application.inlinedatafilepath");
2598
			// the new file name will look like path/docid.rev.2
2599
			File inlineDataDirectory = new File(path);
2600
			File dataFile = new File(inlineDataDirectory, fileName);
2601

    
2602
			fileReader = new InputStreamReader(new FileInputStream(dataFile), encoding);
2603
			// stringReader = new BufferedReader(fileReader);
2604
		} catch (Exception e) {
2605
			throw new McdbException(e.getMessage());
2606
		}
2607
		// return stringReader;
2608
		return fileReader;
2609
	}
2610

    
2611
    /* Delete relations */
2612
    private void deleteRelations() throws SAXException
2613
    {
2614
        PreparedStatement pStmt = null;
2615
        String sql = "DELETE FROM xml_relation where docid =?";
2616
        try {
2617
            pStmt = connection.prepareStatement(sql);
2618
            //bind variable
2619
            pStmt.setString(1, docid);
2620
            //execute query
2621
            logMetacat.debug("Eml200SAXHandler.deleteRelations - executing SQL: " + pStmt.toString());
2622
            pStmt.execute();
2623
            pStmt.close();
2624
        }//try
2625
        catch (SQLException e) {
2626
            throw new SAXException("EMLSAXHandler.deleteRelations(): "
2627
                    + e.getMessage());
2628
        }//catch
2629
        finally {
2630
            try {
2631
                pStmt.close();
2632
            }//try
2633
            catch (SQLException ee) {
2634
                throw new SAXException("EMLSAXHandler.deleteRelations: "
2635
                        + ee.getMessage());
2636
            }//catch
2637
        }//finally
2638
    }
2639

    
2640
    /* Write an online data file id into xml_relation table. The dataId shouldnot
2641
     * have the revision
2642
     */
2643
    private void writeOnlineDataFileIdIntoRelationTable(String dataId)
2644
            throws SAXException
2645
    {
2646
        PreparedStatement pStmt = null;
2647
        String sql = "INSERT into xml_relation (docid, packagetype, subject, "
2648
                + "relationship, object) values (?, ?, ?, ?, ?)";
2649
        try {
2650
            pStmt = connection.prepareStatement(sql);
2651
            //bind variable
2652
            pStmt.setString(1, docid);
2653
            pStmt.setString(2, doctype);
2654
            pStmt.setString(3, docid);
2655
            pStmt.setString(4, RELATION);
2656
            pStmt.setString(5, dataId);
2657
            //execute query
2658
            logMetacat.debug("Eml200SAXHandler.writeOnlineDataFileIdIntoRelationTable - executing SQL: " + pStmt.toString());
2659
            pStmt.execute();
2660
            pStmt.close();
2661
        }//try
2662
        catch (SQLException e) {
2663
            throw new SAXException(
2664
                    "EMLSAXHandler.writeOnlineDataFileIdIntoRelationTable(): "
2665
                            + e.getMessage());
2666
        }//catch
2667
        finally {
2668
            try {
2669
                pStmt.close();
2670
            }//try
2671
            catch (SQLException ee) {
2672
                throw new SAXException(
2673
                        "EMLSAXHandler.writeOnlineDataFileIdIntoRelationTable(): "
2674
                                + ee.getMessage());
2675
            }//catch
2676
        }//finally
2677

    
2678
    }//writeOnlineDataFileIdIntoRelationTable
2679

    
2680
    /*
2681
     * This method will handle data file in online url. If the data file is in
2682
     * ecogrid protocol, then the datafile identifier (guid) be returned.
2683
     * otherwise, null will be returned.
2684
     * If the data file doesn't exsit in xml_documents or
2685
     * xml_revision table, or the user has all permission to the data file if
2686
     * the docid already existed, the data file id (guid) will be returned
2687
     * NEED to do:
2688
     * We should also need to implement http and ftp. Those
2689
     * external files should be download and assign a data file id to it.
2690
     */
2691
    private String handleOnlineUrlDataFile(String url) throws SAXException
2692
    {
2693
      logMetacat.warn("The url is "+ url);
2694
      String docid = null;
2695
      String guid = null;
2696

    
2697
      // if the url is not a ecogrid protocol, null will be getten
2698
      String accessionNumber =
2699
    	  DocumentUtil.getAccessionNumberFromEcogridIdentifier(url);
2700
      if (accessionNumber != null)
2701
      {
2702
        // handle ecogrid protocol
2703
        // get rid of revision number to get the docid.
2704
        docid = DocumentUtil.getDocIdFromAccessionNumber(accessionNumber);
2705
        // use the guid instead
2706
        int rev = DocumentUtil.getRevisionFromAccessionNumber(accessionNumber);
2707
		try {
2708
			guid = IdentifierManager.getInstance().getGUID(docid, rev);
2709
		} catch (McdbDocNotFoundException e1) {
2710
			guid = docid + "." + rev;
2711
			IdentifierManager.getInstance().createMapping(guid, guid);
2712
		}
2713
        onlineDataFileIdInRelationVector.add(docid);
2714
        try
2715
        {
2716

    
2717
          if (!AccessionNumber.accNumberUsed(docid))
2718
          {
2719
            return guid;
2720
          }
2721
          PermissionController controller = new
2722
              PermissionController(accessionNumber);
2723
          if (controller.hasPermission(
2724
              user, groups, AccessControlInterface.ALLSTRING))
2725
          {
2726
            return guid;
2727
          }
2728
          else
2729
          {
2730
            throw new SAXException("User: " + user + " does not have permission to update " +
2731
                  "access rules for data file "+ guid);
2732
          }
2733
        }//try
2734
        catch(Exception e)
2735
        {
2736
          logMetacat.error("Error in " +
2737
                                "Eml200SAXHanlder.handleOnlineUrlDataFile is " +
2738
                                 e.getMessage());
2739
          throw new SAXException(e.getMessage());
2740
        }
2741
      }
2742
      return guid;
2743
    }
2744

    
2745
    private void compareElementNameSpaceAttributes(Stack unchangableNodeStack,
2746
            Hashtable nameSpaces, Attributes attributes, String localName,
2747
            String error) throws SAXException
2748
    {
2749
        //Get element subtree node stack (element node)
2750
        NodeRecord elementNode = null;
2751
        try {
2752
            elementNode = (NodeRecord) unchangableNodeStack.pop();
2753
        } catch (EmptyStackException ee) {
2754
            logMetacat.error("Node stack is empty for element data");
2755
            throw new SAXException(error);
2756
        }
2757
        logMetacat.info("current node type from xml is ELEMENT");
2758
        logMetacat.info("node type from stack: "
2759
                + elementNode.getNodeType());
2760
        logMetacat.info("node name from xml document: " + localName);
2761
        logMetacat.info("node name from stack: "
2762
                + elementNode.getNodeName());
2763
        logMetacat.info("node data from stack: "
2764
                + elementNode.getNodeData());
2765
        logMetacat.info("node id is: " + elementNode.getNodeId());
2766
        // if this node is not element or local name not equal or name space
2767
        // not
2768
        // equals, throw an exception
2769
        if (!elementNode.getNodeType().equals("ELEMENT")
2770
                || !localName.equals(elementNode.getNodeName()))
2771
        //  (uri != null && !uri.equals(elementNode.getNodePrefix())))
2772
        {
2773
            logMetacat.info("Inconsistence happend: ");
2774
            logMetacat.info("current node type from xml is ELEMENT");
2775
            logMetacat.info("node type from stack: "
2776
                    + elementNode.getNodeType());
2777
            logMetacat.info("node name from xml document: "
2778
                    + localName);
2779
            logMetacat.info("node name from stack: "
2780
                    + elementNode.getNodeName());
2781
            logMetacat.info("node data from stack: "
2782
                    + elementNode.getNodeData());
2783
            logMetacat.info("node id is: " + elementNode.getNodeId());
2784
            throw new SAXException(error);
2785
        }
2786

    
2787
        //compare namespace
2788
        Enumeration nameEn = nameSpaces.keys();
2789
        while (nameEn.hasMoreElements()) {
2790
            //Get namespacke node stack (element node)
2791
            NodeRecord nameNode = null;
2792
            try {
2793
                nameNode = (NodeRecord) unchangableNodeStack.pop();
2794
            } catch (EmptyStackException ee) {
2795
                logMetacat.error(
2796
                        "Node stack is empty for namespace data");
2797
                throw new SAXException(error);
2798
            }
2799

    
2800
            String prefixName = (String) nameEn.nextElement();
2801
            String nameSpaceUri = (String) nameSpaces.get(prefixName);
2802
            if (!nameNode.getNodeType().equals("NAMESPACE")
2803
                    || !prefixName.equals(nameNode.getNodeName())
2804
                    || !nameSpaceUri.equals(nameNode.getNodeData())) {
2805
                logMetacat.info("Inconsistence happend: ");
2806
                logMetacat.info(
2807
                        "current node type from xml is NAMESPACE");
2808
                logMetacat.info("node type from stack: "
2809
                        + nameNode.getNodeType());
2810
                logMetacat.info("current node name from xml is: "
2811
                        + prefixName);
2812
                logMetacat.info("node name from stack: "
2813
                        + nameNode.getNodeName());
2814
                logMetacat.info("current node data from xml is: "
2815
                        + nameSpaceUri);
2816
                logMetacat.info("node data from stack: "
2817
                        + nameNode.getNodeData());
2818
                logMetacat.info("node id is: " + nameNode.getNodeId());
2819
                throw new SAXException(error);
2820
            }
2821

    
2822
        }//while
2823

    
2824
        //compare attributes
2825
        for (int i = 0; i < attributes.getLength(); i++) {
2826
            NodeRecord attriNode = null;
2827
            try {
2828
                attriNode = (NodeRecord) unchangableNodeStack.pop();
2829

    
2830
            } catch (EmptyStackException ee) {
2831
                logMetacat.error(
2832
                        "Node stack is empty for attribute data");
2833
                throw new SAXException(error);
2834
            }
2835
            String attributeName = attributes.getQName(i);
2836
            String attributeValue = attributes.getValue(i);
2837
            logMetacat.info(
2838
                    "current node type from xml is ATTRIBUTE ");
2839
            logMetacat.info("node type from stack: "
2840
                    + attriNode.getNodeType());
2841
            logMetacat.info("current node name from xml is: "
2842
                    + attributeName);
2843
            logMetacat.info("node name from stack: "
2844
                    + attriNode.getNodeName());
2845
            logMetacat.info("current node data from xml is: "
2846
                    + attributeValue);
2847
            logMetacat.info("node data from stack: "
2848
                    + attriNode.getNodeData());
2849
            logMetacat.info("node id  is: " + attriNode.getNodeId());
2850

    
2851
            if (!attriNode.getNodeType().equals("ATTRIBUTE")
2852
                    || !attributeName.equals(attriNode.getNodeName())
2853
                    || !attributeValue.equals(attriNode.getNodeData())) {
2854
                logMetacat.info("Inconsistence happend: ");
2855
                logMetacat.info(
2856
                        "current node type from xml is ATTRIBUTE ");
2857
                logMetacat.info("node type from stack: "
2858
                        + attriNode.getNodeType());
2859
                logMetacat.info("current node name from xml is: "
2860
                        + attributeName);
2861
                logMetacat.info("node name from stack: "
2862
                        + attriNode.getNodeName());
2863
                logMetacat.info("current node data from xml is: "
2864
                        + attributeValue);
2865
                logMetacat.info("node data from stack: "
2866
                        + attriNode.getNodeData());
2867
                logMetacat.info("node is: " + attriNode.getNodeId());
2868
                throw new SAXException(error);
2869
            }
2870
        }//for
2871

    
2872
    }
2873

    
2874
    /* mehtod to compare current text node and node in db */
2875
    private void compareTextNode(Stack nodeStack, StringBuffer text,
2876
            String error) throws SAXException
2877
    {
2878
        NodeRecord node = null;
2879
        //get node from current stack
2880
        try {
2881
            node = (NodeRecord) nodeStack.pop();
2882
        } catch (EmptyStackException ee) {
2883
            logMetacat.error(
2884
                    "Node stack is empty for text data in startElement");
2885
            throw new SAXException(error);
2886
        }
2887
        logMetacat.info(
2888
                "current node type from xml is TEXT in start element");
2889
        logMetacat.info("node type from stack: " + node.getNodeType());
2890
        logMetacat.info("current node data from xml is: "
2891
                + text.toString());
2892
        logMetacat.info("node data from stack: " + node.getNodeData());
2893
        logMetacat.info("node name from stack: " + node.getNodeName());
2894
        logMetacat.info("node is: " + node.getNodeId());
2895
        if (!node.getNodeType().equals("TEXT")
2896
                || !(text.toString()).equals(node.getNodeData())) {
2897
            logMetacat.info("Inconsistence happend: ");
2898
            logMetacat.info(
2899
                    "current node type from xml is TEXT in start element");
2900
            logMetacat.info("node type from stack: "
2901
                    + node.getNodeType());
2902
            logMetacat.info("current node data from xml is: "
2903
                    + text.toString());
2904
            logMetacat.info("node data from stack: "
2905
                    + node.getNodeData());
2906
            logMetacat.info("node name from stack: "
2907
                    + node.getNodeName());
2908
            logMetacat.info("node is: " + node.getNodeId());
2909
            throw new SAXException(error);
2910
        }//if
2911
    }
2912

    
2913
    /* Comparet comment from xml and db */
2914
    private void compareCommentNode(Stack nodeStack, String string, String error)
2915
            throws SAXException
2916
    {
2917
        NodeRecord node = null;
2918
        try {
2919
            node = (NodeRecord) nodeStack.pop();
2920
        } catch (EmptyStackException ee) {
2921
            logMetacat.error("the stack is empty for comment data");
2922
            throw new SAXException(error);
2923
        }
2924
        logMetacat.info("current node type from xml is COMMENT");
2925
        logMetacat.info("node type from stack: " + node.getNodeType());
2926
        logMetacat.info("current node data from xml is: " + string);
2927
        logMetacat.info("node data from stack: " + node.getNodeData());
2928
        logMetacat.info("node is from stack: " + node.getNodeId());
2929
        // if not consistent terminate program and throw a exception
2930
        if (!node.getNodeType().equals("COMMENT")
2931
                || !string.equals(node.getNodeData())) {
2932
            logMetacat.info("Inconsistence happend: ");
2933
            logMetacat.info("current node type from xml is COMMENT");
2934
            logMetacat.info("node type from stack: "
2935
                    + node.getNodeType());
2936
            logMetacat.info(
2937
                    "current node data from xml is: " + string);
2938
            logMetacat.info("node data from stack: "
2939
                    + node.getNodeData());
2940
            logMetacat.info("node is from stack: " + node.getNodeId());
2941
            throw new SAXException(error);
2942
        }//if
2943
    }
2944

    
2945
    /* Compare whitespace from xml and db */
2946
   private void compareWhiteSpace(Stack nodeStack, String string, String error)
2947
           throws SAXException
2948
   {
2949
       NodeRecord node = null;
2950
       try {
2951
           node = (NodeRecord) nodeStack.pop();
2952
       } catch (EmptyStackException ee) {
2953
           logMetacat.error("the stack is empty for whitespace data");
2954
           throw new SAXException(error);
2955
       }
2956
       if (!node.getNodeType().equals("TEXT")
2957
               || !string.equals(node.getNodeData())) {
2958
           logMetacat.info("Inconsistence happend: ");
2959
           logMetacat.info(
2960
                   "current node type from xml is WHITESPACE TEXT");
2961
           logMetacat.info("node type from stack: "
2962
                   + node.getNodeType());
2963
           logMetacat.info(
2964
                   "current node data from xml is: " + string);
2965
           logMetacat.info("node data from stack: "
2966
                   + node.getNodeData());
2967
           logMetacat.info("node is from stack: " + node.getNodeId());
2968
           throw new SAXException(error);
2969
       }//if
2970
   }
2971

    
2972

    
2973

    
2974
}
(32-32/64)