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.exceptions.NotFound;
43
import org.dataone.service.exceptions.NotImplemented;
44
import org.dataone.service.exceptions.ServiceFailure;
45
import org.dataone.service.types.v1.AccessPolicy;
46
import org.dataone.service.types.v1.AccessRule;
47
import org.dataone.service.types.v1.Checksum;
48
import org.dataone.service.types.v1.Identifier;
49
import org.dataone.service.types.v1.NodeReference;
50
import org.dataone.service.types.v1.ObjectFormatIdentifier;
51
import org.dataone.service.types.v1.ObjectInfo;
52
import org.dataone.service.types.v1.ObjectList;
53
import org.dataone.service.types.v1.Permission;
54
import org.dataone.service.types.v1.Replica;
55
import org.dataone.service.types.v1.ReplicationPolicy;
56
import org.dataone.service.types.v1.ReplicationStatus;
57
import org.dataone.service.types.v1.Subject;
58
import org.dataone.service.types.v1.SystemMetadata;
59

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

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

    
91
    /**
92
     * A private constructor that initializes the class when getInstance() is
93
     * called.
94
     */
95
    private IdentifierManager() {}
96

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

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

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

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

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

    
291

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
949
		return exists;
950
	}
951
    
952
    /**
953
     * creates a system metadata mapping and adds additional fields from sysmeta
954
     * to the table for quick searching.
955
     * 
956
     * @param guid the id to insert
957
     * @param localId the systemMetadata object to get the local id for
958
     * @throws McdbDocNotFoundException 
959
     * @throws SQLException 
960
     * @throws InvalidSystemMetadata 
961
     */
962
    public void insertSystemMetadata(SystemMetadata sysmeta) 
963
        throws McdbDocNotFoundException, SQLException, InvalidSystemMetadata {
964
    	String guid = sysmeta.getIdentifier().getValue();
965
    	// insert the record
966
        insertSystemMetadata(guid);
967
        // update with the values
968
        updateSystemMetadata(sysmeta);
969
        
970
    }
971
        
972
    
973
    /**
974
     * update a mapping
975
     * @param guid
976
     * @param localId
977
     */
978
    public void updateMapping(String guid, String localId)
979
    {
980
    	
981
        logMetacat.debug("$$$$$$$$$$$$$$ updating mapping table");
982
        int serialNumber = -1;
983
        DBConnection dbConn = null;
984
        try {
985
            // Parse the localId into scope and rev parts
986
            AccessionNumber acc = new AccessionNumber(localId, "NOACTION");
987
            String docid = acc.getDocid();
988
            int rev = 1;
989
            if(acc.getRev() != null)
990
            {
991
              rev = (new Integer(acc.getRev()).intValue());
992
            }
993

    
994
            // Get a database connection from the pool
995
            dbConn = 
996
                DBConnectionPool.getDBConnection("IdentifierManager.updateMapping");
997
            serialNumber = dbConn.getCheckOutSerialNumber();
998

    
999
            // Execute the update statement
1000
            String query = "update " + TYPE_IDENTIFIER + " set (docid, rev) = (?, ?) where guid = ?";
1001
            PreparedStatement stmt = dbConn.prepareStatement(query);
1002
            stmt.setString(1, docid);
1003
            stmt.setInt(2, rev);
1004
            stmt.setString(3, guid);
1005
            int rows = stmt.executeUpdate();
1006

    
1007
            stmt.close();
1008
        } catch (SQLException e) {
1009
            e.printStackTrace();
1010
            logMetacat.error("SQL error while updating a mapping identifier: " 
1011
                    + e.getMessage());
1012
        } catch (NumberFormatException e) {
1013
            e.printStackTrace();
1014
            logMetacat.error("NumberFormat error while updating a mapping identifier: " 
1015
                    + e.getMessage());
1016
        } catch (AccessionNumberException e) {
1017
            e.printStackTrace();
1018
            logMetacat.error("AccessionNumber error while updating a mapping identifier: " 
1019
                    + e.getMessage());
1020
        } finally {
1021
            // Return database connection to the pool
1022
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1023
        }
1024
        logMetacat.debug("done updating mapping");
1025
    }
1026
        
