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: 2014-02-20 16:16:31 -0800 (Thu, 20 Feb 2014) $'
11
 * '$Revision: 8628 $'
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.utilities.access.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.dataone.hazelcast.HazelcastService;
61
import edu.ucsb.nceas.metacat.properties.PropertyService;
62
import edu.ucsb.nceas.metacat.util.AuthUtil;
63
import edu.ucsb.nceas.metacat.util.DocumentUtil;
64
import edu.ucsb.nceas.metacat.util.MetacatUtil;
65
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
66

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
198
    private AccessSection topAccessSubTreeFromDB = null;
199

    
200
    private Vector additionalAccessSubTreeListFromDB = new Vector();
201

    
202
    private Hashtable referencedAccessSubTreeListFromDB = new Hashtable();
203

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

    
207
    private Vector additionalAccessVector = new Vector();
208

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

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

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

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

    
222
    private Hashtable inlineDataNameSpace = null;
223

    
224
    private Writer inlineDataFileWriter = null;
225

    
226
    private String inlineDataFileName = null;
227

    
228
    private int inLineDataIndex = 0;
229

    
230
    private Vector inlineFileIDList = new Vector();
231

    
232
    private boolean inAdditionalMetaData = false;
233

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

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

    
244
    private Hashtable previousUnreadableInlineDataObjectHash = new Hashtable();
245

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

    
252
    private Hashtable accessSubTreeAlreadyWriteDBList = new Hashtable();
253

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

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

    
272
    private int numberOfHitUnWritableInlineData = 0;
273

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
379
            }
380

    
381

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

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

    
404

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

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

    
454
     }
455

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

    
469
        try
470
        {
471

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

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

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

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

    
535
       try
536
       {
537

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

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

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

    
591

    
592

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

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

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

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

    
664

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

    
669
            }
670
            //==============================================================
671

    
672

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

    
680

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

    
689
                }
690

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

    
700
            }
701
            //==================================================================
702

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

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

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

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

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

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

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

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

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

    
841
              }
842

    
843
          }//for
844

    
845

    
846
           //=================================================================
847

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

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

    
881

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

    
907
                accessObject.setStartNodeId(startNodeId);
908
                accessObject.setDocId(docid);
909

    
910

    
911

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

    
919
                accessRule = new AccessRule();
920

    
921
                //set permission type "allow"
922
                accessRule.setPermissionType(ALLOW);
923

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

    
935
            //=================================================================
936
            // some other independ stuff
937

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

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

    
961
            }
962

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

    
1003

    
1004
             //==================================================================
1005
            // reset name space
1006
            namespaces = null;
1007
            namespaces = new Hashtable();
1008

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

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

    
1057
    }
1058

    
1059

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

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

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

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

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

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

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

    
1204

    
1205

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

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

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

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

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

    
1304
                logMetacat.info(
1305
                            "Write text into DB in End Element");
1306

    
1307
                 // write text node into db
1308
                 endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer,
1309
                            currentNode);
1310

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

    
1319
                }
1320
            }//if handle text node
1321

    
1322

    
1323

    
1324
            //set hitText false
1325
            hitTextNode = false;
1326
            // reset textbuff
1327
            textBuffer = null;
1328
            textBuffer = new StringBuffer();
1329

    
1330

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

    
1346
                accessObject.setEndNodeId(endNodeId);
1347
                AccessSection newAccessObject = accessObject;
1348
                newAccessObject.setStoredTmpNodeStack(storedAccessNodeStack);
1349
                if (newAccessObject != null)
1350
                {
1351

    
1352
                    if (processTopLevelAccess)
1353
                    {
1354
                       topAccessSection = newAccessObject;
1355

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

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

    
1379
                }//if
1380
                //reset access section object
1381
                accessObject = null;
1382

    
1383
                // reset tmp stored node stack
1384
                storedAccessNodeStack = null;
1385
                storedAccessNodeStack = new Stack();
1386

    
1387
                // reset flag
1388
                processAdditionalAccess = false;
1389
                processTopLevelAccess = false;
1390
                processOtherAccess = false;
1391

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

    
1404

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

    
1433
            }//else if
1434
            else if (currentTag.equals(DESCRIBES))
1435
            {
1436
                firstDescribesInAdditionalMetadata = false;
1437

    
1438
            }
1439

    
1440

    
1441

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

    
1461

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

    
1483
       try
1484
       {
1485
         if (!compareInlineDataFiles(oldInlineInternalFileName,
1486
                                     newInlineInternalFileName))
1487
         {
1488
           modified = true;
1489

    
1490
         }
1491
         else
1492
         {
1493
           modified = false;
1494
         }
1495
       }
1496
       catch(Exception e)
1497
       {
1498
         modified = true;
1499
       }
1500

    
1501
       // delete the inline data file already in file system
1502
       if (modified)
1503
       {
1504
         deleteInlineDataFile(newInlineInternalFileName);
1505

    
1506
       }
1507
       return modified;
1508
     }
1509

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

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

    
1539
		} catch (Exception e) {
1540
			throw new SAXException(e.getMessage());
1541
		}
1542
		return isEmpty;
1543

    
1544
     }
1545

    
1546

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

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

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

    
1591

    
1592

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

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

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

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

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

    
1670
    }
1671

    
1672

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

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

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

    
1700
          //delete relation table
1701
          deleteRelations();
1702
          //write relations
1703
           for (int i = 0; i < onlineDataFileIdInRelationVector.size(); i++) {
1704
            String id = (String) onlineDataFileIdInRelationVector.elementAt(i);
1705
            writeOnlineDataFileIdIntoRelationTable(id);
1706
           }
1707
        }
1708

    
1709
        // clean the subtree record
1710
        accessSubTreeAlreadyWriteDBList = new Hashtable();
1711
    }
1712

    
1713

    
1714

    
1715
    /* The method will compare all access modules in eml document -
1716
     * topLevel, additionalLevel(data access) and referenced access module*/
1717
    private void compareAllAccessModules() throws SAXException
1718
    {
1719
      //compare top level
1720
      compareAccessSubtree(topAccessSubTreeFromDB, topAccessSection);
1721

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

    
1741
      //compare referenced level
1742
      Enumeration em = referencedAccessSubTreeListFromDB.keys();
1743
      while (em.hasMoreElements())
1744
      {
1745
        String id = (String)em.nextElement();
1746
        AccessSection fromDB = (AccessSection)
1747
                               referencedAccessSubTreeListFromDB.get(id);
1748
        AccessSection fromParser = (AccessSection)
1749
                               possibleReferencedAccessHash.get(id);
1750
        compareAccessSubtree(fromDB, fromParser);
1751
      }
1752
    }
