Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2010 Regents of the University of California and the
4
 *             National Center for Ecological Analysis and Synthesis
5
 *
6
 *   '$Author: jones $'
7
 *     '$Date: 2010-02-03 17:58:12 -0900 (Wed, 03 Feb 2010) $'
8
 * '$Revision: 5211 $'
9
 *
10
 * This program is free software; you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation; either version 2 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program; if not, write to the Free Software
22
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
 */
24

    
25
package edu.ucsb.nceas.metacat;
26

    
27
import java.math.BigInteger;
28
import java.sql.PreparedStatement;
29
import java.sql.ResultSet;
30
import java.sql.SQLException;
31
import java.sql.Timestamp;
32
import java.util.ArrayList;
33
import java.util.Date;
34
import java.util.Hashtable;
35
import java.util.List;
36
import java.util.Vector;
37

    
38
import org.apache.log4j.Logger;
39
import org.dataone.client.ObjectFormatCache;
40
import org.dataone.service.exceptions.BaseException;
41
import org.dataone.service.exceptions.InvalidSystemMetadata;
42
import org.dataone.service.types.v1.AccessPolicy;
43
import org.dataone.service.types.v1.AccessRule;
44
import org.dataone.service.types.v1.Checksum;
45
import org.dataone.service.types.v1.Identifier;
46
import org.dataone.service.types.v1.NodeReference;
47
import org.dataone.service.types.v1.ObjectFormatIdentifier;
48
import org.dataone.service.types.v1.ObjectInfo;
49
import org.dataone.service.types.v1.ObjectList;
50
import org.dataone.service.types.v1.Permission;
51
import org.dataone.service.types.v1.Replica;
52
import org.dataone.service.types.v1.ReplicationPolicy;
53
import org.dataone.service.types.v1.ReplicationStatus;
54
import org.dataone.service.types.v1.Subject;
55
import org.dataone.service.types.v1.SystemMetadata;
56

    
57
import edu.ucsb.nceas.metacat.accesscontrol.AccessControlInterface;
58
import edu.ucsb.nceas.metacat.accesscontrol.XMLAccessAccess;
59
import edu.ucsb.nceas.metacat.accesscontrol.XMLAccessDAO;
60
import edu.ucsb.nceas.metacat.database.DBConnection;
61
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
62
import edu.ucsb.nceas.metacat.properties.PropertyService;
63
import edu.ucsb.nceas.metacat.shared.AccessException;
64
import edu.ucsb.nceas.metacat.shared.ServiceException;
65
import edu.ucsb.nceas.metacat.util.DocumentUtil;
66
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
67

    
68
/**
69
 * Manage the relationship between Metacat local identifiers (LocalIDs) that are
70
 * codified as the (docid, rev) pair with globally unique string identifiers
71
 * (GUIDs) that are opaque strings.  This class provides methods to manage these
72
 * identifiers, and to search for and look up LocalIDs based on their GUID and
73
 * vice versa. IdentifierManager is a singleton.
74
 * 
75
 * @author Matthew Jones
76
 */