1027
    private void updateSystemMetadataFields(long dateUploaded, String rightsHolder,
1028
        String checksum, String checksumAlgorithm, String originMemberNode, 
1029
        String authoritativeMemberNode, long modifiedDate, String submitter, 
1030
        String guid, String objectFormat, BigInteger size, boolean archived,
1031
        boolean replicationAllowed, int numberReplicas, String obsoletes,
1032
        String obsoletedBy, BigInteger serialVersion) throws SQLException  {
1033
        
1034
        DBConnection dbConn = null;
1035
        int serialNumber = -1;
1036
        
1037
        try {
1038
            // Get a database connection from the pool
1039
            dbConn = 
1040
                DBConnectionPool.getDBConnection("IdentifierManager.createMapping");
1041
            serialNumber = dbConn.getCheckOutSerialNumber();
1042

    
1043
            // Execute the insert statement
1044
            String query = "update " + TYPE_SYSTEM_METADATA + 
1045
                " set (date_uploaded, rights_holder, checksum, checksum_algorithm, " +
1046
                "origin_member_node, authoritive_member_node, date_modified, " +
1047
                "submitter, object_format, size, archived, replication_allowed, number_replicas, " +
1048
                "obsoletes, obsoleted_by, serial_version) " +
1049
                "= (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) where guid = ?";
1050
            PreparedStatement stmt = dbConn.prepareStatement(query);
1051
            
1052
            //data values
1053
            stmt.setTimestamp(1, new java.sql.Timestamp(dateUploaded));
1054
            stmt.setString(2, rightsHolder);
1055
            stmt.setString(3, checksum);
1056
            stmt.setString(4, checksumAlgorithm);
1057
            stmt.setString(5, originMemberNode);
1058
            stmt.setString(6, authoritativeMemberNode);
1059
            stmt.setTimestamp(7, new java.sql.Timestamp(modifiedDate));
1060
            stmt.setString(8, submitter);
1061
            stmt.setString(9, objectFormat);
1062
            stmt.setString(10, size.toString());
1063
            stmt.setBoolean(11, archived);
1064
            stmt.setBoolean(12, replicationAllowed);
1065
            stmt.setInt(13, numberReplicas);
1066
            stmt.setString(14, obsoletes);
1067
            stmt.setString(15, obsoletedBy);
1068
            stmt.setString(16, serialVersion.toString());
1069

    
1070
            //where clause
1071
            stmt.setString(17, guid);
1072
            logMetacat.debug("stmt: " + stmt.toString());
1073
            //execute
1074
            int rows = stmt.executeUpdate();
1075

    
1076
            stmt.close();
1077
            
1078
        } catch (SQLException e) {
1079
            logMetacat.error("updateSystemMetadataFields: SQL error while updating system metadata: " 
1080
                    + e.getMessage());
1081
            throw e;
1082
            
1083
        } finally {
1084
            // Return database connection to the pool
1085
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1086
        }
1087
    }
1088
    
1089
    private void insertReplicationPolicy(String guid, String policy, List<String> memberNodes)
1090
    {
1091
        DBConnection dbConn = null;
1092
        int serialNumber = -1;
1093
        
1094
        try {
1095
            // Get a database connection from the pool
1096
            dbConn = 
1097
                DBConnectionPool.getDBConnection("IdentifierManager.insertReplicationPolicy");
1098
            serialNumber = dbConn.getCheckOutSerialNumber();
1099

    
1100
            // remove existing values first
1101
            String delete = "delete from systemMetadataReplicationPolicy " + 
1102
            "where guid = ? and policy = ?";
1103
	        PreparedStatement stmt = dbConn.prepareStatement(delete);
1104
	        //data values
1105
	        stmt.setString(1, guid);
1106
	        stmt.setString(2, policy);
1107
	        //execute
1108
	        int deletedCount = stmt.executeUpdate();
1109
	        stmt.close();
1110
            
1111
            for (String memberNode: memberNodes) {
1112
	            // Execute the insert statement
1113
	            String insert = "insert into systemMetadataReplicationPolicy " + 
1114
	                "(guid, policy, member_node) " +
1115
	                "values (?, ?, ?)";
1116
	            PreparedStatement insertStatement = dbConn.prepareStatement(insert);
1117
	            
1118
	            //data values
1119
	            insertStatement.setString(1, guid);
1120
	            insertStatement.setString(2, policy);
1121
	            insertStatement.setString(3, memberNode);
1122
	            
1123
	            logMetacat.debug("systemMetadataReplicationPolicy sql: " + insertStatement.toString());
1124

    
1125
	            //execute
1126
	            int rows = insertStatement.executeUpdate();
1127
	            insertStatement.close();
1128
            }
1129
        } catch (SQLException e) {
1130
            logMetacat.error("SQL error while adding systemMetadataReplicationPolicy for: " + guid, e); 
1131
        } finally {
1132
            // Return database connection to the pool
1133
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1134
        }
1135
    }