1753

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

    
1769
       Stack tempStack = new Stack();
1770
       while(!nodeStackFromDBTable.isEmpty()){
1771
           tempStack.push(nodeStackFromDBTable.pop());
1772
       }
1773
       comparingNodeStacks(tempStack, nodeStackFromParser);
1774
    }
1775

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

    
1790
          // Pop an element from stack2(stack 2 maybe empty)
1791
          NodeRecord record2 = null;
1792
          try {
1793
              record2 = (NodeRecord) stack2.pop();
1794
          } catch (EmptyStackException ee) {
1795

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

    
1808
      // now stack1 is empty and we should make sure stack2 is empty too
1809
      if (!stack2.isEmpty()) {
1810

    
1811
          logMetacat.info(
1812
                  "stack2 still have some elements while stack1 "
1813
                          + "is empty! ");
1814
          throw new SAXException(UPDATEACCESSERROR);
1815
      }//if
1816
  }//comparingNodeStacks
1817

    
1818

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

    
1832
        if (topAccessSection != null){
1833
          writeTopLevelAccessRuleToDB();
1834
        }
1835
        //System.out.println("after write top access rules");
1836
    }//writeAccessRuleToDB
1837

    
1838

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

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

    
1891
           // search possible referenced access hashtable
1892
           if (possibleReferencedAccessHash.containsKey(reference))
1893
           {
1894
             AccessSection referenceAccess = (AccessSection)
1895
                         possibleReferencedAccessHash.get(reference);
1896
             return resolveAccessRuleReference(referenceAccess);
1897
           }
1898

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

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

    
1945

    
1946
  /* The method to write additional access rule into db. The old rules will be
1947
   * deleted
1948
   * If no describedId in the access object, this access rules will be ingorned
1949
   */
1950
  private void writeadditionalAccessRuleToDB() throws SAXException
1951
  {
1952
     //System.out.println("in write additional");
1953
     // we should delete all inline access rules in xml_access if
1954
     // user has all permission
1955
     if (!needToCheckAccessModule)
1956
     {
1957
       deleteAllInlineDataAccessRules();
1958
     }
1959
     for (int i=0; i < additionalAccessVector.size(); i++)
1960
     {
1961
       //System.out.println("in for loop of write additional");
1962
       AccessSection access = (AccessSection)additionalAccessVector.elementAt(i);
1963
       Vector describeIdList = access.getDescribedIdList();
1964
       // if this access is a reference, a new access object will be created
1965
       // which contains the real access rules referenced. Also, each access tree
1966
       // will be write into xml_accesssubtee table
1967
       AccessSection newAccess = resolveAccessRuleReference(access);
1968
       String permOrder = newAccess.getPermissionOrder();
1969
       if (permOrder.equals(AccessControlInterface.DENYFIRST) && ignoreDenyFirst) {
1970
    	   logMetacat.warn("Metacat no longer supports EML 'denyFirst' access rules - ignoring this access block");
1971
    	   return;
1972
       }
1973
       Vector accessRule = newAccess.getAccessRules();
1974

    
1975
       if (describeIdList == null || describeIdList.isEmpty())
1976
       {
1977
         continue;
1978
       }
1979

    
1980
       for (int j = 0; j < describeIdList.size(); j++)
1981
       {
1982
         String subreeid = (String)describeIdList.elementAt(j);
1983
         logMetacat.info("describe id in additional access " +
1984
                                  subreeid);
1985
         // we need to figure out the real id if this subreeid is points to
1986
         // a distribution reference.
1987
         subreeid = resolveDistributionReference(subreeid);
1988
         if (subreeid != null && !subreeid.trim().equals(""))
1989
         {
1990
           logMetacat.info("subtree id is "+ subreeid +
1991
                                    " after resolve reference id" );
1992
           // if this id is for line data, we need to delete the records first
1993
           // then add new records. The key for deleting is subtee id
1994
           if (inlineDistributionIdList.containsKey(subreeid))
1995
           {
1996
             String inlineFileName = (String)
1997
                                        inlineDistributionIdList.get(subreeid);
1998
             deleteSubtreeAccessRule(subreeid);
1999
             logMetacat.info("Write inline data access into " +
2000
                                   "xml_access table for"+ inlineFileName);
2001
             writeGivenAccessRuleIntoDB(permOrder, accessRule,
2002
                                        inlineFileName, subreeid);
2003
             // Save guid of data object for syncing of access policy with CN after parsing
2004
             // is successful (see DocumentImpl.write)
2005
             
2006
             // look-up pid assuming docid
2007
             String dataGuid = inlineFileName;
2008
             try {
2009
	             String dataDocid = DocumentUtil.getDocIdFromAccessionNumber(inlineFileName);
2010
	             int dataRev = DocumentUtil.getRevisionFromAccessionNumber(inlineFileName);
2011
	             dataGuid = IdentifierManager.getInstance().getGUID(dataDocid, dataRev);
2012
             } catch (McdbDocNotFoundException e) {
2013
            	 // log the warning
2014
            	 logMetacat.warn("No pid found for [assumed] data docid: " + inlineFileName);
2015
            	 // what do you do?
2016
             }
2017
			guidsToSync.add(dataGuid);
2018
           }
2019
           else if (onlineURLDistributionIdList.containsKey(subreeid))
2020
           {
2021
             String url = (String)onlineURLDistributionIdList.get(subreeid);
2022
             //this method will extrace data file id from url. It also will
2023
             // check if user can change the access rules for the data file id.
2024
             // if couldn't, it will throw a exception. Morover, the docid will
2025
             // be to relation id vector too.
2026
             // for online data, the subtree id we set is null.
2027
             // So in xml_access, only the inline data subteeid is not null
2028
             String dataFileName = handleOnlineUrlDataFile(url);
2029
             logMetacat.info("The data fileName in online url " +
2030
                                      dataFileName);
2031
             if (dataFileName != null)
2032
             {
2033
               deletePermissionsInAccessTableForDoc(dataFileName);
2034
               writeGivenAccessRuleIntoDB(permOrder, accessRule,
2035
                                          dataFileName, null);
2036
               logMetacat.info("Write online data access into " +
2037
                                   "xml_access table for " + dataFileName);
2038
               // Save guid of data object for syncing of access policy with CN after parsing
2039
               // is successful (see DocumentImpl.write)
2040
               guidsToSync.add(dataFileName);
2041
               // put the id into a hashtalbe. So when we run wirtetop level
2042
               // access, those id will be ignored because they already has
2043
               // additional access rules
2044
               onlineURLIdHasadditionalAccess.put(subreeid, subreeid);
2045
             }
2046
           }//elseif
2047
         }//if
2048
       }//for
2049
     }//for
2050

    
2051
   }//writeAdditonalLevelAccessRuletoDB