77
public class IdentifierManager {
78
    
79
    public static final String TYPE_SYSTEM_METADATA = "systemmetadata";
80
    public static final String TYPE_IDENTIFIER = "identifier";
81
  
82
    /**
83
     * The single instance of the manager that is always returned.
84
     */
85
    private static IdentifierManager self = null;
86
    private Logger logMetacat = Logger.getLogger(IdentifierManager.class);
87

    
88
    /**
89
     * A private constructor that initializes the class when getInstance() is
90
     * called.
91
     */
92
    private IdentifierManager() {}
93

    
94
    /**
95
     * Return the single instance of the manager after initializing it if it
96
     * wasn't previously initialized.
97
     * 
98
     * @return the single IdentifierManager instance
99
     */
100
    public static IdentifierManager getInstance()
101
    {
102
        if (self == null) {
103
            self = new IdentifierManager();
104
        }
105
        return self;
106
    }
107
    
108
    public SystemMetadata asSystemMetadata(Date dateUploaded, String rightsHolder,
109
            String checksum, String checksumAlgorithm, String originMemberNode,
110
            String authoritativeMemberNode, Date dateModified, String submitter, 
111
            String guid, String fmtidStr, BigInteger size, BigInteger serialVersion) {
112
        SystemMetadata sysMeta = new SystemMetadata();
113

    
114
        Identifier sysMetaId = new Identifier();
115
        sysMetaId.setValue(guid);
116
        sysMeta.setIdentifier(sysMetaId);
117
        sysMeta.setDateUploaded(dateUploaded);
118
        Subject rightsHolderSubject = new Subject();
119
        rightsHolderSubject.setValue(rightsHolder);
120
        sysMeta.setRightsHolder(rightsHolderSubject);
121
        Checksum checksumObject = new Checksum();
122
        checksumObject.setValue(checksum);
123
        checksumObject.setAlgorithm(checksumAlgorithm);
124
        sysMeta.setChecksum(checksumObject);
125
        NodeReference omn = new NodeReference();
126
        omn.setValue(originMemberNode);
127
        sysMeta.setOriginMemberNode(omn);
128
        NodeReference amn = new NodeReference();
129
        amn.setValue(authoritativeMemberNode);
130
        sysMeta.setAuthoritativeMemberNode(amn);
131
        sysMeta.setDateSysMetadataModified(dateModified);
132
        Subject submitterSubject = new Subject();
133
        submitterSubject.setValue(submitter);
134
        sysMeta.setSubmitter(submitterSubject);
135
        ObjectFormatIdentifier fmtid = null;
136
        try {
137
        	ObjectFormatIdentifier formatId = new ObjectFormatIdentifier();
138
        	formatId.setValue(fmtidStr);
139
        	fmtid = ObjectFormatCache.getInstance().getFormat(formatId).getFormatId();
140
        	sysMeta.setFormatId(fmtid);
141
        	
142
        } catch (BaseException nfe) {
143
            logMetacat.error("The objectFormat " + fmtidStr +
144
          	" is not registered. Setting the default format id.");
145
            fmtid = new ObjectFormatIdentifier();
146
            fmtid.setValue("application/octet-stream");
147
            sysMeta.setFormatId(fmtid);
148
            
149
        }
150
        sysMeta.setSize(size);
151
        sysMeta.setSerialVersion(serialVersion);
152
        
153
        return sysMeta;
154
    }
155
    
156
    /**
157
     * return a hash of all of the info that is in the systemmetadata table
158
     * @param localId
159
     * @return
160
     */
161
    public Hashtable<String, String> getSystemMetadataInfo(String localId)
162
    throws McdbDocNotFoundException
163
    {
164
        try
165
        {
166
            AccessionNumber acc = new AccessionNumber(localId, "NONE");
167
            localId = acc.getDocid();
168
        }
169
        catch(Exception e)
170
        {
171
            //do nothing. just try the localId as it is
172
        }
173
        Hashtable<String, String> h = new Hashtable<String, String>();
174
        String sql = "select guid, date_uploaded, rights_holder, checksum, checksum_algorithm, " +
175
          "origin_member_node, authoritive_member_node, date_modified, submitter, object_format, size " +
176
          "from systemmetadata where docid = ?";
177
        DBConnection dbConn = null;
178
        int serialNumber = -1;
179
        try 
180
        {
181
            // Get a database connection from the pool
182
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getDocumentInfo");
183
            serialNumber = dbConn.getCheckOutSerialNumber();
184

    
185
            // Execute the insert statement
186
            PreparedStatement stmt = dbConn.prepareStatement(sql);
187
            stmt.setString(1, localId);
188
            ResultSet rs = stmt.executeQuery();
189
            if (rs.next()) 
190
            {
191
                String guid = rs.getString(1);
192
                Timestamp dateUploaded = rs.getTimestamp(2);
193
                String rightsHolder = rs.getString(3);
194
                String checksum = rs.getString(4);
195
                String checksumAlgorithm = rs.getString(5);
196
                String originMemberNode = rs.getString(6);
197
                String authoritativeMemberNode = rs.getString(7);
198
                Timestamp dateModified = rs.getTimestamp(8);
199
                String submitter = rs.getString(9);
200
                String objectFormat = rs.getString(10);
201
                long size = new Long(rs.getString(11)).longValue();
202
                
203
                h.put("guid", guid);
204
                h.put("date_uploaded", new Long(dateUploaded.getTime()).toString());
205
                h.put("rights_holder", rightsHolder);
206
                h.put("checksum", checksum);
207
                h.put("checksum_algorithm", checksumAlgorithm);
208
                h.put("origin_member_node", originMemberNode);
209
                h.put("authoritative_member_node", authoritativeMemberNode);
210
                h.put("date_modified", new Long(dateModified.getTime()).toString());
211
                h.put("submitter", submitter);
212
                h.put("object_format", objectFormat);
213
                h.put("size", new Long(size).toString());
214
                
215
                stmt.close();
216
            } 
217
            else
218
            {
219
                stmt.close();
220
                DBConnectionPool.returnDBConnection(dbConn, serialNumber);
221
                throw new McdbDocNotFoundException("2Could not find document " + localId);
222
            }
223
            
224
        } 
225
        catch (SQLException e) 
226
        {
227
            e.printStackTrace();
228
            logMetacat.error("Error while getting system metadata info for localid " + localId + " : "  
229
                    + e.getMessage());
230
        } 
231
        finally 
232
        {
233
            // Return database connection to the pool
234
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
235
        }
236
        return h;
237
    }
238
    
239
    /**
240
     * return a hash of all of the info that is in the systemmetadata table
241
     * @param guid
242
     * @return
243
     * @throws McdbDocNotFoundException 
244
     */
245
    public SystemMetadata getSystemMetadata(String guid)
246
    	throws McdbDocNotFoundException
247
    {
248
        
249
        SystemMetadata sysMeta = new SystemMetadata();
250
        String sql = "select guid, date_uploaded, rights_holder, checksum, checksum_algorithm, " +
251
          "origin_member_node, authoritive_member_node, date_modified, submitter, object_format, size, " +
252
          "replication_allowed, number_replicas, obsoletes, obsoleted_by, serial_version " +
253
          "from systemmetadata where guid = ?";
254
        DBConnection dbConn = null;
255
        int serialNumber = -1;
256
        Boolean replicationAllowed = new Boolean(false);
257
        BigInteger numberOfReplicas = new BigInteger("-1");
258
        BigInteger serialVersion = new BigInteger("-1");
259

    
260
        try 
261
        {
262
            // Get a database connection from the pool
263
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getSystemMetadata");
264
            serialNumber = dbConn.getCheckOutSerialNumber();
265

    
266
            // Execute the statement
267
            PreparedStatement stmt = dbConn.prepareStatement(sql);
268
            stmt.setString(1, guid);
269
            ResultSet rs = stmt.executeQuery();
270
            if (rs.next()) 
271
            {
272
                Timestamp dateUploaded = rs.getTimestamp(2);
273
                String rightsHolder = rs.getString(3);
274
                String checksum = rs.getString(4);
275
                String checksumAlgorithm = rs.getString(5);
276
                String originMemberNode = rs.getString(6);
277
                String authoritativeMemberNode = rs.getString(7);
278
                Timestamp dateModified = rs.getTimestamp(8);
279
                String submitter = rs.getString(9);
280
                String fmtidStr = rs.getString(10);
281
                BigInteger size = new BigInteger(rs.getString(11));
282
                replicationAllowed = new Boolean(rs.getBoolean(12));
283
                numberOfReplicas = new BigInteger(rs.getString(13));
284
                String obsoletes = rs.getString(14);
285
                String obsoletedBy = rs.getString(15);
286
                serialVersion = new BigInteger(rs.getString(16));
287

    
288

    
289
                Identifier sysMetaId = new Identifier();
290
                sysMetaId.setValue(guid);
291
                sysMeta.setIdentifier(sysMetaId);
292
                sysMeta.setSerialVersion(serialVersion);
293
                sysMeta.setDateUploaded(dateUploaded);
294
                Subject rightsHolderSubject = new Subject();
295
                rightsHolderSubject.setValue(rightsHolder);
296
                sysMeta.setRightsHolder(rightsHolderSubject);
297
                Checksum checksumObject = new Checksum();
298
                checksumObject.setValue(checksum);
299
                checksumObject.setAlgorithm(checksumAlgorithm);
300
                sysMeta.setChecksum(checksumObject);
301
                if (originMemberNode != null) {
302
	                NodeReference omn = new NodeReference();
303
	                omn.setValue(originMemberNode);
304
	                sysMeta.setOriginMemberNode(omn);
305
                }
306
                if (authoritativeMemberNode != null) {
307
	                NodeReference amn = new NodeReference();
308
	                amn.setValue(authoritativeMemberNode);
309
	                sysMeta.setAuthoritativeMemberNode(amn);
310
                }
311
                sysMeta.setDateSysMetadataModified(dateModified);
312
                Subject submitterSubject = new Subject();
313
                submitterSubject.setValue(submitter);
314
                sysMeta.setSubmitter(submitterSubject);
315
                ObjectFormatIdentifier fmtid = new ObjectFormatIdentifier();
316
                fmtid.setValue(fmtidStr);
317
            	sysMeta.setFormatId(fmtid);
318
                sysMeta.setSize(size);
319
                if (obsoletes != null) {
320
	                Identifier obsoletesId = new Identifier();
321
	                obsoletesId.setValue(obsoletes);
322
	                sysMeta.setObsoletes(obsoletesId);
323
                }
324
                if (obsoletedBy != null) {
325
		            Identifier obsoletedById = new Identifier();
326
		            obsoletedById.setValue(obsoletedBy);
327
		            sysMeta.setObsoletedBy(obsoletedById);
328
                }
329
                stmt.close();
330
            } 
331
            else
332
            {
333
                stmt.close();
334
                DBConnectionPool.returnDBConnection(dbConn, serialNumber);
335
                throw new McdbDocNotFoundException("Could not find " + guid);
336
            }
337
            
338
        } 
339
        catch (SQLException e) 
340
        {
341
            e.printStackTrace();
342
            logMetacat.error("Error while getting system metadata for guid " + guid + " : "  
343
                    + e.getMessage());
344
        } 
345
        finally 
346
        {
347
            // Return database connection to the pool
348
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
349
        }
350

    
351
        // populate the replication policy
352
        ReplicationPolicy replicationPolicy = new ReplicationPolicy();
353
        if ( numberOfReplicas != null  && numberOfReplicas.intValue() != -1 ) {
354
            replicationPolicy.setNumberReplicas(numberOfReplicas.intValue());
355
            
356
        }
357
        
358
        if ( replicationAllowed != null ) {
359
            replicationPolicy.setReplicationAllowed(replicationAllowed);
360
            
361
        }
362
        replicationPolicy.setBlockedMemberNodeList(getReplicationPolicy(guid, "blocked"));
363
        replicationPolicy.setPreferredMemberNodeList(getReplicationPolicy(guid, "preferred"));
364
		    sysMeta.setReplicationPolicy(replicationPolicy);
365
		
366
		    // look up replication status
367
		    sysMeta.setReplicaList(getReplicationStatus(guid));
368
		
369
		    // look up access policy
370
		    try {
371
		    	sysMeta.setAccessPolicy(getAccessPolicy(guid));
372
		    } catch (AccessException e) {
373
		    	throw new McdbDocNotFoundException(e);
374
		    }
375
        
376
        return sysMeta;
377
    }
378
    
379
    
380
    private List<NodeReference> getReplicationPolicy(String guid, String policy)
381
		throws McdbDocNotFoundException {
382
		
383
		List<NodeReference> nodes = new ArrayList<NodeReference>();
384
		String sql = "select guid, policy, member_node " +
385
			"from smReplicationPolicy where guid = ? and policy = ?";
386
	    DBConnection dbConn = null;
387
	    int serialNumber = -1;
388
	    try {
389
	        // Get a database connection from the pool
390
	        dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getReplicationPolicy");
391
	        serialNumber = dbConn.getCheckOutSerialNumber();
392
	
393
	        // Execute the statement
394
	        PreparedStatement stmt = dbConn.prepareStatement(sql);
395
	        stmt.setString(1, guid);
396
	        stmt.setString(2, policy);
397
	        ResultSet rs = stmt.executeQuery();
398
	        while (rs.next()) 
399
	        {
400
	            String memberNode = rs.getString(3);
401
	            NodeReference node = new NodeReference();
402
	            node.setValue(memberNode);
403
	            nodes.add(node);
404
	        
405
	        } 
406
	        stmt.close();
407
	        
408
	    } catch (SQLException e) {
409
	        logMetacat.error("Error while getting system metadata replication policy for guid " + guid, e);
410
	    } 
411
	    finally {
412
	        // Return database connection to the pool
413
	        DBConnectionPool.returnDBConnection(dbConn, serialNumber);
414
	    }
415
	    
416
	    return nodes;
417
	}
418
    
419
    private List<Replica> getReplicationStatus(String guid) throws McdbDocNotFoundException {
420
		
421
		List<Replica> replicas = new ArrayList<Replica>();
422
		String sql = "select guid, member_node, status, date_verified " +
423
			"from smReplicationStatus where guid = ?";
424
	    DBConnection dbConn = null;
425
	    int serialNumber = -1;
426
	    try {
427
	        // Get a database connection from the pool
428
	        dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getReplicas");
429
	        serialNumber = dbConn.getCheckOutSerialNumber();
430
	
431
	        // Execute the statement
432
	        PreparedStatement stmt = dbConn.prepareStatement(sql);
433
	        stmt.setString(1, guid);
434
	        ResultSet rs = stmt.executeQuery();
435
	        while (rs.next()) 
436
	        {
437
	            String memberNode = rs.getString(2);
438
	            String status = rs.getString(3);
439
	            java.sql.Date verified = rs.getDate(4);
440
	            
441
	            Replica replica = new Replica();	            
442
	            NodeReference node = new NodeReference();
443
	            node.setValue(memberNode);
444
	            replica.setReplicaMemberNode(node);
445
	            replica.setReplicationStatus(ReplicationStatus.valueOf(status));
446
	            replica.setReplicaVerified(new Date(verified.getTime()));
447
	            replicas.add(replica);
448
	        } 
449
	        stmt.close();
450
	        
451
	    } catch (SQLException e) {
452
	        logMetacat.error("Error while getting system metadata replication policy for guid " + guid, e);
453
	    } 
454
	    finally {
455
	        // Return database connection to the pool
456
	        DBConnectionPool.returnDBConnection(dbConn, serialNumber);
457
	    }
458
	    
459
	    return replicas;
460
	}
461
    
462
    
463
    /**
464
     * return the newest rev for a given localId
465
     * @param localId
466
     * @return
467
     */
468
    public int getLatestRevForLocalId(String localId)
469
        throws McdbDocNotFoundException
470
    {
471
        try
472
        {
473
            AccessionNumber acc = new AccessionNumber(localId, "NONE");
474
            localId = acc.getDocid();
475
        }
476
        catch(Exception e)
477
        {
478
            //do nothing. just try the localId as it is
479
        }
480
        int rev = 0;
481
        String sql = "select rev from xml_documents where docid like ? ";
482
        DBConnection dbConn = null;
483
        int serialNumber = -1;
484
        try 
485
        {
486
            // Get a database connection from the pool
487
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getLatestRevForLocalId");
488
            serialNumber = dbConn.getCheckOutSerialNumber();
489

    
490
            // Execute the insert statement
491
            PreparedStatement stmt = dbConn.prepareStatement(sql);
492
            stmt.setString(1, localId);
493
            ResultSet rs = stmt.executeQuery();
494
            if (rs.next()) 
495
            {
496
                rev = rs.getInt(1);
497
                stmt.close();
498
            } 
499
            else
500
            {
501
                stmt.close();
502
                DBConnectionPool.returnDBConnection(dbConn, serialNumber);
503
                throw new McdbDocNotFoundException("While trying to get the latest rev, could not find document " + localId);
504
            }
505
        } 
506
        catch (SQLException e) 
507
        {
508
            logMetacat.error("Error while looking up the guid: " 
509
                    + e.getMessage());
510
        } 
511
        finally 
512
        {
513
            // Return database connection to the pool
514
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
515
        }
516
        return rev;
517
    }
518
    
519
    /**
520
     * return all local ids in the object store that do not have associated
521
     * system metadata
522
     */
523
    public List<String> getLocalIdsWithNoSystemMetadata(boolean includeRevisions, int serverLocation)
524
    {
525
        Vector<String> ids = new Vector<String>();
526
        String sql = "select docid, rev from xml_documents " +
527
        		"where docid not in " +
528
        		"(select docid from identifier where guid in (select guid from systemmetadata))";
529
        if (serverLocation > 0) {
530
        	sql = sql + " and server_location = ? ";
531
        }
532
        
533
        String revisionSql = "select docid, rev from xml_revisions " +
534
				"where docid not in " +
535
				"(select docid from identifier where guid in (select guid from systemmetadata))";
536
        if (serverLocation > 0) {
537
        	revisionSql = revisionSql + " and server_location = ? ";
538
        }
539
        
540
        if (includeRevisions) {
541
        	sql = sql + " UNION ALL " + revisionSql;
542
        }
543
        
544
        DBConnection dbConn = null;
545
        int serialNumber = -1;
546
        try 
547
        {
548
            // Get a database connection from the pool
549
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getLocalIdsWithNoSystemMetadata");
550
            serialNumber = dbConn.getCheckOutSerialNumber();
551

    
552
            // Execute the insert statement
553
            PreparedStatement stmt = dbConn.prepareStatement(sql);
554
            // set params based on what we have in the query string
555
            if (serverLocation > 0) {
556
            	stmt.setInt(1, serverLocation);
557
            	if (includeRevisions) {
558
            		stmt.setInt(2, serverLocation);
559
            	}
560
            }
561
            ResultSet rs = stmt.executeQuery();
562
            while (rs.next()) 
563
            {
564
                String localid = rs.getString(1);
565
                String rev = rs.getString(2);
566
                localid += "." + rev;
567
                logMetacat.debug("id to add SM for: " + localid);
568
                ids.add(localid);
569
            } 
570
            stmt.close();
571
        } 
572
        catch (SQLException e) 
573
        {
574
            logMetacat.error("Error while looking up the guid: " 
575
                    + e.getMessage());
576
        } 
577
        finally 
578
        {
579
            // Return database connection to the pool
580
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
581
        }
582
        
583
        return ids;
584
    }
585
    
586
    /**
587
     * return a listing of all local ids in the object store
588
     * @return a list of all local ids in metacat
589
     */
590
    public List<String> getAllLocalIds()
591
    // seems to be an unnecessary and restrictive throw -rnahf 13-Sep-2011
592
    //    throws Exception
593
    {
594
        Vector<String> ids = new Vector<String>();
595
        String sql = "select docid from xml_documents";
596
        DBConnection dbConn = null;
597
        int serialNumber = -1;
598
        try 
599
        {
600
            // Get a database connection from the pool
601
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getAllLocalIds");
602
            serialNumber = dbConn.getCheckOutSerialNumber();
603

    
604
            // Execute the insert statement
605
            PreparedStatement stmt = dbConn.prepareStatement(sql);
606
            ResultSet rs = stmt.executeQuery();
607
            while (rs.next()) 
608
            {
609
                String localid = rs.getString(1);
610
                ids.add(localid);
611
            } 
612
            stmt.close();
613
        } 
614
        catch (SQLException e) 
615
        {
616
            logMetacat.error("Error while looking up the guid: " 
617
                    + e.getMessage());
618
        } 
619
        finally 
620
        {
621
            // Return database connection to the pool
622
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
623
        }
624
        return ids;
625
    }
626
    
627
    
628
    /**
629
     * return a listing of all guids in the object store
630
     * @return a list of all GUIDs in metacat
631
     */
632
    public List<String> getAllSystemMetadataGUIDs()
633
    {
634
        Vector<String> guids = new Vector<String>();
635
        String sql = "select guid from systemmetadata";
636
        DBConnection dbConn = null;
637
        int serialNumber = -1;
638
        try 
639
        {
640
            // Get a database connection from the pool
641
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getAllGUIDs");
642
            serialNumber = dbConn.getCheckOutSerialNumber();
643

    
644
            // Execute the insert statement
645
            PreparedStatement stmt = dbConn.prepareStatement(sql);
646
            ResultSet rs = stmt.executeQuery();
647
            while (rs.next()) 
648
            {
649
                String guid = rs.getString(1);
650
                guids.add(guid);
651
            } 
652
            stmt.close();
653
        } 
654
        catch (SQLException e) 
655
        {
656
            logMetacat.error("Error while retrieving the guid: " 
657
                    + e.getMessage());
658
        } 
659
        finally 
660
        {
661
            // Return database connection to the pool
662
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
663
        }
664
        return guids;
665
    }
666
    
667
    
668
    
669
    /**
670
     * returns a list of system metadata-only guids since the given date
671
     * @return a list of system ids in metacat that do not correspond to objects
672
     * TODO: need to check which server they are on
673
     */
674
    public List<String> getUpdatedSystemMetadataIds(Date since)
675
       throws Exception
676
    {
677
        List<String> ids = new Vector<String>();
678
        String sql = 
679
        	"select guid from " + TYPE_SYSTEM_METADATA +
680
        	" where guid not in " +
681
        	" (select guid from " + TYPE_IDENTIFIER + ") " +
682
        	" and date_modified > ?";
683
        DBConnection dbConn = null;
684
        int serialNumber = -1;
685
        try 
686
        {
687
            // Get a database connection from the pool
688
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getUpdatedSystemMetadataIds");
689
            serialNumber = dbConn.getCheckOutSerialNumber();
690

    
691
            // Execute the insert statement
692
            PreparedStatement stmt = dbConn.prepareStatement(sql);
693
            stmt.setDate(1, new java.sql.Date(since.getTime()));
694
            ResultSet rs = stmt.executeQuery();
695
            while (rs.next()) 
696
            {
697
                String guid = rs.getString(1);
698
                ids.add(guid);
699
            } 
700
            stmt.close();
701
        } 
702
        catch (SQLException e) 
703
        {
704
            logMetacat.error("Error while looking up the updated guids: " 
705
                    + e.getMessage());
706
        } 
707
        finally 
708
        {
709
            // Return database connection to the pool
710
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
711
        }
712
        return ids;
713
    }
714
    
715
    /**
716
     * returns a list of system metadata-only guids since the given date
717
     * @return a list of system ids in metacat that do not correspond to objects
718
     * TODO: need to check which server they are on
719
     */
720
    public Date getLastModifiedDate() throws Exception {
721
        Date maxDate = null;
722

    
723
        List<String> ids = new Vector<String>();
724
        String sql = 
725
        	"select max(date_modified) from " + TYPE_SYSTEM_METADATA;
726
        DBConnection dbConn = null;
727
        int serialNumber = -1;
728
        try 
729
        {
730
            // Get a database connection from the pool
731
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getLastModifiedDate");
732
            serialNumber = dbConn.getCheckOutSerialNumber();
733

    
734
            // Execute the insert statement
735
            PreparedStatement stmt = dbConn.prepareStatement(sql);
736
            ResultSet rs = stmt.executeQuery();
737
            if (rs.next()) {
738
            	maxDate = rs.getDate(1);
739
            } 
740
            stmt.close();
741
        } 
742
        catch (SQLException e) 
743
        {
744
            logMetacat.error("Error while looking up the latest update date: " 
745
                    + e.getMessage());
746
        } 
747
        finally 
748
        {
749
            // Return database connection to the pool
750
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
751
        }
752
        return maxDate;
753
    }
754

    
755
    
756
    /**
757
     * Determine if an identifier exists already, returning true if so.
758
     * NOTE: looks in the identifier and system metadata table for a match
759
     * (in that order)
760
     * 
761
     * @param guid the global identifier to look up
762
     * @return boolean true if the identifier exists
763
     */
764
    public boolean identifierExists(String guid)
765
    {
766
        boolean idExists = false;
767
        try {
768
            String id = getLocalId(guid);
769
            if (id != null) {
770
                idExists = true;
771
            }
772
        } catch (McdbDocNotFoundException e) {
773
        	// try system metadata only
774
        	try {
775
        		idExists = systemMetadataExists(guid);
776
            } catch (Exception e2) {
777
            	idExists = false;
778
            }
779
        }
780
        return idExists;
781
    }
782
    
783
    /**
784
     * Determine if an identifier mapping exists already, 
785
     * returning true if so.
786
     * 
787
     * @param guid the global identifier to look up
788
     * @return boolean true if the identifier exists
789
     */
790
    public boolean mappingExists(String guid)
791
    {
792
        boolean idExists = false;
793
        try {
794
            String id = getLocalId(guid);
795
            if (id != null) {
796
                idExists = true;
797
            }
798
        } catch (McdbDocNotFoundException e) {
799
        	// nope!
800
        }
801
        return idExists;
802
    }
803
    
804
    /**
805
     * 
806
     * @param guid
807
     * @param rev
808
     * @return
809
     */
810
    public String generateLocalId(String guid, int rev)
811
    {
812
        return generateLocalId(guid, rev, false);
813
    }
814

    
815
    /**
816
     * Given a global identifier (guid), create a suitable local identifier that
817
     * follows Metacat's docid semantics and format (scope.id.rev), and create
818
     * a mapping between these two identifiers.  This effectively reserves both
819
     * the global and the local identifier, as they will now be present in the
820
     * identifier mapping table.  
821
     * 
822
     * REMOVED feature: If the incoming guid has the syntax of a
823
     * Metacat docid (scope.id.rev), then simply use it.
824
     * WHY: because "test.1.001" becomes "test.1.1" which is not correct for DataONE
825
     * identifier use (those revision numbers are just chartacters and should not be interpreted)
826
     * 
827
     * @param guid the global string identifier
828
     * @param rev the revision number to be used in the localId
829
     * @return String containing the localId to be used for Metacat operations
830
     */
831
    public String generateLocalId(String guid, int rev, boolean isSystemMetadata) 
832
    {
833
        String localId = "";
834
        boolean conformsToDocidFormat = false;
835
        
836
        // BRL -- do not allow Metacat-conforming IDs to be used:
837
        // test.1.001 becomes test.1.1 which is NOT correct for DataONE identifiers
838
        // Check if the guid passed in is already in docid (scope.id.rev) format
839
//        try {
840
//            AccessionNumber acc = new AccessionNumber(guid, "NONE");
841
//            if (new Integer(acc.getRev()).intValue() > 0) {
842
//                conformsToDocidFormat = true;
843
//            }
844
//        } catch (NumberFormatException e) {
845
//            // No action needed, simply detecting invalid AccessionNumbers
846
//        } catch (AccessionNumberException e) {
847
//            // No action needed, simply detecting invalid AccessionNumbers
848
//        } catch (SQLException e) {
849
//            // No action needed, simply detecting invalid AccessionNumbers
850
//        }
851
        
852
        if (conformsToDocidFormat) {
853
            // if it conforms, use it for both guid and localId
854
            localId = guid;
855
        } else {
856
            // if not, then generate a new unique localId
857
            localId = DocumentUtil.generateDocumentId(rev);
858
        }
859
        
860
        // Register this new pair in the identifier mapping table
861
        logMetacat.debug("creating mapping in generateLocalId");
862
        if(!isSystemMetadata)
863
        { //don't do this if we're generating for system metadata
864
            createMapping(guid, localId);
865
        }
866
        
867
        return localId;
868
    }
869
    
870
    /**
871
     * given a local identifer, look up the guid.  Throw McdbDocNotFoundException
872
     * if the docid, rev is not found in the identifiers or systemmetadata tables
873
     *
874
     * @param docid the docid to look up
875
     * @param rev the revision of the docid to look up
876
     * @return String containing the mapped guid
877
     * @throws McdbDocNotFoundException if the docid, rev is not found
878
     */
879
    public String getGUID(String docid, int rev)
880
      throws McdbDocNotFoundException
881
    {
882
        logMetacat.debug("getting guid for " + docid);
883
        String query = "select guid from identifier where docid = ? and rev = ?";
884
        String guid = null;
885
        
886
        DBConnection dbConn = null;
887
        int serialNumber = -1;
888
        try {
889
            // Get a database connection from the pool
890
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getGUID");
891
            serialNumber = dbConn.getCheckOutSerialNumber();
892
            
893
            // Execute the insert statement
894
            PreparedStatement stmt = dbConn.prepareStatement(query);
895
            stmt.setString(1, docid);
896
            stmt.setInt(2, rev);
897
            ResultSet rs = stmt.executeQuery();
898
            if (rs.next()) 
899
            {
900
                guid = rs.getString(1);
901
            } 
902
            else
903
            {
904
            	throw new McdbDocNotFoundException("No guid registered for docid " + docid + "." + rev);
905
            }
906
            
907
        } catch (SQLException e) {
908
            logMetacat.error("Error while looking up the guid: " 
909
                    + e.getMessage());
910
        } finally {
911
            // Return database connection to the pool
912
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
913
        }
914
        
915
        return guid;
916
    }
917
    
918
    public boolean systemMetadataExists(String guid) {
919
		logMetacat.debug("looking up system metadata for guid " + guid);
920
		boolean exists = false;
921
		String query = "select guid from systemmetadata where guid = ?";
922

    
923
		DBConnection dbConn = null;
924
		int serialNumber = -1;
925
		try {
926
			// Get a database connection from the pool
927
			dbConn = DBConnectionPool.getDBConnection("IdentifierManager.systemMetadataExisits");
928
			serialNumber = dbConn.getCheckOutSerialNumber();
929

    
930
			// Execute the insert statement
931
			PreparedStatement stmt = dbConn.prepareStatement(query);
932
			stmt.setString(1, guid);
933
			ResultSet rs = stmt.executeQuery();
934
			if (rs.next()) {
935
				exists = true;
936
			}
937

    
938
		} catch (SQLException e) {
939
			logMetacat.error("Error while looking up the system metadata: "
940
					+ e.getMessage());
941
		} finally {
942
			// Return database connection to the pool
943
			DBConnectionPool.returnDBConnection(dbConn, serialNumber);
944
		}
945

    
946
		return exists;
947
	}
948
    
949
    /**
950
     * creates a system metadata mapping and adds additional fields from sysmeta
951
     * to the table for quick searching.
952
     * 
953
     * @param guid the id to insert
954
     * @param localId the systemMetadata object to get the local id for
955
     * @throws McdbDocNotFoundException 
956
     * @throws SQLException 
957
     * @throws InvalidSystemMetadata 
958
     */
959
    public void insertOrUpdateSystemMetadata(SystemMetadata sysmeta) 
960
        throws McdbDocNotFoundException, SQLException, InvalidSystemMetadata {
961
    	String guid = sysmeta.getIdentifier().getValue();
962
    	
963
    	 // Get a database connection from the pool
964
        DBConnection dbConn = DBConnectionPool.getDBConnection("IdentifierManager.insertSystemMetadata");
965
        int serialNumber = dbConn.getCheckOutSerialNumber();
966
        
967
        try {
968
        	// use a single transaction for it all
969
        	dbConn.setAutoCommit(false);
970
        	
971
	    	// insert the record if needed
972
        	if (!IdentifierManager.getInstance().systemMetadataExists(guid)) {
973
    	        insertSystemMetadata(guid, dbConn);
974
			}
975
	        // update with the values
976
	        updateSystemMetadata(sysmeta, dbConn);
977
	        
978
	        // commit if we got here with no errors
979
	        dbConn.commit();
980
        } catch (Exception e) {
981
            e.printStackTrace();
982
            logMetacat.error("Error while creating " + TYPE_SYSTEM_METADATA + " record: " + guid, e );
983
            dbConn.rollback();
984
        } finally {
985
            // Return database connection to the pool
986
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
987
        }
988
        
989
        
990
    }
991
        
992
    
993
    /**
994
     * update a mapping
995
     * @param guid
996
     * @param localId
997
     */
998
    public void updateMapping(String guid, String localId)
999
    {
1000
    	
1001
        logMetacat.debug("$$$$$$$$$$$$$$ updating mapping table");
1002
        int serialNumber = -1;
1003
        DBConnection dbConn = null;
1004
        try {
1005
            // Parse the localId into scope and rev parts
1006
            AccessionNumber acc = new AccessionNumber(localId, "NOACTION");
1007
            String docid = acc.getDocid();
1008
            int rev = 1;
1009
            if(acc.getRev() != null)
1010
            {
1011
              rev = (new Integer(acc.getRev()).intValue());
1012
            }
1013

    
1014
            // Get a database connection from the pool
1015
            dbConn = 
1016
                DBConnectionPool.getDBConnection("IdentifierManager.updateMapping");
1017
            serialNumber = dbConn.getCheckOutSerialNumber();
1018

    
1019
            // Execute the update statement
1020
            String query = "update " + TYPE_IDENTIFIER + " set (docid, rev) = (?, ?) where guid = ?";
1021
            PreparedStatement stmt = dbConn.prepareStatement(query);
1022
            stmt.setString(1, docid);
1023
            stmt.setInt(2, rev);
1024
            stmt.setString(3, guid);
1025
            int rows = stmt.executeUpdate();
1026

    
1027
            stmt.close();
1028
        } catch (SQLException e) {
1029
            e.printStackTrace();
1030
            logMetacat.error("SQL error while updating a mapping identifier: " 
1031
                    + e.getMessage());
1032
        } catch (NumberFormatException e) {
1033
            e.printStackTrace();
1034
            logMetacat.error("NumberFormat error while updating a mapping identifier: " 
1035
                    + e.getMessage());
1036
        } catch (AccessionNumberException e) {
1037
            e.printStackTrace();
1038
            logMetacat.error("AccessionNumber error while updating a mapping identifier: " 
1039
                    + e.getMessage());
1040
        } finally {
1041
            // Return database connection to the pool
1042
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1043
        }
1044
        logMetacat.debug("done updating mapping");
1045
    }
1046
        
1047
    private void updateSystemMetadataFields(long dateUploaded, String rightsHolder,
1048
        String checksum, String checksumAlgorithm, String originMemberNode, 
1049
        String authoritativeMemberNode, long modifiedDate, String submitter, 
1050
        String guid, String objectFormat, BigInteger size, boolean archived,
1051
        boolean replicationAllowed, int numberReplicas, String obsoletes,
1052
        String obsoletedBy, BigInteger serialVersion, DBConnection dbConn) throws SQLException  {
1053
  
1054
        // Execute the insert statement
1055
        String query = "update " + TYPE_SYSTEM_METADATA + 
1056
            " set (date_uploaded, rights_holder, checksum, checksum_algorithm, " +
1057
            "origin_member_node, authoritive_member_node, date_modified, " +
1058
            "submitter, object_format, size, archived, replication_allowed, number_replicas, " +
1059
            "obsoletes, obsoleted_by, serial_version) " +
1060
            "= (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) where guid = ?";
1061
        PreparedStatement stmt = dbConn.prepareStatement(query);
1062
        
1063
        //data values
1064
        stmt.setTimestamp(1, new java.sql.Timestamp(dateUploaded));
1065
        stmt.setString(2, rightsHolder);
1066
        stmt.setString(3, checksum);
1067
        stmt.setString(4, checksumAlgorithm);
1068
        stmt.setString(5, originMemberNode);
1069
        stmt.setString(6, authoritativeMemberNode);
1070
        stmt.setTimestamp(7, new java.sql.Timestamp(modifiedDate));
1071
        stmt.setString(8, submitter);
1072
        stmt.setString(9, objectFormat);
1073
        stmt.setString(10, size.toString());
1074
        stmt.setBoolean(11, archived);
1075
        stmt.setBoolean(12, replicationAllowed);
1076
        stmt.setInt(13, numberReplicas);
1077
        stmt.setString(14, obsoletes);
1078
        stmt.setString(15, obsoletedBy);
1079
        stmt.setString(16, serialVersion.toString());
1080

    
1081
        //where clause
1082
        stmt.setString(17, guid);
1083
        logMetacat.debug("stmt: " + stmt.toString());
1084
        //execute
1085
        int rows = stmt.executeUpdate();
1086

    
1087
        stmt.close();
1088
               
1089
    }
1090
    
1091
    private void insertReplicationPolicy(String guid, String policy, List<String> memberNodes, DBConnection dbConn) throws SQLException
1092
    {
1093
           
1094
        // remove existing values first
1095
        String delete = "delete from smReplicationPolicy " + 
1096
        "where guid = ? and policy = ?";
1097
        PreparedStatement stmt = dbConn.prepareStatement(delete);
1098
        //data values
1099
        stmt.setString(1, guid);
1100
        stmt.setString(2, policy);
1101
        //execute
1102
        int deletedCount = stmt.executeUpdate();
1103
        stmt.close();
1104
        
1105
        for (String memberNode: memberNodes) {
1106
            // Execute the insert statement
1107
            String insert = "insert into smReplicationPolicy " + 
1108
                "(guid, policy, member_node) " +
1109
                "values (?, ?, ?)";
1110
            PreparedStatement insertStatement = dbConn.prepareStatement(insert);
1111
            
1112
            //data values
1113
            insertStatement.setString(1, guid);
1114
            insertStatement.setString(2, policy);
1115
            insertStatement.setString(3, memberNode);
1116
            
1117
            logMetacat.debug("smReplicationPolicy sql: " + insertStatement.toString());
1118

    
1119
            //execute
1120
            int rows = insertStatement.executeUpdate();
1121
            insertStatement.close();
1122
        }
1123
        
1124
    }
1125
    
1126
    private void insertReplicationStatus(String guid, List<Replica> replicas, DBConnection dbConn) throws SQLException {
1127
       
1128
        // remove existing values first
1129
        String delete = "delete from smReplicationStatus " + 
1130
        "where guid = ?";
1131
        PreparedStatement stmt = dbConn.prepareStatement(delete);
1132
        //data values
1133
        stmt.setString(1, guid);
1134
        //execute
1135
        int deletedCount = stmt.executeUpdate();
1136
        stmt.close();
1137
        
1138
        if (replicas != null) {
1139
            for (Replica replica: replicas) {
1140
	            // Execute the insert statement
1141
	            String insert = "insert into smReplicationStatus " + 
1142
	                "(guid, member_node, status, date_verified) " +
1143
	                "values (?, ?, ?, ?)";
1144
	            PreparedStatement insertStatement = dbConn.prepareStatement(insert);
1145
	            
1146
	            //data values
1147
	            String memberNode = replica.getReplicaMemberNode().getValue();
1148
	            String status = replica.getReplicationStatus().toString();
1149
	            java.sql.Date sqlDate = new java.sql.Date(replica.getReplicaVerified().getTime());
1150
	            insertStatement.setString(1, guid);
1151
	            insertStatement.setString(2, memberNode);
1152
	            insertStatement.setString(3, status);
1153
	            insertStatement.setDate(4, sqlDate);
1154

    
1155
	            logMetacat.debug("smReplicationStatus sql: " + insertStatement.toString());
1156
	            
1157
	            //execute
1158
	            int rows = insertStatement.executeUpdate();
1159
	            insertStatement.close();
1160
            }
1161
        }
1162
       
1163
    }
1164
    
1165
    /**
1166
     * Insert the system metadata fields into the db
1167
     * @param sm
1168
     * @throws McdbDocNotFoundException 
1169
     * @throws SQLException 
1170
     * @throws InvalidSystemMetadata 
1171
     * @throws AccessException 
1172
     */
1173
    public void updateSystemMetadata(SystemMetadata sm, DBConnection dbConn) 
1174
      throws McdbDocNotFoundException, SQLException, InvalidSystemMetadata, AccessException {
1175
    	
1176
      Boolean replicationAllowed = false;
1177
		  Integer numberReplicas = -1;
1178
    	ReplicationPolicy replicationPolicy = sm.getReplicationPolicy();
1179
    	if (replicationPolicy != null) {
1180
    		replicationAllowed = replicationPolicy.getReplicationAllowed();
1181
    		numberReplicas = replicationPolicy.getNumberReplicas();
1182
    		replicationAllowed = replicationAllowed == null ? false: replicationAllowed;
1183
    		numberReplicas = numberReplicas == null ? -1: numberReplicas;
1184
    	}
1185
    	
1186
    	// the main systemMetadata fields
1187
		  updateSystemMetadataFields(
1188
				sm.getDateUploaded() == null ? null: sm.getDateUploaded().getTime(),
1189
				sm.getRightsHolder() == null ? null: sm.getRightsHolder().getValue(), 
1190
				sm.getChecksum() == null ? null: sm.getChecksum().getValue(), 
1191
				sm.getChecksum() == null ? null: sm.getChecksum().getAlgorithm(), 
1192
				sm.getOriginMemberNode() == null ? null: sm.getOriginMemberNode().getValue(),
1193
				sm.getAuthoritativeMemberNode() == null ? null: sm.getAuthoritativeMemberNode().getValue(), 
1194
				sm.getDateSysMetadataModified() == null ? null: sm.getDateSysMetadataModified().getTime(),
1195
				sm.getSubmitter() == null ? null: sm.getSubmitter().getValue(), 
1196
		    sm.getIdentifier().getValue(),
1197
		    sm.getFormatId() == null ? null: sm.getFormatId().getValue(),
1198
		    sm.getSize(),
1199
		    sm.getArchived() == null ? false: sm.getArchived(),
1200
		    replicationAllowed, 
1201
		    numberReplicas,
1202
		    sm.getObsoletes() == null ? null:sm.getObsoletes().getValue(),
1203
		    sm.getObsoletedBy() == null ? null: sm.getObsoletedBy().getValue(),
1204
		    sm.getSerialVersion(),
1205
		    dbConn
1206
        );
1207
        
1208
        String guid = sm.getIdentifier().getValue();
1209
        
1210
        // save replication policies
1211
        if (replicationPolicy != null) {
1212
		    List<String> nodes = null;
1213
		    String policy = null;
1214
		    
1215
		    // check for null 
1216
		    if (replicationPolicy.getBlockedMemberNodeList() != null) {
1217
			    nodes = new ArrayList<String>();
1218
			    policy = "blocked";
1219
			    for (NodeReference node: replicationPolicy.getBlockedMemberNodeList()) {
1220
			    	nodes.add(node.getValue());
1221
			    }
1222
			    this.insertReplicationPolicy(guid, policy, nodes, dbConn);
1223
		    }
1224
		    
1225
		    if (replicationPolicy.getPreferredMemberNodeList() != null) {
1226
			    nodes = new ArrayList<String>();
1227
			    policy = "preferred";
1228
			    for (NodeReference node: replicationPolicy.getPreferredMemberNodeList()) {
1229
			    	nodes.add(node.getValue());
1230
			    }
1231
		        this.insertReplicationPolicy(guid, policy, nodes, dbConn);
1232
		    }
1233
        }
1234
        
1235
        // save replica information
1236
        this.insertReplicationStatus(guid, sm.getReplicaList(), dbConn);
1237
        
1238
        // save access policy
1239
        AccessPolicy accessPolicy = sm.getAccessPolicy();
1240
        if (accessPolicy != null) {
1241
			this.insertAccessPolicy(guid, accessPolicy);
1242
        }
1243
    }
1244
    
1245
    /**
1246
     * Creates Metacat access rules and inserts them
1247
     * @param accessPolicy
1248
     * @throws McdbDocNotFoundException
1249
     * @throws AccessException
1250
     */
1251
    private void insertAccessPolicy(String guid, AccessPolicy accessPolicy) throws McdbDocNotFoundException, AccessException {
1252
    	
1253
    	// check for the existing permOrder so that we remain compatible with it (DataONE does not care)
1254
        XMLAccessAccess accessController  = new XMLAccessAccess();
1255
		String existingPermOrder = AccessControlInterface.ALLOWFIRST;
1256
        Vector<XMLAccessDAO> existingAccess = accessController.getXMLAccessForDoc(guid);
1257
        if (existingAccess != null && existingAccess.size() > 0) {
1258
        	existingPermOrder = existingAccess.get(0).getPermOrder();
1259
        }
1260
        
1261
    	List<XMLAccessDAO> accessDAOs = new ArrayList<XMLAccessDAO>();
1262
        for (AccessRule accessRule: accessPolicy.getAllowList()) {
1263
        	List<Subject> subjects = accessRule.getSubjectList();
1264
        	List<Permission> permissions = accessRule.getPermissionList();
1265
        	for (Subject subject: subjects) {
1266
    			XMLAccessDAO accessDAO = new XMLAccessDAO();
1267
        		accessDAO.setPrincipalName(subject.getValue());
1268
    			accessDAO.setGuid(guid);
1269
    			accessDAO.setPermType(AccessControlInterface.ALLOW);
1270
				accessDAO.setPermOrder(existingPermOrder);
1271
    			if (permissions != null) {
1272
	    			for (Permission permission: permissions) {
1273
	    				Long metacatPermission = new Long(convertPermission(permission));
1274
	        			accessDAO.addPermission(metacatPermission);
1275
	    			}
1276
    			}
1277
    			accessDAOs.add(accessDAO);
1278
        	}
1279
        }
1280
        
1281
        
1282
        // remove all existing allow records
1283
        accessController.deleteXMLAccessForDoc(guid, AccessControlInterface.ALLOW);
1284
        // add the ones we can for this guid
1285
        accessController.insertAccess(guid, accessDAOs);
1286
        
1287
        
1288
    }
1289
    
1290
    /**
1291
     * Lookup access policy from Metacat
1292
     * @param guid
1293
     * @return
1294
     * @throws McdbDocNotFoundException
1295
     * @throws AccessException
1296
     */
1297
    public AccessPolicy getAccessPolicy(String guid) throws McdbDocNotFoundException, AccessException {
1298
        AccessPolicy accessPolicy = new AccessPolicy();
1299

    
1300
    	// use GUID to look up the access
1301
        XMLAccessAccess accessController  = new XMLAccessAccess();
1302
        List<XMLAccessDAO> accessDAOs = accessController.getXMLAccessForDoc(guid);
1303
        
1304
        for (XMLAccessDAO accessDAO: accessDAOs) {
1305
        	// only add allow rule
1306
        	if (accessDAO.getPermType().equals(AccessControlInterface.ALLOW)) {
1307
	        	AccessRule accessRule = new AccessRule();    	
1308
	        	List <Permission> permissions = convertPermission(accessDAO.getPermission().intValue());
1309
	        	// cannot include if we have no permissions
1310
	        	if (permissions == null || permissions.isEmpty()) {
1311
	        		logMetacat.warn("skipping empty access rule permissions for " + guid);
1312
	        		continue;
1313
	        	}
1314
	        	accessRule.setPermissionList(permissions);
1315
	        	Subject subject = new Subject();
1316
	        	subject.setValue(accessDAO.getPrincipalName());
1317
	        	accessRule.addSubject(subject);
1318
	            accessPolicy.addAllow(accessRule);
1319
        	}
1320
        }
1321
        return accessPolicy;
1322
    }
1323
    
1324
    public int convertPermission(Permission permission) {
1325
    	if (permission.equals(Permission.READ)) {
1326
    		return AccessControlInterface.READ;
1327
    	}
1328
    	if (permission.equals(Permission.WRITE)) {
1329
    		return AccessControlInterface.WRITE;
1330
    	}
1331
    	if (permission.equals(Permission.CHANGE_PERMISSION)) {
1332
    		return AccessControlInterface.CHMOD;
1333
    	}
1334
		return -1;
1335
    }
1336
    
1337
    public List<Permission> convertPermission(int permission) {
1338
    	
1339
    	List<Permission> permissions = new ArrayList<Permission>();
1340
    	if (permission == AccessControlInterface.ALL) {
1341
    		permissions.add(Permission.READ);
1342
    		permissions.add(Permission.WRITE);
1343
    		permissions.add(Permission.CHANGE_PERMISSION);
1344
    		return permissions;
1345
    	}
1346
    	
1347
    	if ((permission & AccessControlInterface.CHMOD) == AccessControlInterface.CHMOD) {
1348
    		permissions.add(Permission.CHANGE_PERMISSION);
1349
    	}
1350
    	if ((permission & AccessControlInterface.READ) == AccessControlInterface.READ) {
1351
    		permissions.add(Permission.READ);
1352
    	}
1353
    	if ((permission & AccessControlInterface.WRITE) == AccessControlInterface.WRITE) {
1354
    		permissions.add(Permission.WRITE);
1355
    	}
1356
    	
1357
		return permissions;
1358
    }
1359
    
1360
    /**
1361
     * Lookup a localId given the GUID. If
1362
     * the identifier is not found, throw an exception.
1363
     * 
1364
     * @param guid the global identifier to look up
1365
     * @return String containing the corresponding LocalId
1366
     * @throws McdbDocNotFoundException if the identifier is not found
1367
     */
1368
    public String getLocalId(String guid) throws McdbDocNotFoundException {
1369
      
1370
      String db_guid = "";
1371
      String docid = "";
1372
      int rev = 0;
1373
      
1374
      String query = "select guid, docid, rev from " + TYPE_IDENTIFIER + " where guid = ?";
1375
      
1376
      DBConnection dbConn = null;
1377
      int serialNumber = -1;
1378
      try {
1379
          // Get a database connection from the pool
1380
          dbConn = DBConnectionPool.getDBConnection("Identifier.getLocalId");
1381
          serialNumber = dbConn.getCheckOutSerialNumber();
1382
          
1383
          // Execute the insert statement
1384
          PreparedStatement stmt = dbConn.prepareStatement(query);
1385
          stmt.setString(1, guid);
1386
          ResultSet rs = stmt.executeQuery();
1387
          if (rs.next()) {
1388
              db_guid = rs.getString(1);
1389
              docid = rs.getString(2);
1390
              rev = rs.getInt(3);
1391
              assert(db_guid.equals(guid));
1392
          } else {
1393
              throw new McdbDocNotFoundException("Document not found:" + guid);
1394
          }
1395
          stmt.close();
1396
      } catch (SQLException e) {
1397
          logMetacat.error("Error while looking up the local identifier: " 
1398
                  + e.getMessage());
1399
      } finally {
1400
          // Return database connection to the pool
1401
          DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1402
      }
1403
      return docid + "." + rev;
1404
    }
1405
    
1406
    /**
1407
     * query the systemmetadata table based on the given parameters
1408
     * @param startTime
1409
     * @param endTime
1410
     * @param objectFormat
1411
     * @param replicaStatus
1412
     * @param start
1413
     * @param count
1414
     * @return ObjectList
1415
     * @throws SQLException 
1416
     * @throws ServiceException 
1417
     * @throws PropertyNotFoundException 
1418
     */
1419
    public ObjectList querySystemMetadata(Date startTime, Date endTime,
1420
        ObjectFormatIdentifier objectFormatId, boolean replicaStatus,
1421
        int start, int count) 
1422
        throws SQLException, PropertyNotFoundException, ServiceException {
1423
        ObjectList ol = new ObjectList();
1424
        int total = 0;
1425
        DBConnection dbConn = null;
1426
        int serialNumber = -1;
1427

    
1428
        try {
1429
            String sql = "select guid, date_uploaded, rights_holder, checksum, "
1430
                    + "checksum_algorithm, origin_member_node, authoritive_member_node, "
1431
                    + "date_modified, submitter, object_format, size from systemmetadata";
1432

    
1433
            boolean f1 = false;
1434
            boolean f2 = false;
1435
            boolean f3 = false;
1436

    
1437
            if (startTime != null) {
1438
                sql += " where systemmetadata.date_modified >= ?";
1439
                f1 = true;
1440
            }
1441

    
1442
            if (endTime != null) {
1443
                if (!f1) {
1444
                    sql += " where systemmetadata.date_modified < ?";
1445
                } else {
1446
                    sql += " and systemmetadata.date_modified < ?";
1447
                }
1448
                f2 = true;
1449
            }
1450

    
1451
            if (objectFormatId != null) {
1452
                if (!f1 && !f2) {
1453
                    sql += " where object_format = ?";
1454
                } else {
1455
                    sql += " and object_format = ?";
1456
                }
1457
                f3 = true;
1458
            }
1459

    
1460
            if (replicaStatus) {
1461
                String currentNodeId = PropertyService.getInstance().getProperty("dataone.nodeId");
1462
                if (!f1 && !f2 && !f3) {
1463
                    sql += " where authoritive_member_node != '" +
1464
                        currentNodeId.trim() + "'";
1465
                } else {
1466
                    sql += " and authoritive_member_node != '" +
1467
                        currentNodeId.trim() + "'";
1468
                }
1469
            }
1470

    
1471
            // order the results for slicing ops
1472
            sql += " order by guid ";
1473
            
1474
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.querySystemMetadata");
1475
            serialNumber = dbConn.getCheckOutSerialNumber();
1476
            PreparedStatement stmt = dbConn.prepareStatement(sql);
1477

    
1478
            if (f1 && f2 && f3) {
1479
                stmt.setTimestamp(1, new Timestamp(startTime.getTime()));
1480
                stmt.setTimestamp(2, new Timestamp(endTime.getTime()));
1481
                stmt.setString(3, objectFormatId.getValue());
1482
            } else if (f1 && f2 && !f3) {
1483
                stmt.setTimestamp(1, new Timestamp(startTime.getTime()));
1484
                stmt.setTimestamp(2, new Timestamp(endTime.getTime()));
1485
            } else if (f1 && !f2 && f3) {
1486
                stmt.setTimestamp(1, new Timestamp(startTime.getTime()));
1487
                stmt.setString(2, objectFormatId.getValue());
1488
            } else if (f1 && !f2 && !f3) {
1489
                stmt.setTimestamp(1, new Timestamp(startTime.getTime()));
1490
            } else if (!f1 && f2 && f3) {
1491
                stmt.setTimestamp(1, new Timestamp(endTime.getTime()));
1492
                stmt.setString(2, objectFormatId.getValue());
1493
            } else if (!f1 && !f2 && f3) {
1494
                stmt.setString(1, objectFormatId.getValue());
1495
            } else if (!f1 && f2 && !f3) {
1496
                stmt.setTimestamp(1, new Timestamp(endTime.getTime()));
1497
            }
1498

    
1499
            // logMetacat.debug("LISTOBJECTS QUERY: " + stmt.toString());
1500

    
1501
            ResultSet rs = stmt.executeQuery();
1502
            for (int i = 0; i < start; i++) {
1503
                if (rs.next()) {
1504
                    total++;
1505
                } else {
1506
                    break;
1507
                }
1508
            }
1509

    
1510
            int countIndex = 0;
1511

    
1512
            while (rs.next()) {                
1513
                total++;
1514
                if (countIndex >= count) {
1515
                    // allow unlimited (negative number for count)
1516
                    if (count > 0) {
1517
                        break;
1518
                    }
1519
                }
1520

    
1521
                String guid = rs.getString(1);
1522
                // logMetacat.debug("query found doc with guid " + guid);
1523
                // Timestamp dateUploaded = rs.getTimestamp(2);
1524
                // String rightsHolder = rs.getString(3);
1525
                String checksum = rs.getString(4);
1526
                String checksumAlgorithm = rs.getString(5);
1527
                // String originMemberNode = rs.getString(6);
1528
                // String authoritiveMemberNode = rs.getString(7);
1529
                Timestamp dateModified = rs.getTimestamp(8);
1530
                // String submitter = rs.getString(9);
1531
                String fmtidStr = rs.getString(10);
1532
                String sz = rs.getString(11);
1533
                BigInteger size = new BigInteger("0");
1534

    
1535
                if (sz != null && !sz.trim().equals("")) {
1536
                    size = new BigInteger(rs.getString(11));
1537
                }
1538

    
1539
                ObjectInfo oi = new ObjectInfo();
1540

    
1541
                Identifier id = new Identifier();
1542
                id.setValue(guid);
1543
                oi.setIdentifier(id);
1544

    
1545
                if (dateModified != null) {
1546
                    oi.setDateSysMetadataModified(dateModified);
1547
                }
1548

    
1549
                Checksum cs = new Checksum();
1550
                cs.setValue(checksum);
1551
                try {
1552
                    // cs.setAlgorithm(ChecksumAlgorithm.valueOf(checksumAlgorithm));
1553
                    cs.setAlgorithm(checksumAlgorithm);
1554
                } catch (Exception e) {
1555
                    logMetacat.error("could not parse checksum algorithm", e);
1556
                    continue;
1557
                }
1558
                oi.setChecksum(cs);
1559

    
1560
                // set the format type
1561
                ObjectFormatIdentifier fmtid = new ObjectFormatIdentifier();
1562
                fmtid.setValue(fmtidStr);
1563
                oi.setFormatId(fmtid);
1564

    
1565
                oi.setSize(size);
1566

    
1567
                // when requested count == 0, return an empty object list
1568
                if (count != 0) {
1569
                    ol.addObjectInfo(oi);                    
1570
                }
1571
                countIndex++;
1572
            }
1573

    
1574
            // expend the resultset to get the total count of possible rows
1575
            while (rs.next()) {
1576
                total++;
1577
            }
1578
        }
1579

    
1580
        finally {
1581
            // Return database connection to the pool
1582
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1583
        }
1584

    
1585
        ol.setStart(start);
1586
        ol.setCount(ol.sizeObjectInfoList());
1587
        ol.setTotal(total);
1588

    
1589
        return ol;
1590
    }
1591
    
1592
    /**
1593
     * create a mapping in the identifier table
1594
     * @param guid
1595
     * @param localId
1596
     */
1597
    public void createMapping(String guid, String localId)
1598
    {        
1599
        
1600
        int serialNumber = -1;
1601
        DBConnection dbConn = null;
1602
        try {
1603

    
1604
            // Parse the localId into scope and rev parts
1605
            AccessionNumber acc = new AccessionNumber(localId, "NOACTION");
1606
            String docid = acc.getDocid();
1607
            int rev = 1;
1608
            if (acc.getRev() != null) {
1609
              rev = (new Integer(acc.getRev()).intValue());
1610
            }
1611

    
1612
            // Get a database connection from the pool
1613
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.createMapping");
1614
            serialNumber = dbConn.getCheckOutSerialNumber();
1615

    
1616
            // Execute the insert statement
1617
            String query = "insert into " + TYPE_IDENTIFIER + " (guid, docid, rev) values (?, ?, ?)";
1618
            PreparedStatement stmt = dbConn.prepareStatement(query);
1619
            stmt.setString(1, guid);
1620
            stmt.setString(2, docid);
1621
            stmt.setInt(3, rev);
1622
            logMetacat.debug("mapping query: " + stmt.toString());
1623
            int rows = stmt.executeUpdate();
1624

    
1625
            stmt.close();
1626
        } catch (SQLException e) {
1627
            e.printStackTrace();
1628
            logMetacat.error("createGenericMapping: SQL error while creating a mapping to the " + TYPE_IDENTIFIER + " identifier: " 
1629
                    + e.getMessage());
1630
        } catch (NumberFormatException e) {
1631
            e.printStackTrace();
1632
            logMetacat.error("createGenericMapping: NumberFormat error while creating a mapping to the " + TYPE_IDENTIFIER + " identifier: " 
1633
                    + e.getMessage());
1634
        } catch (AccessionNumberException e) {
1635
            e.printStackTrace();
1636
            logMetacat.error("createGenericMapping: AccessionNumber error while creating a mapping to the " + TYPE_IDENTIFIER + " identifier: " 
1637
                    + e.getMessage());
1638
        } finally {
1639
            // Return database connection to the pool
1640
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1641
        }
1642
    }
1643
    
1644
    /**
1645
     * remove a mapping in the identifier table
1646
     * @param guid
1647
     * @param localId
1648
     */
1649
    public void removeMapping(String guid, String localId)
1650
    {        
1651
        
1652
        int serialNumber = -1;
1653
        DBConnection dbConn = null;
1654
        try {
1655

    
1656
            // Parse the localId into scope and rev parts
1657
            AccessionNumber acc = new AccessionNumber(localId, "NOACTION");
1658
            String docid = acc.getDocid();
1659
            int rev = 1;
1660
            if (acc.getRev() != null) {
1661
              rev = (new Integer(acc.getRev()).intValue());
1662
            }
1663

    
1664
            // Get a database connection from the pool
1665
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.removeMapping");
1666
            serialNumber = dbConn.getCheckOutSerialNumber();
1667

    
1668
            // Execute the insert statement
1669
            String query = "DELETE FROM " + TYPE_IDENTIFIER + " WHERE guid = ? AND docid = ? AND rev = ?";
1670
            PreparedStatement stmt = dbConn.prepareStatement(query);
1671
            stmt.setString(1, guid);
1672
            stmt.setString(2, docid);
1673
            stmt.setInt(3, rev);
1674
            logMetacat.debug("remove mapping query: " + stmt.toString());
1675
            int rows = stmt.executeUpdate();
1676

    
1677
            stmt.close();
1678
        } catch (SQLException e) {
1679
            e.printStackTrace();
1680
            logMetacat.error("removeMapping: SQL error while removing a mapping to the " + TYPE_IDENTIFIER + " identifier: " 
1681
                    + e.getMessage());
1682
        } catch (NumberFormatException e) {
1683
            e.printStackTrace();
1684
            logMetacat.error("removeMapping: NumberFormat error while removing a mapping to the " + TYPE_IDENTIFIER + " identifier: " 
1685
                    + e.getMessage());
1686
        } catch (AccessionNumberException e) {
1687
            e.printStackTrace();
1688
            logMetacat.error("removeMapping: AccessionNumber error while removing a mapping to the " + TYPE_IDENTIFIER + " identifier: " 
1689
                    + e.getMessage());
1690
        } finally {
1691
            // Return database connection to the pool
1692
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1693
        }
1694
    }
1695
    
1696
    /**
1697
     * create the systemmetadata record
1698
     * @param guid
1699
     * @param dbConn 
1700
     * @throws SQLException 
1701
     */
1702
    private void insertSystemMetadata(String guid, DBConnection dbConn) throws SQLException
1703
    {        
1704

    
1705
        // Execute the insert statement
1706
        String query = "insert into " + TYPE_SYSTEM_METADATA + " (guid) values (?)";
1707
        PreparedStatement stmt = dbConn.prepareStatement(query);
1708
        stmt.setString(1, guid);
1709
        logMetacat.debug("system metadata query: " + stmt.toString());
1710
        int rows = stmt.executeUpdate();
1711

    
1712
        stmt.close();
1713
        
1714
    }
1715
    
1716
    public void deleteSystemMetadata(String guid)
1717
    {        
1718
        
1719
        int serialNumber = -1;
1720
        DBConnection dbConn = null;
1721
        String query = null;
1722
        PreparedStatement stmt = null;
1723
        int rows = 0;
1724
        try {
1725

    
1726
            // Get a database connection from the pool
1727
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.deleteSystemMetadata");
1728
            serialNumber = dbConn.getCheckOutSerialNumber();
1729

    
1730
            // remove main system metadata entry
1731
            query = "delete from " + TYPE_SYSTEM_METADATA + " where guid = ? ";
1732
            stmt = dbConn.prepareStatement(query);
1733
            stmt.setString(1, guid);
1734
            logMetacat.debug("delete system metadata: " + stmt.toString());
1735
            rows = stmt.executeUpdate();
1736
            stmt.close();
1737
            
1738
            // remove the smReplicationPolicy
1739
            query = "delete from smReplicationPolicy " + 
1740
            "where guid = ?";
1741
            stmt = dbConn.prepareStatement(query);
1742
            stmt.setString(1, guid);
1743
            logMetacat.debug("delete smReplicationPolicy: " + stmt.toString());
1744
            rows = stmt.executeUpdate();
1745
            stmt.close();
1746
            
1747
            // remove the smReplicationStatus
1748
            query = "delete from smReplicationStatus " + 
1749
            "where guid = ?";
1750
            stmt = dbConn.prepareStatement(query);
1751
            stmt.setString(1, guid);
1752
            logMetacat.debug("delete smReplicationStatus: " + stmt.toString());
1753
            rows = stmt.executeUpdate();
1754
            stmt.close();
1755
            
1756
            // TODO: remove the access?
1757
            // Metacat keeps "deleted" documents so we should not remove access rules.
1758
            
1759
        } catch (Exception e) {
1760
            e.printStackTrace();
1761
            logMetacat.error("Error while deleting " + TYPE_SYSTEM_METADATA + " record: " + guid, e );
1762
            try {
1763
				dbConn.rollback();
1764
			} catch (SQLException sqle) {
1765
	            logMetacat.error("Error while rolling back delete for record: " + guid, sqle );
1766
			}
1767
        } finally {
1768
            // Return database connection to the pool
1769
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1770
        }
1771
    }
1772
    
1773
    public void updateAuthoritativeMemberNodeId(String existingMemberNodeId, String newMemberNodeId)
1774
    {
1775
        DBConnection dbConn = null;
1776
        int serialNumber = -1;
1777
        
1778
        try {
1779
            // Get a database connection from the pool
1780
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.updateAuthoritativeMemberNodeId");
1781
            serialNumber = dbConn.getCheckOutSerialNumber();
1782

    
1783
            // Execute the insert statement
1784
            String query = "update " + TYPE_SYSTEM_METADATA + 
1785
                " set authoritive_member_node = ? " +
1786
                " where authoritive_member_node = ?";
1787
            PreparedStatement stmt = dbConn.prepareStatement(query);
1788
            
1789
            //data values
1790
            stmt.setString(1, newMemberNodeId);
1791
            stmt.setString(2, existingMemberNodeId);
1792

    
1793
            logMetacat.debug("stmt: " + stmt.toString());
1794
            //execute
1795
            int rows = stmt.executeUpdate();
1796

    
1797
            stmt.close();
1798
        } catch (SQLException e) {
1799
            e.printStackTrace();
1800
            logMetacat.error("updateSystemMetadataFields: SQL error while updating system metadata: " 
1801
                    + e.getMessage());
1802
        } catch (NumberFormatException e) {
1803
            e.printStackTrace();
1804
            logMetacat.error("updateSystemMetadataFields: NumberFormat error while updating system metadata: " 
1805
                    + e.getMessage());
1806
        } finally {
1807
            // Return database connection to the pool
1808
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1809
        }
1810
    }
1811
}
1812

    
(37-37/64)