1136
    
1137
    private void insertReplicationStatus(String guid, List<Replica> replicas) {
1138
        DBConnection dbConn = null;
1139
        int serialNumber = -1;
1140
        
1141
        try {
1142
            // Get a database connection from the pool
1143
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.insertReplicas");
1144
            serialNumber = dbConn.getCheckOutSerialNumber();
1145

    
1146
            // remove existing values first
1147
            String delete = "delete from systemMetadataReplicationStatus " + 
1148
            "where guid = ?";
1149
	        PreparedStatement stmt = dbConn.prepareStatement(delete);
1150
	        //data values
1151
	        stmt.setString(1, guid);
1152
	        //execute
1153
	        int deletedCount = stmt.executeUpdate();
1154
	        stmt.close();
1155
            
1156
	        if (replicas != null) {
1157
	            for (Replica replica: replicas) {
1158
		            // Execute the insert statement
1159
		            String insert = "insert into systemMetadataReplicationStatus " + 
1160
		                "(guid, member_node, status, date_verified) " +
1161
		                "values (?, ?, ?, ?)";
1162
		            PreparedStatement insertStatement = dbConn.prepareStatement(insert);
1163
		            
1164
		            //data values
1165
		            String memberNode = replica.getReplicaMemberNode().getValue();
1166
		            String status = replica.getReplicationStatus().toString();
1167
		            java.sql.Date sqlDate = new java.sql.Date(replica.getReplicaVerified().getTime());
1168
		            insertStatement.setString(1, guid);
1169
		            insertStatement.setString(2, memberNode);
1170
		            insertStatement.setString(3, status);
1171
		            insertStatement.setDate(4, sqlDate);
1172
	
1173
		            logMetacat.debug("systemMetadataReplicationStatus sql: " + insertStatement.toString());
1174
		            
1175
		            //execute
1176
		            int rows = insertStatement.executeUpdate();
1177
		            insertStatement.close();
1178
	            }
1179
	        }
1180
        } catch (SQLException e) {
1181
            logMetacat.error("SQL error while adding systemMetadataReplicationStatus for: " + guid, e); 
1182
        } finally {
1183
            // Return database connection to the pool
1184
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1185
        }
1186
    }
1187
    
1188
    /**
1189
     * Insert the system metadata fields into the db
1190
     * @param sm
1191
     * @throws McdbDocNotFoundException 
1192
     * @throws SQLException 
1193
     * @throws InvalidSystemMetadata 
1194
     */
1195
    public void updateSystemMetadata(SystemMetadata sm) 