2052

    
2053

    
2054
    /* The method to write additional access rule into db. */
2055
    private void writeTopLevelAccessRuleToDB() throws SAXException
2056
    {
2057
       // if top access is reference, we need figure out the real access rules
2058
       // it points to
2059
       //System.out.println("permorder in top level" + topAccessSection.getPermissionOrder());
2060
       AccessSection newAccess = resolveAccessRuleReference(topAccessSection);
2061
       //System.out.println("permorder in new level" + newAccess.getPermissionOrder());
2062
       String permOrder = newAccess.getPermissionOrder();
2063
       if (permOrder.equals(AccessControlInterface.DENYFIRST) && ignoreDenyFirst) {
2064
    	   logMetacat.warn("Metacat no longer supports EML 'denyFirst' access rules - ignoring this access block");
2065
    	   return;
2066
       }
2067
       Vector accessRule = newAccess.getAccessRules();
2068
       String subtree     = null;
2069
       
2070
       // document itself
2071
       // use GUID
2072
       String guid = null;
2073
		try {
2074
			guid = IdentifierManager.getInstance().getGUID(docid, Integer.valueOf(revision));
2075
		} catch (NumberFormatException e) {
2076
			throw new SAXException(e.getMessage(), e);
2077
		} catch (McdbDocNotFoundException e) {
2078
			// register the default mapping now
2079
			guid = docid + "." + revision;
2080
			IdentifierManager.getInstance().createMapping(guid, guid);
2081
		}
2082
       deletePermissionsInAccessTableForDoc(guid);
2083
       writeGivenAccessRuleIntoDB(permOrder, accessRule, guid, subtree);
2084
       
2085
       // for online data, it includes with id and without id.
2086
       // 1. for the data with subtree id, we should ignore the ones already in
2087
       // the hash - onlineURLIdHasAddionalAccess.
2088
       // 2. for those without subreeid, it couldn't have additional access and we
2089
       // couldn't ignore it.
2090
       // 3. for inline data, we need do nothing because if it doesn't have
2091
       // additional access, it default value is the top one.
2092

    
2093
       // here is the online url with id
2094
       Enumeration em = onlineURLDistributionIdList.keys();
2095
       while (em.hasMoreElements())
2096
       {
2097
         String onlineSubtreeId = (String)em.nextElement();
2098
         if (!onlineURLIdHasadditionalAccess.containsKey(onlineSubtreeId))
2099
         {
2100
            String url =
2101
                       (String)onlineURLDistributionIdList.get(onlineSubtreeId);
2102
            String onlineDataId = handleOnlineUrlDataFile(url);
2103
            if (onlineDataId != null)
2104
            {
2105
              deletePermissionsInAccessTableForDoc(onlineDataId);
2106
              writeGivenAccessRuleIntoDB(permOrder, accessRule,
2107
                                         onlineDataId, subtree);
2108
            }
2109

    
2110
         }
2111
       }//while
2112

    
2113
       // here is the onlineURL without id
2114
       for (int i= 0; i < onlineURLDistributionListWithoutId.size(); i++)
2115
       {
2116
         String url = (String)onlineURLDistributionListWithoutId.elementAt(i);
2117
         String onlineDataId = handleOnlineUrlDataFile(url);
2118
         if (onlineDataId != null)
2119
         {
2120
           deletePermissionsInAccessTableForDoc(onlineDataId);
2121
           writeGivenAccessRuleIntoDB(permOrder, accessRule,
2122
                                         onlineDataId, subtree);
2123
         }
2124
       }//for
2125
    }//writeTopAccessRuletoDB
2126

    
2127
    /* Write a gaven access rule into db */
2128
    private void writeGivenAccessRuleIntoDB(String permOrder, Vector accessRules,
2129
                     String dataId, String subTreeId) throws SAXException
2130
    {
2131
      if (permOrder == null || permOrder.trim().equals("") || dataId == null ||
2132
          dataId.trim().equals("") || accessRules == null ||
2133
          accessRules.isEmpty())
2134
      {
2135
        logMetacat.info("The access object is null and tried to " +
2136
                                  " write to xml_access table");
2137
        throw new SAXException("The access object is null");
2138
      }
2139
      
2140
      // geet the guid, not the docid alone
2141
      String guid = null;
2142
		try {
2143
			guid = IdentifierManager.getInstance().getGUID(docid, Integer.valueOf(revision));
2144
		} catch (NumberFormatException e) {
2145
			throw new SAXException(e.getMessage(), e);
2146
		} catch (McdbDocNotFoundException e) {
2147
			// register the default mapping now
2148
			guid = docid + "." + revision;
2149
			IdentifierManager.getInstance().createMapping(guid, guid);
2150
		}
2151
      
2152
       String sql = null;
2153
       PreparedStatement pstmt = null;
2154
       sql = "INSERT INTO xml_access (guid, principal_name, permission, "
2155
               + "perm_type, perm_order, accessfileid, subtreeid) VALUES "
2156
               + " (?, ?, ?, ?, ?, ?, ?)";
2157

    
2158
       try
2159
       {
2160

    
2161
           pstmt = connection.prepareStatement(sql);
2162
           // Increase DBConnection usage count
2163
           connection.increaseUsageCount(1);
2164
           // Bind the values to the query
2165
           pstmt.setString(1, dataId);
2166
           logMetacat.info("guid in accesstable: " + dataId);
2167
           pstmt.setString(6, guid);
2168
           logMetacat.info("Accessfileid in accesstable: " + guid);
2169
           pstmt.setString(5, permOrder);
2170
           logMetacat.info("PermOder in accesstable: " + permOrder);
2171
           pstmt.setString(7, subTreeId);
2172
           logMetacat.info("subtree id in accesstable: " + subTreeId);
2173
           // if it is not top level, set s id
2174

    
2175
           //Vector accessRules = accessSection.getAccessRules();
2176
           // go through every rule
2177
           for (int i = 0; i < accessRules.size(); i++)
2178
           {
2179
               AccessRule rule = (AccessRule) accessRules.elementAt(i);
2180
               String permType = rule.getPermissionType();
2181
               int permission = rule.getPermission();
2182
               pstmt.setInt(3, permission);
2183
               logMetacat.info("permission in accesstable: "
2184
                       + permission);
2185
               pstmt.setString(4, permType);
2186
               logMetacat.info(
2187
                       "Permtype in accesstable: " + permType);
2188
               // go through every principle in rule
2189
               Vector nameVector = rule.getPrincipal();
2190
               for (int j = 0; j < nameVector.size(); j++)
2191
               {
2192
                   String prName = (String) nameVector.elementAt(j);
2193
                   pstmt.setString(2, prName);
2194
                   logMetacat.info("Principal in accesstable: "
2195
                           + prName);
2196
                   logMetacat.debug("running sql: " + pstmt.toString());
2197
                   pstmt.execute();
2198
               }//for
2199
           }//for
2200
           pstmt.close();
2201
       }//try
2202
       catch (SQLException e)
2203
       {
2204
           throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
2205
                   + e.getMessage());
2206
       }//catch