1196
      throws McdbDocNotFoundException, SQLException, InvalidSystemMetadata {
1197
    	
1198
      Boolean replicationAllowed = false;
1199
		  Integer numberReplicas = -1;
1200
    	ReplicationPolicy replicationPolicy = sm.getReplicationPolicy();
1201
    	if (replicationPolicy != null) {
1202
    		replicationAllowed = replicationPolicy.getReplicationAllowed();
1203
    		numberReplicas = replicationPolicy.getNumberReplicas();
1204
    		replicationAllowed = replicationAllowed == null ? false: replicationAllowed;
1205
    		numberReplicas = numberReplicas == null ? -1: numberReplicas;
1206
    	}
1207
    	
1208
    	// the main systemMetadata fields
1209
		  updateSystemMetadataFields(
1210
				sm.getDateUploaded() == null ? null: sm.getDateUploaded().getTime(),
1211
				sm.getRightsHolder() == null ? null: sm.getRightsHolder().getValue(), 
1212
				sm.getChecksum() == null ? null: sm.getChecksum().getValue(), 
1213
				sm.getChecksum() == null ? null: sm.getChecksum().getAlgorithm(), 
1214
				sm.getOriginMemberNode() == null ? null: sm.getOriginMemberNode().getValue(),
1215
				sm.getAuthoritativeMemberNode() == null ? null: sm.getAuthoritativeMemberNode().getValue(), 
1216
				sm.getDateSysMetadataModified() == null ? null: sm.getDateSysMetadataModified().getTime(),
1217
				sm.getSubmitter() == null ? null: sm.getSubmitter().getValue(), 
1218
		    sm.getIdentifier().getValue(),
1219
		    sm.getFormatId() == null ? null: sm.getFormatId().getValue(),
1220
		    sm.getSize(),
1221
		    sm.getArchived() == null ? false: sm.getArchived(),
1222
		    replicationAllowed, 
1223
		    numberReplicas,
1224
		    sm.getObsoletes() == null ? null:sm.getObsoletes().getValue(),
1225
		    sm.getObsoletedBy() == null ? null: sm.getObsoletedBy().getValue(),
1226
		    sm.getSerialVersion()
1227
        );
1228
        
1229
        String guid = sm.getIdentifier().getValue();
1230
        
1231
        // save replication policies
1232
        if (replicationPolicy != null) {
1233
		    List<String> nodes = null;
1234
		    String policy = null;
1235
		    
1236
		    // check for null 
1237
		    if (replicationPolicy.getBlockedMemberNodeList() != null) {
1238
			    nodes = new ArrayList<String>();
1239
			    policy = "blocked";
1240
			    for (NodeReference node: replicationPolicy.getBlockedMemberNodeList()) {
1241
			    	nodes.add(node.getValue());
1242
			    }
1243
			    this.insertReplicationPolicy(guid, policy, nodes);
1244
		    }
1245
		    
1246
		    if (replicationPolicy.getPreferredMemberNodeList() != null) {
1247
			    nodes = new ArrayList<String>();
1248
			    policy = "preferred";
1249
			    for (NodeReference node: replicationPolicy.getPreferredMemberNodeList()) {
1250
			    	nodes.add(node.getValue());
1251
			    }
1252
		        this.insertReplicationPolicy(guid, policy, nodes);
1253
		    }
1254
        }
1255
        
1256
        // save replica information
1257
        this.insertReplicationStatus(guid, sm.getReplicaList());
1258
        
1259
        // save access policy
1260
        AccessPolicy accessPolicy = sm.getAccessPolicy();
1261
        if (accessPolicy != null) {
1262
        	try {
1263
				this.insertAccessPolicy(guid, accessPolicy);
1264
			} catch (AccessException e) {
1265
				throw new McdbDocNotFoundException(e);
1266
			}
1267
        }
1268
    }
1269
    
1270
    /**
1271
     * Creates Metacat access rules and inserts them
1272
     * @param accessPolicy
1273
     * @throws McdbDocNotFoundException
1274
     * @throws AccessException
1275
     */
1276
    private void insertAccessPolicy(String guid, AccessPolicy accessPolicy) throws McdbDocNotFoundException, AccessException {
1277
    	
1278
    	// check for the existing permOrder so that we remain compatible with it (DataONE does not care)
1279
        XMLAccessAccess accessController  = new XMLAccessAccess();
1280
		String existingPermOrder = AccessControlInterface.ALLOWFIRST;
1281
        Vector<XMLAccessDAO> existingAccess = accessController.getXMLAccessForDoc(guid);
1282
        if (existingAccess != null && existingAccess.size() > 0) {
1283
        	existingPermOrder = existingAccess.get(0).getPermOrder();
1284
        }
1285
        
1286
    	List<XMLAccessDAO> accessDAOs = new ArrayList<XMLAccessDAO>();
1287
        for (AccessRule accessRule: accessPolicy.getAllowList()) {
1288
        	List<Subject> subjects = accessRule.getSubjectList();
1289
        	List<Permission> permissions = accessRule.getPermissionList();
1290
        	for (Subject subject: subjects) {
1291
    			XMLAccessDAO accessDAO = new XMLAccessDAO();
1292
        		accessDAO.setPrincipalName(subject.getValue());
1293
    			accessDAO.setGuid(guid);
1294
    			accessDAO.setPermType(AccessControlInterface.ALLOW);
1295
				accessDAO.setPermOrder(existingPermOrder);
1296
    			if (permissions != null) {
1297
	    			for (Permission permission: permissions) {
1298
	    				Long metacatPermission = new Long(convertPermission(permission));
1299
	        			accessDAO.addPermission(metacatPermission);
1300
	    			}
1301
    			}
1302
    			accessDAOs.add(accessDAO);
1303
        	}
1304
        }
1305
        
1306
        
1307
        // remove all existing allow records
1308
        accessController.deleteXMLAccessForDoc(guid, AccessControlInterface.ALLOW);
1309
        // add the ones we can for this guid
1310
        accessController.insertAccess(guid, accessDAOs);
1311
        
1312
        
1313
    }