2207
       finally
2208
       {
2209
           try
2210
           {
2211
               pstmt.close();
2212
           }
2213
           catch (SQLException ee)
2214
           {
2215
               throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
2216
                       + ee.getMessage());
2217
           }
2218
       }//finally
2219
       
2220
       // for D1, refresh the entries
2221
       HazelcastService.getInstance().refreshSystemMetadataEntry(guid);
2222
       HazelcastService.getInstance().refreshSystemMetadataEntry(dataId);
2223

    
2224
    }//writeGivenAccessRuleIntoDB
2225

    
2226

    
2227
    /* Delete from db all permission for resources related guid, if any. */
2228
    private void deletePermissionsInAccessTableForDoc(String guid)
2229
            throws SAXException
2230
    {
2231
        PreparedStatement pstmt = null;
2232
        try {
2233
        	String sql = "DELETE FROM xml_access WHERE guid = ? ";
2234
            // delete all acl records for resources related to guid if any
2235
            pstmt = connection.prepareStatement(sql);
2236
            pstmt.setString(1, guid);
2237
            // Increase DBConnection usage count
2238
            connection.increaseUsageCount(1);
2239
            pstmt.execute();
2240

    
2241
        } catch (SQLException e) {
2242
            throw new SAXException(e.getMessage());
2243
        } finally {
2244
            try {
2245
                pstmt.close();
2246
            } catch (SQLException ee) {
2247
                throw new SAXException(ee.getMessage());
2248
            }
2249
        }
2250
    }//deletePermissionsInAccessTable
2251

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

    
2286
    }
2287

    
2288
    private void deleteAllInlineDataAccessRules() throws SAXException
2289
    {
2290
      PreparedStatement pstmt = null;
2291
       try
2292
       {
2293
    	   String sql = 
2294
    		   "DELETE FROM xml_access " +
2295
    		   "WHERE accessfileid IN (SELECT guid from identifier where docid = ? and rev = ?) " +
2296
    		   "AND subtreeid IS NOT NULL";
2297
           pstmt = connection.prepareStatement(sql);
2298
           pstmt.setString(1, docid);
2299
           pstmt.setInt(2, Integer.valueOf(revision));
2300
           // Increase DBConnection usage count
2301
           connection.increaseUsageCount(1);
2302
           pstmt.execute();
2303
       }
2304
       catch (SQLException e)
2305
       {
2306
           throw new SAXException(e.getMessage());
2307
       }
2308
       finally
2309
       {
2310
           try
2311
           {
2312
               pstmt.close();
2313
           }
2314
           catch (SQLException ee)
2315
           {
2316
               throw new SAXException(ee.getMessage());
2317
           }
2318
       }
2319

    
2320
    }
2321

    
2322
    /*
2323
     * In order to make sure only usr has "all" permission can update access
2324
     * subtree in eml document we need to keep access subtree info in
2325
     * xml_accesssubtree table, such as docid, version, startnodeid, endnodeid
2326
     */
2327
    private void writeAccessSubTreeIntoDB(AccessSection accessSection)
2328
                                          throws SAXException
2329
    {
2330
        if (accessSection == null)
2331
        {
2332

    
2333
          logMetacat.info("Access object is null and tried to write "+
2334
                                   "into access subtree table");
2335
          throw new SAXException("The access object is null to write access " +
2336
                                 "sbutree");
2337
        }
2338

    
2339
        String sql = null;
2340
        PreparedStatement pstmt = null;
2341
        sql = "INSERT INTO xml_accesssubtree (docid, rev, controllevel, "
2342
                + "subtreeid, startnodeid, endnodeid) VALUES "
2343
                + " (?, ?, ?, ?, ?, ?)";
2344
        try
2345
        {
2346

    
2347
            pstmt = connection.prepareStatement(sql);
2348
            // Increase DBConnection usage count
2349
            connection.increaseUsageCount(1);
2350
            String level = accessSection.getControlLevel();
2351
            long startNodeId = -1;
2352
            if (level != null && level.equals(DATAACCESSLEVEL))
2353
            {
2354
              // for additional access module the start node id should be
2355
              // descirbes element id
2356
              startNodeId = accessSection.getStartedDescribesNodeId();
2357
              // if in additional access, there is not describes element,
2358
              // in this senario, describesNodeId will be -1. Then we should
2359
              // the start access element id
2360
              if (startNodeId == -1)
2361
              {
2362
                startNodeId = accessSection.getStartNodeId();
2363
              }
2364
            }
2365
            else
2366
            {
2367
                startNodeId = accessSection.getStartNodeId();
2368
            }
2369

    
2370
            long endNodeId = accessSection.getEndNodeId();
2371
            String sectionId = accessSection.getSubTreeId();
2372

    
2373
            if (startNodeId ==-1 || endNodeId == -1)
2374
            {
2375
              throw new SAXException("Don't find start node or end node id " +
2376
                                      "for the access subtee");
2377

    
2378
            }
2379

    
2380
            // Bind the values to the query
2381
            pstmt.setString(1, docid);
2382
            logMetacat.info("Docid in access-subtreetable: " + docid);
2383
            pstmt.setInt(2, (new Integer(revision)).intValue());
2384
            logMetacat.info("rev in accesssubtreetable: " + revision);
2385
            pstmt.setString(3, level);
2386
            logMetacat.info("contorl level in access-subtree table: "
2387
                    + level);
2388
            pstmt.setString(4, sectionId);
2389
            logMetacat.info("Subtree id in access-subtree table: "
2390
                    + sectionId);
2391
            pstmt.setLong(5, startNodeId);
2392
            logMetacat.info("Start node id is: " + startNodeId);
2393
            pstmt.setLong(6, endNodeId);
2394
            logMetacat.info("End node id is: " + endNodeId);
2395
            logMetacat.debug("Eml200SAXHandler.writeAccessSubTreeIntoDB - executing SQL: " + pstmt.toString());
2396
            pstmt.execute();
2397
            pstmt.close();
2398
        }//try
2399
        catch (SQLException e)
2400
        {
2401
            throw new SAXException("EMLSAXHandler.writeAccessSubTreeIntoDB(): "
2402
                    + e.getMessage());
2403
        }//catch
2404
        finally
2405
        {
2406
            try
2407
            {
2408
                pstmt.close();
2409
            }
2410
            catch (SQLException ee)
2411
            {
2412
                throw new SAXException(
2413
                        "EMLSAXHandler.writeAccessSubTreeIntoDB(): "
2414
                                + ee.getMessage());
2415
            }
2416
        }//finally
2417

    
2418
    }//writeAccessSubtreeIntoDB
2419

    
2420
    /* Delete every access subtree record from xml_accesssubtree. */
2421
    private void deleteAccessSubTreeRecord(String docId) throws SAXException
2422
    {
2423
        PreparedStatement pstmt = null;
2424
        try {
2425
        	String sql = "DELETE FROM xml_accesssubtree WHERE docid = ?";
2426
            // delete all acl records for resources related to @aclid if any
2427
            pstmt = connection.prepareStatement(sql);
2428
            pstmt.setString(1, docId);
2429
            // Increase DBConnection usage count
2430
            connection.increaseUsageCount(1);                   
2431
            logMetacat.debug("running sql: " + sql);
2432
            pstmt.execute();
2433

    
2434
        } catch (SQLException e) {
2435
            throw new SAXException(e.getMessage());
2436
        } finally {
2437
            try {
2438
                pstmt.close();
2439
            } catch (SQLException ee) {
2440
                throw new SAXException(ee.getMessage());
2441
            }
2442
        }
2443
    }//deleteAccessSubTreeRecord
2444

    
2445
    // open a file writer for writing inline data to file
2446
    private Writer createInlineDataFileWriter(String fileName, String encoding)
2447
            throws SAXException
2448
    {
2449
        Writer writer = null;
2450
        String path;
2451
        try {
2452
        	 path = PropertyService.getProperty("application.inlinedatafilepath");
2453
        } catch (PropertyNotFoundException pnfe) {
2454
            throw new SAXException(pnfe.getMessage());
2455
        }
2456
        /*
2457
         * File inlineDataDirectory = new File(path);
2458
         */
2459
        String newFile = path + "/" + fileName;
2460
        logMetacat.info("inline file name: " + newFile);
2461
        try {
2462
            // true means append
2463
        	writer = new OutputStreamWriter(new FileOutputStream(newFile, true), encoding);
2464
        } catch (IOException ioe) {
2465
            throw new SAXException(ioe.getMessage());
2466
        }
2467
        return writer;
2468
    }
2469

    
2470
    // write inline data into file system and return file name(without path)
2471
    private void writeInlineDataIntoFile(Writer writer, StringBuffer data)
2472
            throws SAXException
2473
    {
2474
        try {
2475
            writer.write(data.toString());
2476
            writer.flush();
2477
        } catch (Exception e) {
2478
            throw new SAXException(e.getMessage());
2479
        }
2480
    }
2481

    
2482

    
2483

    
2484
    /*
2485
     * In eml2, the inline data wouldn't store in db, it store in file system
2486
     * The db stores file name(without path). We got the old file name from db
2487
     * and compare to the new in line data file
2488
     */
2489
    public boolean compareInlineDataFiles(String oldFileName, String newFileName)
2490
            throws McdbException
2491
    {
2492
        // this method need to be testing
2493
        boolean same = true;
2494
        String data = null;
2495
        try {
2496
        	String path = PropertyService.getProperty("application.inlinedatafilepath");
2497
			// the new file name will look like path/docid.rev.2
2498
			File inlineDataDirectory = new File(path);
2499
			File oldDataFile = new File(inlineDataDirectory, oldFileName);
2500
			File newDataFile = new File(inlineDataDirectory, newFileName);
2501

    
2502
            Reader oldFileReader = new InputStreamReader(new FileInputStream(oldDataFile), encoding);
2503
            BufferedReader oldStringReader = new BufferedReader(oldFileReader);
2504
            Reader newFileReader = new InputStreamReader(new FileInputStream(newDataFile), encoding);
2505
            BufferedReader newStringReader = new BufferedReader(newFileReader);
2506
            // read first line of data
2507
            String oldString = oldStringReader.readLine();
2508
            String newString = newStringReader.readLine();
2509

    
2510
            // at the end oldstring will be null
2511
            while (oldString != null) {
2512
                oldString = oldStringReader.readLine();
2513
                newString = newStringReader.readLine();
2514
                if (!oldString.equals(newString)) {
2515
                    same = false;
2516
                    break;
2517
                }
2518
            }
2519

    
2520
            // if oldString is null but newString is not null, they are same
2521
            if (same) {
2522
                if (newString != null) {
2523
                    same = false;
2524
                }
2525
            }
2526

    
2527
        } catch (Exception e) {
2528
            throw new McdbException(e.getMessage());
2529
        }
2530
        logMetacat.info("the inline data retrieve from file: " + data);
2531
        return same;
2532
    }
2533

    
2534
   /*
2535
	 * Copy a old line file to a new inline file
2536
	 */
2537
	public void copyInlineFile(String inlineDistributionId, String newFileName)