1314
    
1315
    /**
1316
     * Lookup access policy from Metacat
1317
     * @param guid
1318
     * @return
1319
     * @throws McdbDocNotFoundException
1320
     * @throws AccessException
1321
     */
1322
    public AccessPolicy getAccessPolicy(String guid) throws McdbDocNotFoundException, AccessException {
1323
        AccessPolicy accessPolicy = new AccessPolicy();
1324

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

    
1453
        try {
1454
            String sql = "select guid, date_uploaded, rights_holder, checksum, "
1455
                    + "checksum_algorithm, origin_member_node, authoritive_member_node, "
1456
                    + "date_modified, submitter, object_format, size from systemmetadata";
1457

    
1458
            boolean f1 = false;
1459
            boolean f2 = false;
1460
            boolean f3 = false;
1461

    
1462
            if (startTime != null) {
1463
                sql += " where systemmetadata.date_modified >= ?";
1464
                f1 = true;
1465
            }
1466

    
1467
            if (endTime != null) {
1468
                if (!f1) {
1469
                    sql += " where systemmetadata.date_modified < ?";
1470
                } else {
1471
                    sql += " and systemmetadata.date_modified < ?";
1472
                }
1473
                f2 = true;
1474
            }
1475

    
1476
            if (objectFormatId != null) {
1477
                if (!f1 && !f2) {
1478
                    sql += " where object_format = ?";
1479
                } else {
1480
                    sql += " and object_format = ?";
1481
                }
1482
                f3 = true;
1483
            }
1484

    
1485
            if (replicaStatus) {
1486
                String currentNodeId = PropertyService.getInstance().getProperty("dataone.nodeId");
1487
                if (!f1 && !f2 && !f3) {
1488
                    sql += " where authoritive_member_node != '" +
1489
                        currentNodeId.trim() + "'";
1490
                } else {
1491
                    sql += " and authoritive_member_node != '" +
1492
                        currentNodeId.trim() + "'";
1493
                }
1494
            }
1495

    
1496
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.querySystemMetadata");
1497
            serialNumber = dbConn.getCheckOutSerialNumber();
1498
            PreparedStatement stmt = dbConn.prepareStatement(sql);
1499

    
1500
            if (f1 && f2 && f3) {
1501
                stmt.setTimestamp(1, new Timestamp(startTime.getTime()));
1502
                stmt.setTimestamp(2, new Timestamp(endTime.getTime()));
1503
                stmt.setString(3, objectFormatId.getValue());
1504
            } else if (f1 && f2 && !f3) {
1505
                stmt.setTimestamp(1, new Timestamp(startTime.getTime()));
1506
                stmt.setTimestamp(2, new Timestamp(endTime.getTime()));
1507
            } else if (f1 && !f2 && f3) {
1508
                stmt.setTimestamp(1, new Timestamp(startTime.getTime()));
1509
                stmt.setString(2, objectFormatId.getValue());
1510
            } else if (f1 && !f2 && !f3) {
1511
                stmt.setTimestamp(1, new Timestamp(startTime.getTime()));
1512
            } else if (!f1 && f2 && f3) {
1513
                stmt.setTimestamp(1, new Timestamp(endTime.getTime()));
1514
                stmt.setString(2, objectFormatId.getValue());
1515
            } else if (!f1 && !f2 && f3) {
1516
                stmt.setString(1, objectFormatId.getValue());
1517
            } else if (!f1 && f2 && !f3) {
1518
                stmt.setTimestamp(1, new Timestamp(endTime.getTime()));
1519
            }
1520

    
1521
            // logMetacat.debug("LISTOBJECTS QUERY: " + stmt.toString());
1522

    
1523
            ResultSet rs = stmt.executeQuery();
1524
            for (int i = 0; i < start; i++) {
1525
                if (rs.next()) {
1526
                    total++;
1527
                } else {
1528
                    break;
1529
                }
1530
            }
1531

    
1532
            int countIndex = 0;
1533

    
1534
            while (rs.next()) {                
1535
                total++;
1536
                if (countIndex >= count) {
1537
                    // allow unlimited (negative number for count)
1538
                    if (count > 0) {
1539
                        break;
1540
                    }
1541
                }
1542

    
1543
                String guid = rs.getString(1);
1544
                // logMetacat.debug("query found doc with guid " + guid);
1545
                // Timestamp dateUploaded = rs.getTimestamp(2);
1546
                // String rightsHolder = rs.getString(3);
1547
                String checksum = rs.getString(4);
1548
                String checksumAlgorithm = rs.getString(5);
1549
                // String originMemberNode = rs.getString(6);
1550
                // String authoritiveMemberNode = rs.getString(7);
1551
                Timestamp dateModified = rs.getTimestamp(8);
1552
                // String submitter = rs.getString(9);
1553
                String fmtidStr = rs.getString(10);
1554
                String sz = rs.getString(11);
1555
                BigInteger size = new BigInteger("0");
1556

    
1557
                if (sz != null && !sz.trim().equals("")) {
1558
                    size = new BigInteger(rs.getString(11));
1559
                }
1560

    
1561
                ObjectInfo oi = new ObjectInfo();
1562

    
1563
                Identifier id = new Identifier();
1564
                id.setValue(guid);
1565
                oi.setIdentifier(id);
1566

    
1567
                if (dateModified != null) {
1568
                    oi.setDateSysMetadataModified(dateModified);
1569
                }
1570

    
1571
                Checksum cs = new Checksum();
1572
                cs.setValue(checksum);
1573
                try {
1574
                    // cs.setAlgorithm(ChecksumAlgorithm.valueOf(checksumAlgorithm));
1575
                    cs.setAlgorithm(checksumAlgorithm);
1576
                } catch (Exception e) {
1577
                    logMetacat.error("could not parse checksum algorithm", e);
1578
                    continue;
1579
                }
1580
                oi.setChecksum(cs);
1581

    
1582
                // set the format type
1583
                ObjectFormatIdentifier fmtid = new ObjectFormatIdentifier();
1584
                fmtid.setValue(fmtidStr);
1585
                oi.setFormatId(fmtid);
1586

    
1587
                oi.setSize(size);
1588

    
1589
                // when requested count == 0, return an empty object list
1590
                if (count != 0) {
1591
                    ol.addObjectInfo(oi);                    
1592
                }
1593
                countIndex++;
1594
            }
1595

    
1596
            // expend the resultset to get the total count of possible rows
1597
            while (rs.next()) {
1598
                total++;
1599
            }
1600
        }
1601

    
1602
        finally {
1603
            // Return database connection to the pool
1604
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1605
        }
1606

    
1607
        ol.setStart(start);
1608
        ol.setCount(ol.sizeObjectInfoList());
1609
        ol.setTotal(total);
1610

    
1611
        return ol;
1612
    }
1613
    
1614
    /**
1615
     * create a mapping in the identifier table
1616
     * @param guid
1617
     * @param localId
1618
     */
1619
    public void createMapping(String guid, String localId)
1620
    {        
1621
        
1622
        int serialNumber = -1;
1623
        DBConnection dbConn = null;
1624
        try {
1625

    
1626
            // Parse the localId into scope and rev parts
1627
            AccessionNumber acc = new AccessionNumber(localId, "NOACTION");
1628
            String docid = acc.getDocid();
1629
            int rev = 1;
1630
            if (acc.getRev() != null) {
1631
              rev = (new Integer(acc.getRev()).intValue());
1632
            }
1633

    
1634
            // Get a database connection from the pool
1635
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.createMapping");
1636
            serialNumber = dbConn.getCheckOutSerialNumber();
1637

    
1638
            // Execute the insert statement
1639
            String query = "insert into " + TYPE_IDENTIFIER + " (guid, docid, rev) values (?, ?, ?)";
1640
            PreparedStatement stmt = dbConn.prepareStatement(query);
1641
            stmt.setString(1, guid);
1642
            stmt.setString(2, docid);
1643
            stmt.setInt(3, rev);
1644
            logMetacat.debug("mapping query: " + stmt.toString());
1645
            int rows = stmt.executeUpdate();
1646

    
1647
            stmt.close();
1648
        } catch (SQLException e) {
1649
            e.printStackTrace();
1650
            logMetacat.error("createGenericMapping: SQL error while creating a mapping to the " + TYPE_IDENTIFIER + " identifier: " 
1651
                    + e.getMessage());
1652
        } catch (NumberFormatException e) {
1653
            e.printStackTrace();
1654
            logMetacat.error("createGenericMapping: NumberFormat error while creating a mapping to the " + TYPE_IDENTIFIER + " identifier: " 
1655
                    + e.getMessage());
1656
        } catch (AccessionNumberException e) {
1657
            e.printStackTrace();
1658
            logMetacat.error("createGenericMapping: AccessionNumber error while creating a mapping to the " + TYPE_IDENTIFIER + " identifier: " 
1659
                    + e.getMessage());
1660
        } finally {
1661
            // Return database connection to the pool
1662
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1663
        }
1664
    }