2538
			throws SAXException {
2539
		if (inlineDistributionId == null || newFileName == null) {
2540
			throw new SAXException("Could not copy inline file from old one to new "
2541
					+ "one!");
2542
		}
2543
		// get old file id from previousUnreadable data object
2544
		String oldInlineInternalFileName = (String) previousUnreadableInlineDataObjectHash
2545
				.get(inlineDistributionId);
2546

    
2547
		if (oldInlineInternalFileName == null
2548
				|| oldInlineInternalFileName.trim().equals("")) {
2549
			throw new SAXException("Could not copy inline file from old one to new "
2550
					+ "one because can't find old file name");
2551
		}
2552
		logMetacat.info("in handle inline data");
2553
		logMetacat.info("the inline data file name from xml_access is: "
2554
				+ oldInlineInternalFileName);
2555

    
2556
		InputStream oldFileReader = null;
2557
		OutputStream newFileWriter = null;
2558
		try {
2559
			String path = PropertyService.getProperty("application.inlinedatafilepath");
2560
			// the new file name will look like path/docid.rev.2
2561
			File inlineDataDirectory = new File(path);
2562
			File oldDataFile = new File(inlineDataDirectory, oldInlineInternalFileName);
2563
			File newDataFile = new File(inlineDataDirectory, newFileName);
2564

    
2565
			oldFileReader = new FileInputStream(oldDataFile);
2566
			newFileWriter = new FileOutputStream(newDataFile);
2567
			byte[] buf = new byte[4 * 1024]; // 4K buffer
2568
			int b = oldFileReader.read(buf);
2569
			while (b != -1) {
2570
				newFileWriter.write(buf, 0, b);
2571
				b = oldFileReader.read(buf);
2572
			}
2573
		} catch (Exception e) {
2574
			throw new SAXException(e.getMessage());
2575
		} finally {
2576
			if (oldFileReader != null) {
2577
				try {
2578
					oldFileReader.close();
2579
				} catch (Exception ee) {
2580
					throw new SAXException(ee.getMessage());
2581
				}
2582

    
2583
			}
2584
			if (newFileWriter != null) {
2585
				try {
2586
					newFileWriter.close();
2587
				} catch (Exception ee) {
2588
					throw new SAXException(ee.getMessage());
2589
				}
2590

    
2591
			}
2592
		}
2593
	}
2594

    
2595

    
2596
    // if xml file failed to upload, we need to call this method to delete
2597
    // the inline data already in file system
2598
    public void deleteInlineFiles() throws SAXException
2599
    {
2600
        if (!inlineFileIDList.isEmpty()) {
2601
            for (int i = 0; i < inlineFileIDList.size(); i++) {
2602
                String fileName = (String) inlineFileIDList.elementAt(i);
2603
                deleteInlineDataFile(fileName);
2604
            }
2605
        }
2606
    }
2607

    
2608
    /* delete the inline data file */
2609
    private void deleteInlineDataFile(String fileName) throws SAXException
2610
    {
2611
    	String path;
2612
    	try {
2613
    		path = PropertyService.getProperty("application.inlinedatafilepath");
2614
    	} catch (PropertyNotFoundException pnfe) {
2615
    		throw new SAXException ("Could not find inline data file path: " 
2616
    				+ pnfe.getMessage());
2617
    	}
2618
        File inlineDataDirectory = new File(path);
2619
        File newFile = new File(inlineDataDirectory, fileName);
2620
        newFile.delete();
2621

    
2622
    }
2623

    
2624
    /*
2625
	 * In eml2, the inline data wouldn't store in db, it store in file system
2626
	 * The db stores file name(without path).
2627
	 */
2628
	public static Reader readInlineDataFromFileSystem(String fileName, String encoding)
2629
			throws McdbException {
2630
		// BufferedReader stringReader = null;
2631
		Reader fileReader = null;
2632
		try {
2633
			String path = PropertyService.getProperty("application.inlinedatafilepath");
2634
			// the new file name will look like path/docid.rev.2
2635
			File inlineDataDirectory = new File(path);
2636
			File dataFile = new File(inlineDataDirectory, fileName);
2637

    
2638
			fileReader = new InputStreamReader(new FileInputStream(dataFile), encoding);
2639
			// stringReader = new BufferedReader(fileReader);
2640
		} catch (Exception e) {
2641
			throw new McdbException(e.getMessage());
2642
		}
2643
		// return stringReader;
2644
		return fileReader;
2645
	}
2646

    
2647
    /* Delete relations */
2648
    private void deleteRelations() throws SAXException
2649
    {
2650
        PreparedStatement pStmt = null;
2651
        String sql = "DELETE FROM xml_relation where docid =?";
2652
        try {
2653
            pStmt = connection.prepareStatement(sql);
2654
            //bind variable
2655
            pStmt.setString(1, docid);
2656
            //execute query
2657
            logMetacat.debug("Eml200SAXHandler.deleteRelations - executing SQL: " + pStmt.toString());
2658
            pStmt.execute();
2659
            pStmt.close();
2660
        }//try
2661
        catch (SQLException e) {
2662
            throw new SAXException("EMLSAXHandler.deleteRelations(): "
2663
                    + e.getMessage());
2664
        }//catch
2665
        finally {
2666
            try {
2667
                pStmt.close();
2668
            }//try
2669
            catch (SQLException ee) {
2670
                throw new SAXException("EMLSAXHandler.deleteRelations: "
2671
                        + ee.getMessage());
2672
            }//catch
2673
        }//finally
2674
    }
2675

    
2676
    /* Write an online data file id into xml_relation table. The dataId shouldnot
2677
     * have the revision
2678
     */
2679
    private void writeOnlineDataFileIdIntoRelationTable(String dataId)
2680
            throws SAXException
2681
    {
2682
        PreparedStatement pStmt = null;
2683
        String sql = "INSERT into xml_relation (docid, packagetype, subject, "
2684
                + "relationship, object) values (?, ?, ?, ?, ?)";
2685
        try {
2686
            pStmt = connection.prepareStatement(sql);
2687
            //bind variable
2688
            pStmt.setString(1, docid);
2689
            pStmt.setString(2, doctype);
2690
            pStmt.setString(3, docid);
2691
            pStmt.setString(4, RELATION);
2692
            pStmt.setString(5, dataId);
2693
            //execute query
2694
            logMetacat.debug("Eml200SAXHandler.writeOnlineDataFileIdIntoRelationTable - executing SQL: " + pStmt.toString());
2695
            pStmt.execute();
2696
            pStmt.close();
2697
        }//try
2698
        catch (SQLException e) {
2699
            throw new SAXException(
2700
                    "EMLSAXHandler.writeOnlineDataFileIdIntoRelationTable(): "
2701
                            + e.getMessage());
2702
        }//catch
2703
        finally {
2704
            try {
2705
                pStmt.close();
2706
            }//try
2707
            catch (SQLException ee) {
2708
                throw new SAXException(
2709
                        "EMLSAXHandler.writeOnlineDataFileIdIntoRelationTable(): "
2710
                                + ee.getMessage());
2711
            }//catch
2712
        }//finally
2713

    
2714
    }//writeOnlineDataFileIdIntoRelationTable
2715

    
2716
    /*
2717
     * This method will handle data file in online url. If the data file is in
2718
     * ecogrid protocol, then the datafile identifier (guid) be returned.
2719
     * otherwise, null will be returned.
2720
     * If the data file doesn't exsit in xml_documents or
2721
     * xml_revision table, or the user has all permission to the data file if
2722
     * the docid already existed, the data file id (guid) will be returned
2723
     * NEED to do:
2724
     * We should also need to implement http and ftp. Those
2725
     * external files should be download and assign a data file id to it.
2726
     */