1665
    
1666
    /**
1667
     * remove a mapping in the identifier table
1668
     * @param guid
1669
     * @param localId
1670
     */
1671
    public void removeMapping(String guid, String localId)
1672
    {        
1673
        
1674
        int serialNumber = -1;
1675
        DBConnection dbConn = null;
1676
        try {
1677

    
1678
            // Parse the localId into scope and rev parts
1679
            AccessionNumber acc = new AccessionNumber(localId, "NOACTION");
1680
            String docid = acc.getDocid();
1681
            int rev = 1;
1682
            if (acc.getRev() != null) {
1683
              rev = (new Integer(acc.getRev()).intValue());
1684
            }
1685

    
1686
            // Get a database connection from the pool
1687
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.removeMapping");
1688
            serialNumber = dbConn.getCheckOutSerialNumber();
1689

    
1690
            // Execute the insert statement
1691
            String query = "DELETE FROM " + TYPE_IDENTIFIER + " WHERE guid = ? AND docid = ? AND rev = ?";
1692
            PreparedStatement stmt = dbConn.prepareStatement(query);
1693
            stmt.setString(1, guid);
1694
            stmt.setString(2, docid);
1695
            stmt.setInt(3, rev);
1696
            logMetacat.debug("remove mapping query: " + stmt.toString());
1697
            int rows = stmt.executeUpdate();
1698

    
1699
            stmt.close();
1700
        } catch (SQLException e) {
1701
            e.printStackTrace();
1702
            logMetacat.error("removeMapping: SQL error while removing a mapping to the " + TYPE_IDENTIFIER + " identifier: " 
1703
                    + e.getMessage());
1704
        } catch (NumberFormatException e) {
1705
            e.printStackTrace();
1706
            logMetacat.error("removeMapping: NumberFormat error while removing a mapping to the " + TYPE_IDENTIFIER + " identifier: " 
1707
                    + e.getMessage());
1708
        } catch (AccessionNumberException e) {
1709
            e.printStackTrace();
1710
            logMetacat.error("removeMapping: AccessionNumber error while removing a mapping to the " + TYPE_IDENTIFIER + " identifier: " 
1711
                    + e.getMessage());
1712
        } finally {
1713
            // Return database connection to the pool
1714
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1715
        }
1716
    }
1717
    
1718
    /**
1719
     * create the systemmetadata record
1720
     * @param guid
1721
     */
1722
    private void insertSystemMetadata(String guid)
1723
    {        
1724
        
1725
        int serialNumber = -1;
1726
        DBConnection dbConn = null;
1727
        try {
1728

    
1729
            // Get a database connection from the pool
1730
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.insertSystemMetadata");
1731
            serialNumber = dbConn.getCheckOutSerialNumber();
1732

    
1733
            // Execute the insert statement
1734
            String query = "insert into " + TYPE_SYSTEM_METADATA + " (guid) values (?)";
1735
            PreparedStatement stmt = dbConn.prepareStatement(query);
1736
            stmt.setString(1, guid);
1737
            logMetacat.debug("system metadata query: " + stmt.toString());
1738
            int rows = stmt.executeUpdate();
1739

    
1740
            stmt.close();
1741
        } catch (Exception e) {
1742
            e.printStackTrace();
1743
            logMetacat.error("Error while creating " + TYPE_SYSTEM_METADATA + " record: " + guid, e );
1744
        } finally {
1745
            // Return database connection to the pool
1746
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1747
        }
1748
    }
1749
    
1750
    public void deleteSystemMetadata(String guid)
1751
    {        
1752
        
1753
        int serialNumber = -1;
1754
        DBConnection dbConn = null;
1755
        String query = null;
1756
        PreparedStatement stmt = null;
1757
        int rows = 0;
1758
        try {
1759

    
1760
            // Get a database connection from the pool
1761
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.deleteSystemMetadata");
1762
            serialNumber = dbConn.getCheckOutSerialNumber();
1763

    
1764
            // remove main system metadata entry
1765
            query = "delete from " + TYPE_SYSTEM_METADATA + " where guid = ? ";
1766
            stmt = dbConn.prepareStatement(query);
1767
            stmt.setString(1, guid);
1768
            logMetacat.debug("delete system metadata: " + stmt.toString());
1769
            rows = stmt.executeUpdate();
1770
            stmt.close();
1771
            
1772
            // remove the systemMetadataReplicationPolicy
1773
            query = "delete from systemMetadataReplicationPolicy " + 
1774
            "where guid = ?";
1775
            stmt = dbConn.prepareStatement(query);
1776
            stmt.setString(1, guid);
1777
            logMetacat.debug("delete systemMetadataReplicationPolicy: " + stmt.toString());
1778
            rows = stmt.executeUpdate();
1779
            stmt.close();
1780
            
1781
            // remove the systemMetadataReplicationStatus
1782
            query = "delete from systemMetadataReplicationStatus " + 
1783
            "where guid = ?";
1784
            stmt = dbConn.prepareStatement(query);
1785
            stmt.setString(1, guid);
1786
            logMetacat.debug("delete systemMetadataReplicationStatus: " + stmt.toString());
1787
            rows = stmt.executeUpdate();
1788
            stmt.close();
1789
            
1790
            // TODO: remove the access?
1791
            // Metacat keeps "deleted" documents so we should not remove access rules.
1792
            
1793
        } catch (Exception e) {
1794
            e.printStackTrace();
1795
            logMetacat.error("Error while deleting " + TYPE_SYSTEM_METADATA + " record: " + guid, e );
1796
            try {
1797
				dbConn.rollback();
1798
			} catch (SQLException sqle) {
1799
	            logMetacat.error("Error while rolling back delete for record: " + guid, sqle );
1800
			}
1801
        } finally {
1802
            // Return database connection to the pool
1803
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1804
        }
1805
    }
1806
    
1807
    public void updateAuthoritativeMemberNodeId(String existingMemberNodeId, String newMemberNodeId)
1808
    {
1809
        DBConnection dbConn = null;
1810
        int serialNumber = -1;
1811
        
1812
        try {
1813
            // Get a database connection from the pool
1814
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.updateAuthoritativeMemberNodeId");
1815
            serialNumber = dbConn.getCheckOutSerialNumber();
1816

    
1817
            // Execute the insert statement
1818
            String query = "update " + TYPE_SYSTEM_METADATA + 
1819
                " set authoritive_member_node = ? " +
1820
                " where authoritive_member_node = ?";
1821
            PreparedStatement stmt = dbConn.prepareStatement(query);
1822
            
1823
            //data values
1824
            stmt.setString(1, newMemberNodeId);
1825
            stmt.setString(2, existingMemberNodeId);
1826

    
1827
            logMetacat.debug("stmt: " + stmt.toString());
1828
            //execute
1829
            int rows = stmt.executeUpdate();
1830

    
1831
            stmt.close();
1832
        } catch (SQLException e) {
1833
            e.printStackTrace();
1834
            logMetacat.error("updateSystemMetadataFields: SQL error while updating system metadata: " 
1835
                    + e.getMessage());
1836
        } catch (NumberFormatException e) {
1837
            e.printStackTrace();
1838
            logMetacat.error("updateSystemMetadataFields: NumberFormat error while updating system metadata: " 
1839
                    + e.getMessage());
1840
        } finally {
1841
            // Return database connection to the pool
1842
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1843
        }
1844
    }
1845
}
1846

    
(37-37/64)