2727
    private String handleOnlineUrlDataFile(String url) throws SAXException
2728
    {
2729
      logMetacat.warn("The url is "+ url);
2730
      String docid = null;
2731
      String guid = null;
2732

    
2733
      // if the url is not a ecogrid protocol, null will be getten
2734
      String accessionNumber =
2735
    	  DocumentUtil.getAccessionNumberFromEcogridIdentifier(url);
2736
      
2737
      // check for valid docid.rev
2738
      int rev = 0;
2739
      if (accessionNumber != null) {
2740
			// get rid of revision number to get the docid.
2741
			try {
2742
				docid = DocumentUtil.getDocIdFromAccessionNumber(accessionNumber);
2743
				rev = DocumentUtil.getRevisionFromAccessionNumber(accessionNumber);
2744
			} catch (Exception e) {
2745
				logMetacat.warn(e.getClass().getName() + " - Problem parsing accession number for: " + accessionNumber + ". Message: " + e.getMessage());
2746
				accessionNumber = null;
2747
			}
2748
		}
2749
      
2750
      if (accessionNumber != null)
2751
      {
2752
		try {
2753
			guid = IdentifierManager.getInstance().getGUID(docid, rev);
2754
		} catch (McdbDocNotFoundException e1) {
2755
			guid = docid + "." + rev;
2756
			IdentifierManager.getInstance().createMapping(guid, guid);
2757
		}
2758
        onlineDataFileIdInRelationVector.add(docid);
2759
        try
2760
        {
2761

    
2762
          if (!AccessionNumber.accNumberUsed(docid))
2763
          {
2764
            return guid;
2765
          }
2766
          // check the previous revision if we have it
2767
          int previousRevision = rev;
2768
          Vector<Integer> revisions = DBUtil.getRevListFromRevisionTable(docid);
2769
          if (revisions != null && revisions.size() > 0) {
2770
        	  previousRevision = revisions.get(revisions.size() - 1);
2771
          }
2772
          String previousDocid = 
2773
        	  docid + PropertyService.getProperty("document.accNumSeparator") + previousRevision;
2774
          PermissionController controller = new PermissionController(previousDocid);
2775
          if (controller.hasPermission(user, groups, AccessControlInterface.ALLSTRING)) {
2776
            return guid;
2777
          }
2778
          else
2779
          {
2780
            throw new SAXException("User: " + user + " does not have permission to update " +
2781
                  "access rules for data file "+ guid);
2782
          }
2783
        }//try
2784
        catch(Exception e)
2785
        {
2786
          logMetacat.error("Error in " +
2787
                                "Eml200SAXHanlder.handleOnlineUrlDataFile is " +
2788
                                 e.getMessage());
2789
          throw new SAXException(e.getMessage());
2790
        }
2791
      }
2792
      return guid;
2793
    }
2794

    
2795
    private void compareElementNameSpaceAttributes(Stack unchangableNodeStack,
2796
            Hashtable nameSpaces, Attributes attributes, String localName,
2797
            String error) throws SAXException
2798
    {
2799
        //Get element subtree node stack (element node)
2800
        NodeRecord elementNode = null;
2801
        try {
2802
            elementNode = (NodeRecord) unchangableNodeStack.pop();
2803
        } catch (EmptyStackException ee) {
2804
            logMetacat.error("Node stack is empty for element data");
2805
            throw new SAXException(error);
2806
        }
2807
        logMetacat.info("current node type from xml is ELEMENT");
2808
        logMetacat.info("node type from stack: "
2809
                + elementNode.getNodeType());
2810
        logMetacat.info("node name from xml document: " + localName);
2811
        logMetacat.info("node name from stack: "
2812
                + elementNode.getNodeName());
2813
        logMetacat.info("node data from stack: "
2814
                + elementNode.getNodeData());
2815
        logMetacat.info("node id is: " + elementNode.getNodeId());
2816
        // if this node is not element or local name not equal or name space
2817
        // not
2818
        // equals, throw an exception
2819
        if (!elementNode.getNodeType().equals("ELEMENT")
2820
                || !localName.equals(elementNode.getNodeName()))
2821
        //  (uri != null && !uri.equals(elementNode.getNodePrefix())))
2822
        {
2823
            logMetacat.info("Inconsistence happend: ");
2824
            logMetacat.info("current node type from xml is ELEMENT");
2825
            logMetacat.info("node type from stack: "
2826
                    + elementNode.getNodeType());
2827
            logMetacat.info("node name from xml document: "
2828
                    + localName);
2829
            logMetacat.info("node name from stack: "
2830
                    + elementNode.getNodeName());
2831
            logMetacat.info("node data from stack: "
2832
                    + elementNode.getNodeData());
2833
            logMetacat.info("node id is: " + elementNode.getNodeId());
2834
            throw new SAXException(error);
2835
        }
2836

    
2837
        //compare namespace
2838
        Enumeration nameEn = nameSpaces.keys();
2839
        while (nameEn.hasMoreElements()) {
2840
            //Get namespacke node stack (element node)
2841
            NodeRecord nameNode = null;
2842
            try {
2843
                nameNode = (NodeRecord) unchangableNodeStack.pop();
2844
            } catch (EmptyStackException ee) {
2845
                logMetacat.error(
2846
                        "Node stack is empty for namespace data");
2847
                throw new SAXException(error);
2848
            }
2849

    
2850
            String prefixName = (String) nameEn.nextElement();
2851
            String nameSpaceUri = (String) nameSpaces.get(prefixName);
2852
            if (!nameNode.getNodeType().equals("NAMESPACE")
2853
                    || !prefixName.equals(nameNode.getNodeName())
2854
                    || !nameSpaceUri.equals(nameNode.getNodeData())) {
2855
                logMetacat.info("Inconsistence happend: ");
2856
                logMetacat.info(
2857
                        "current node type from xml is NAMESPACE");
2858
                logMetacat.info("node type from stack: "
2859
                        + nameNode.getNodeType());
2860
                logMetacat.info("current node name from xml is: "
2861
                        + prefixName);
2862
                logMetacat.info("node name from stack: "
2863
                        + nameNode.getNodeName());
2864
                logMetacat.info("current node data from xml is: "
2865
                        + nameSpaceUri);
2866
                logMetacat.info("node data from stack: "
2867
                        + nameNode.getNodeData());
2868
                logMetacat.info("node id is: " + nameNode.getNodeId());
2869
                throw new SAXException(error);
2870
            }
2871

    
2872
        }//while
2873

    
2874
        //compare attributes
2875
        for (int i = 0; i < attributes.getLength(); i++) {
2876
            NodeRecord attriNode = null;
2877
            try {
2878
                attriNode = (NodeRecord) unchangableNodeStack.pop();
2879

    
2880
            } catch (EmptyStackException ee) {
2881
                logMetacat.error(
2882
                        "Node stack is empty for attribute data");
2883
                throw new SAXException(error);
2884
            }
2885
            String attributeName = attributes.getQName(i);
2886
            String attributeValue = attributes.getValue(i);
2887
            logMetacat.info(
2888
                    "current node type from xml is ATTRIBUTE ");
2889
            logMetacat.info("node type from stack: "
2890
                    + attriNode.getNodeType());
2891
            logMetacat.info("current node name from xml is: "
2892
                    + attributeName);
2893
            logMetacat.info("node name from stack: "
2894
                    + attriNode.getNodeName());
2895
            logMetacat.info("current node data from xml is: "
2896
                    + attributeValue);
2897
            logMetacat.info("node data from stack: "
2898
                    + attriNode.getNodeData());
2899
            logMetacat.info("node id  is: " + attriNode.getNodeId());
2900

    
2901
            if (!attriNode.getNodeType().equals("ATTRIBUTE")
2902
                    || !attributeName.equals(attriNode.getNodeName())
2903
                    || !attributeValue.equals(attriNode.getNodeData())) {
2904
                logMetacat.info("Inconsistence happend: ");
2905
                logMetacat.info(
2906
                        "current node type from xml is ATTRIBUTE ");
2907
                logMetacat.info("node type from stack: "
2908
                        + attriNode.getNodeType());
2909
                logMetacat.info("current node name from xml is: "
2910
                        + attributeName);
2911
                logMetacat.info("node name from stack: "
2912
                        + attriNode.getNodeName());
2913
                logMetacat.info("current node data from xml is: "
2914
                        + attributeValue);
2915
                logMetacat.info("node data from stack: "
2916
                        + attriNode.getNodeData());
2917
                logMetacat.info("node is: " + attriNode.getNodeId());
2918
                throw new SAXException(error);
2919
            }
2920
        }//for
2921

    
2922
    }
2923

    
2924
    /* mehtod to compare current text node and node in db */
2925
    private void compareTextNode(Stack nodeStack, StringBuffer text,
2926
            String error) throws SAXException
2927
    {
2928
        NodeRecord node = null;
2929
        //get node from current stack
2930
        try {
2931
            node = (NodeRecord) nodeStack.pop();
2932
        } catch (EmptyStackException ee) {
2933
            logMetacat.error(
2934
                    "Node stack is empty for text data in startElement");
2935
            throw new SAXException(error);
2936
        }
2937
        logMetacat.info(
2938
                "current node type from xml is TEXT in start element");
2939
        logMetacat.info("node type from stack: " + node.getNodeType());
2940
        logMetacat.info("current node data from xml is: "
2941
                + text.toString());
2942
        logMetacat.info("node data from stack: " + node.getNodeData());
2943
        logMetacat.info("node name from stack: " + node.getNodeName());
2944
        logMetacat.info("node is: " + node.getNodeId());
2945
        if (!node.getNodeType().equals("TEXT")
2946
                || !(text.toString()).equals(node.getNodeData())) {
2947
            logMetacat.info("Inconsistence happend: ");
2948
            logMetacat.info(
2949
                    "current node type from xml is TEXT in start element");
2950
            logMetacat.info("node type from stack: "
2951
                    + node.getNodeType());
2952
            logMetacat.info("current node data from xml is: "
2953
                    + text.toString());
2954
            logMetacat.info("node data from stack: "
2955
                    + node.getNodeData());
2956
            logMetacat.info("node name from stack: "
2957
                    + node.getNodeName());
2958
            logMetacat.info("node is: " + node.getNodeId());
2959
            throw new SAXException(error);
2960
        }//if
2961
    }
2962

    
2963
    /* Comparet comment from xml and db */
2964
    private void compareCommentNode(Stack nodeStack, String string, String error)
2965
            throws SAXException
2966
    {
2967
        NodeRecord node = null;
2968
        try {
2969
            node = (NodeRecord) nodeStack.pop();
2970
        } catch (EmptyStackException ee) {
2971
            logMetacat.error("the stack is empty for comment data");
2972
            throw new SAXException(error);
2973
        }
2974
        logMetacat.info("current node type from xml is COMMENT");
2975
        logMetacat.info("node type from stack: " + node.getNodeType());
2976
        logMetacat.info("current node data from xml is: " + string);
2977
        logMetacat.info("node data from stack: " + node.getNodeData());
2978
        logMetacat.info("node is from stack: " + node.getNodeId());
2979
        // if not consistent terminate program and throw a exception
2980
        if (!node.getNodeType().equals("COMMENT")
2981
                || !string.equals(node.getNodeData())) {
2982
            logMetacat.info("Inconsistence happend: ");
2983
            logMetacat.info("current node type from xml is COMMENT");
2984
            logMetacat.info("node type from stack: "
2985
                    + node.getNodeType());
2986
            logMetacat.info(
2987
                    "current node data from xml is: " + string);
2988
            logMetacat.info("node data from stack: "
2989
                    + node.getNodeData());
2990
            logMetacat.info("node is from stack: " + node.getNodeId());
2991
            throw new SAXException(error);
2992
        }//if
2993
    }
2994

    
2995
    /* Compare whitespace from xml and db */
2996
   private void compareWhiteSpace(Stack nodeStack, String string, String error)
2997
           throws SAXException
2998
   {
2999
       NodeRecord node = null;
3000
       try {
3001
           node = (NodeRecord) nodeStack.pop();
3002
       } catch (EmptyStackException ee) {
3003
           logMetacat.error("the stack is empty for whitespace data");
3004
           throw new SAXException(error);
3005
       }
3006
       if (!node.getNodeType().equals("TEXT")
3007
               || !string.equals(node.getNodeData())) {
3008
           logMetacat.info("Inconsistence happend: ");
3009
           logMetacat.info(
3010
                   "current node type from xml is WHITESPACE TEXT");
3011
           logMetacat.info("node type from stack: "
3012
                   + node.getNodeType());
3013
           logMetacat.info(
3014
                   "current node data from xml is: " + string);
3015
           logMetacat.info("node data from stack: "
3016
                   + node.getNodeData());
3017
           logMetacat.info("node is from stack: " + node.getNodeId());
3018
           throw new SAXException(error);
3019
       }//if
3020
   }
3021

    
3022

    
3023

    
3024
}
(31-31/63)