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.util.*;
28

    
29
import java.sql.PreparedStatement;
30
import java.sql.ResultSet;
31
import java.sql.SQLException;
32

    
33
import org.apache.log4j.Logger;
34

    
35
import edu.ucsb.nceas.metacat.database.DBConnection;
36
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
37
import edu.ucsb.nceas.metacat.util.DocumentUtil;
38

    
39
/**
40
 * Manage the relationship between Metacat local identifiers (LocalIDs) that are
41
 * codified as the (docid, rev) pair with globally uniqe string identifiers
42
 * (GUIDs) that are opaque strings.  This class provides methods to manage these
43
 * identifiers, and to search for and look up LocalIDs based on their GUID and
44
 * vice versa. IdentifierManager is a singleton.
45
 * 
46
 * @author Matthew Jones
47
 */
48
public class IdentifierManager {
49
    
50
    public static final String TYPE_SYSTEM_METADATA = "systemmetadata";
51
    public static final String TYPE_IDENTIFIER = "identifier";
52
    public static final String DATAONE_SM_DOCTYPE = "http://dataone.org/service/types/SystemMetadata/0.1";
53
  
54
    /**
55
     * The single instance of the manager that is always returned.
56
     */
57
    private static IdentifierManager self = null;
58
    private Logger logMetacat = Logger.getLogger(EventLog.class);
59

    
60
    /**
61
     * A private constructor that initializes the class when getInstance() is
62
     * called.
63
     */
64
    private IdentifierManager()
65
    {
66
    }
67

    
68
    /**
69
     * Return the single instance of the manager after initializing it if it
70
     * wasn't previously initialized.
71
     * 
72
     * @return the single IdentifierManager instance
73
     */
74
    public static IdentifierManager getInstance()
75
    {
76
        if (self == null) {
77
            self = new IdentifierManager();
78
        }
79
        return self;
80
    }
81
    
82
    /**
83
     * return information on the document with localId.  These are the fields
84
     * from the xml_documents table.  They can be used to contstruct metadata 
85
     * about the object that is stored.
86
     * @param localId
87
     * @return
88
     * @throws McdbDocNotFoundException
89
     */
90
    public Hashtable<String, Object> getDocumentInfo(String localId)
91
        throws McdbDocNotFoundException
92
    {
93
        try
94
        {
95
            AccessionNumber acc = new AccessionNumber(localId, "NONE");
96
            localId = acc.getDocid();
97
        }
98
        catch(Exception e)
99
        {
100
            //do nothing. just try the localId as it is
101
        }
102
        Hashtable<String, Object> h = new Hashtable<String, Object>();
103
        String sql = "select docname, doctype, user_owner, user_updated, " +
104
            "server_location, rev, date_created, date_updated from " + 
105
            "xml_documents where docid like '" + localId + "'";
106
        DBConnection dbConn = null;
107
        int serialNumber = -1;
108
        try 
109
        {
110
            // Get a database connection from the pool
111
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getDocumentInfo");
112
            serialNumber = dbConn.getCheckOutSerialNumber();
113

    
114
            // Execute the insert statement
115
            PreparedStatement stmt = dbConn.prepareStatement(sql);
116
            ResultSet rs = stmt.executeQuery();
117
            if (rs.next()) 
118
            {
119
                String docname = rs.getString(1);
120
                String doctype = rs.getString(2);
121
                String user_owner = rs.getString(3);
122
                String user_updated = rs.getString(4);
123
                String server_location = rs.getString(5);
124
                int rev = rs.getInt(6);
125
                String date_created = rs.getString(7);
126
                String date_updated = rs.getString(8);
127
                h.put("docname", docname);
128
                h.put("doctype", doctype);
129
                h.put("user_owner", user_owner);
130
                h.put("user_updated", user_updated);
131
                h.put("server_location", server_location);
132
                h.put("rev", new Integer(rev).toString());
133
                h.put("date_created", date_created);
134
                h.put("date_updated", date_updated);
135
                
136
                stmt.close();
137
            } 
138
            else
139
            {
140
                stmt.close();
141
                DBConnectionPool.returnDBConnection(dbConn, serialNumber);
142
                throw new McdbDocNotFoundException("2Could not find document " + localId);
143
            }
144
            
145
            String sql2 = "select principal_name, permission, perm_type, perm_order from xml_access " +
146
            "where docid like '" + localId + "'";
147
            System.out.println("executing sql: " + sql2);
148
            PreparedStatement stmt2 = dbConn.prepareStatement(sql2);
149
            rs = stmt2.executeQuery();
150
            Vector accessVector = new Vector();
151
            while(rs.next())
152
            {
153
                Hashtable accessHash = new Hashtable();
154
                String principal_name = rs.getString(1);
155
                String permission = rs.getString(2);
156
                String permissionType = rs.getString(3);
157
                String permissionOrder = rs.getString(4);
158
                accessHash.put("principal_name", principal_name);
159
                accessHash.put("permission", permission);
160
                accessHash.put("permission_type", permissionType);
161
                accessHash.put("permission_order", permissionOrder);
162
                System.out.println("accessHash: " + accessHash.toString());
163
                accessVector.add(accessHash);
164
            }
165
            h.put("access", accessVector);
166
        } 
167
        catch (SQLException e) 
168
        {
169
            e.printStackTrace();
170
            logMetacat.error("Error while getting document info for localid " + localId + " : "  
171
                    + e.getMessage());
172
        } 
173
        finally 
174
        {
175
            // Return database connection to the pool
176
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
177
        }
178
        return h;
179
    }
180
    
181
    /**
182
     * return the newest rev for a given localId
183
     * @param localId
184
     * @return
185
     */
186
    public int getLatestRevForLocalId(String localId)
187
        throws McdbDocNotFoundException
188
    {
189
        try
190
        {
191
            AccessionNumber acc = new AccessionNumber(localId, "NONE");
192
            localId = acc.getDocid();
193
        }
194
        catch(Exception e)
195
        {
196
            //do nothing. just try the localId as it is
197
        }
198
        int rev = 0;
199
        String sql = "select rev from xml_documents where docid like '" + localId + "'";
200
        DBConnection dbConn = null;
201
        int serialNumber = -1;
202
        try 
203
        {
204
            // Get a database connection from the pool
205
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getLatestRevForLocalId");
206
            serialNumber = dbConn.getCheckOutSerialNumber();
207

    
208
            // Execute the insert statement
209
            PreparedStatement stmt = dbConn.prepareStatement(sql);
210
            ResultSet rs = stmt.executeQuery();
211
            if (rs.next()) 
212
            {
213
                rev = rs.getInt(1);
214
                stmt.close();
215
            } 
216
            else
217
            {
218
                stmt.close();
219
                DBConnectionPool.returnDBConnection(dbConn, serialNumber);
220
                throw new McdbDocNotFoundException("While trying to get the latest rev, could not find document " + localId);
221
            }
222
        } 
223
        catch (SQLException e) 
224
        {
225
            logMetacat.error("Error while looking up the guid: " 
226
                    + e.getMessage());
227
        } 
228
        finally 
229
        {
230
            // Return database connection to the pool
231
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
232
        }
233
        return rev;
234
    }
235
    
236
    /**
237
     * return all local ids in the object store that do not have associated
238
     * system metadata and are not themselves system metadata
239
     */
240
    public List<String> getLocalIdsWithNoSystemMetadata()
241
    {
242
        Vector<String> ids = new Vector<String>();
243
        //String sql = "select docid from identifier where guid in (select guid from systemmetadata)"; 
244
        //String sql = "select docid from xml_documents where docid not " +
245
        //    "in (select docid from identifier where guid in (select guid from systemmetadata)) " +
246
        //    "and doctype not like '" + DATAONE_SM_DOCTYPE + "'";
247
        String sql = "select docid, rev from xml_documents where docid not in " +
248
            "(select docid from systemmetadata) and (docid not in " +
249
            "(select docid from identifier where guid in (select guid from systemmetadata)))";
250
        DBConnection dbConn = null;
251
        int serialNumber = -1;
252
        try 
253
        {
254
            // Get a database connection from the pool
255
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getLocalIdsWithNoSystemMetadata");
256
            serialNumber = dbConn.getCheckOutSerialNumber();
257

    
258
            // Execute the insert statement
259
            PreparedStatement stmt = dbConn.prepareStatement(sql);
260
            ResultSet rs = stmt.executeQuery();
261
            while (rs.next()) 
262
            {
263
                String localid = rs.getString(1);
264
                String rev = rs.getString(2);
265
                localid += "." + rev;
266
                System.out.println("id to add SM for: " + localid);
267
                ids.add(localid);
268
            } 
269
            stmt.close();
270
        } 
271
        catch (SQLException e) 
272
        {
273
            logMetacat.error("Error while looking up the guid: " 
274
                    + e.getMessage());
275
        } 
276
        finally 
277
        {
278
            // Return database connection to the pool
279
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
280
        }
281
        
282
        return ids;
283
    }
284
    
285
    /**
286
     * return a listing of all local ids in the object store
287
     * @return a list of all local ids in metacat
288
     */
289
    public List<String> getAllLocalIds()
290
       throws Exception
291
    {
292
        Vector<String> ids = new Vector<String>();
293
        String sql = "select docid from xml_documents";
294
        DBConnection dbConn = null;
295
        int serialNumber = -1;
296
        try 
297
        {
298
            // Get a database connection from the pool
299
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getAllLocalIds");
300
            serialNumber = dbConn.getCheckOutSerialNumber();
301

    
302
            // Execute the insert statement
303
            PreparedStatement stmt = dbConn.prepareStatement(sql);
304
            ResultSet rs = stmt.executeQuery();
305
            while (rs.next()) 
306
            {
307
                String localid = rs.getString(1);
308
                ids.add(localid);
309
            } 
310
            stmt.close();
311
        } 
312
        catch (SQLException e) 
313
        {
314
            logMetacat.error("Error while looking up the guid: " 
315
                    + e.getMessage());
316
        } 
317
        finally 
318
        {
319
            // Return database connection to the pool
320
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
321
        }
322
        return ids;
323
    }
324
    
325
    /**
326
     * Lookup a GUID from the database, and return the associated LocalID. If
327
     * the identifier is not found, throw an exception.
328
     * 
329
     * @param guid the global identifier to look up
330
     * @return String containing the corresponding LocalId
331
     * @throws McdbDocNotFoundException if the identifier is not found
332
     */
333
    public String getLocalId(String guid) throws McdbDocNotFoundException
334
    {
335
        return this.getLocalId(guid, TYPE_IDENTIFIER);
336
    }
337
    
338
    /**
339
     * Determine if an identifier exists already, returning true if so.
340
     * 
341
     * @param guid the global identifier to look up
342
     * @return boolean true if the identifier exists
343
     */
344
    public boolean identifierExists(String guid)
345
    {
346
        boolean idExists = false;
347
        try {
348
            String id = getLocalId(guid);
349
            if (id != null) {
350
                idExists = true;
351
            }
352
        } catch (McdbDocNotFoundException e) {
353
            idExists = false;
354
        }
355
        return idExists;
356
    }
357
    
358
    /**
359
     * Create a mapping between two identifiers, one representing an 
360
     * unconstrained, globally unique string (guid), and one a localId 
361
     * in the Metacat docid format (scope.id.revision). 
362
     * This mapping allows one to find the global id (guid) from the localId.
363
     * 
364
     * @param guid the global string identifier to be mapped
365
     * @param localId the local identifier to be mapped
366
     */
367
    public void createMapping(String guid, String localId)
368
    {   
369
        createGenericMapping(guid, localId, TYPE_IDENTIFIER);
370
    }
371
    
372
    /**
373
     * 
374
     * @param guid
375
     * @param rev
376
     * @return
377
     */
378
    public String generateLocalId(String guid, int rev)
379
    {
380
        return generateLocalId(guid, rev, false);
381
    }
382

    
383
    /**
384
     * Given a global identifier (guid), create a suitable local identifier that
385
     * follows Metacat's docid semantics and format (scope.id.rev), and create
386
     * a mapping between these two identifiers.  This effectively reserves both
387
     * the global and the local identifier, as they will now be present in the
388
     * identifier mapping table.  If the incoming guid has the syntax of a
389
     * Metacat docid (scope.id.rev), then simply use it.
390
     * 
391
     * @param guid the global string identifier
392
     * @param rev the revision number to be used in the localId
393
     * @return String containing the localId to be used for Metacat operations
394
     */
395
    public String generateLocalId(String guid, int rev, boolean isSystemMetadata) 
396
    {
397
        String localId = "";
398
        boolean conformsToDocidFormat = false;
399
        
400
        // Check if the guid passed in is already in docid (scope.id.rev) format
401
        try {
402
            AccessionNumber acc = new AccessionNumber(guid, "NONE");
403
            if (new Integer(acc.getRev()).intValue() > 0) {
404
                conformsToDocidFormat = true;
405
            }
406
        } catch (NumberFormatException e) {
407
            // No action needed, simply detecting invalid AccessionNumbers
408
        } catch (AccessionNumberException e) {
409
            // No action needed, simply detecting invalid AccessionNumbers
410
        } catch (SQLException e) {
411
            // No action needed, simply detecting invalid AccessionNumbers
412
        }
413
        
414
        if (conformsToDocidFormat) {
415
            // if it conforms, use it for both guid and localId
416
            localId = guid;
417
        } else {
418
            // if not, then generate a new unique localId
419
            localId = DocumentUtil.generateDocumentId(rev);
420
        }
421
        
422
        // Register this new pair in the identifier mapping table
423
        System.out.println("creating mapping in generateLocalId");
424
        if(!isSystemMetadata)
425
        { //don't do this if we're generating for system metadata
426
            createMapping(guid, localId);
427
        }
428
        
429
        return localId;
430
    }
431
    
432
    /**
433
     * given a local identifer, look up the guid.  Throw McdbDocNotFoundException
434
     * if the docid, rev is not found in the identifiers or systemmetadata tables
435
     *
436
     * @param docid the docid to look up
437
     * @param rev the revision of the docid to look up
438
     * @return String containing the mapped guid
439
     * @throws McdbDocNotFoundException if the docid, rev is not found
440
     */
441
    public String getGUID(String docid, int rev)
442
      throws McdbDocNotFoundException
443
    {
444
        System.out.println("getting guid for " + docid);
445
        String query = "select guid from identifier where docid = ? and rev = ?";
446
        String guid = null;
447
        boolean found = false;
448
        
449
        DBConnection dbConn = null;
450
        int serialNumber = -1;
451
        try {
452
            // Get a database connection from the pool
453
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getGUID");
454
            serialNumber = dbConn.getCheckOutSerialNumber();
455
            
456
            // Execute the insert statement
457
            PreparedStatement stmt = dbConn.prepareStatement(query);
458
            stmt.setString(1, docid);
459
            stmt.setInt(2, rev);
460
            ResultSet rs = stmt.executeQuery();
461
            if (rs.next()) 
462
            {
463
                guid = rs.getString(1);
464
            } 
465
            else
466
            {
467
                query = "select guid from systemmetadata where docid = ? and rev = ?";
468
                stmt = dbConn.prepareStatement(query);
469
                stmt.setString(1, docid);
470
                stmt.setInt(2, rev);
471
                rs = stmt.executeQuery();
472
                if(rs.next())
473
                {
474
                    guid = rs.getString(1);
475
                }
476
                else
477
                {
478
                    throw new McdbDocNotFoundException("No guid registered for docid " + docid + "." + rev);
479
                }
480
            }
481
            
482
        } catch (SQLException e) {
483
            logMetacat.error("Error while looking up the guid: " 
484
                    + e.getMessage());
485
        } finally {
486
            // Return database connection to the pool
487
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
488
        }
489
        
490
        return guid;
491
    }
492
    
493
    /**
494
     * insert a system metadata id for a local id
495
     * 
496
     * @param guid the id to insert
497
     * @param localId the systemMetadata object to get the local id for
498
     */
499
    public void createSystemMetadataMapping(String guid, String localId)
500
    {
501
        createGenericMapping(guid, localId, TYPE_SYSTEM_METADATA);
502
    }
503
    
504
    /**
505
     * update a mapping
506
     * @param guid
507
     * @param localId
508
     */
509
    public void updateSystemMetadataMapping(String guid, String localId)
510
    {
511
        int serialNumber = -1;
512
        DBConnection dbConn = null;
513
        try {
514
            // Parse the localId into scope and rev parts
515
            AccessionNumber acc = new AccessionNumber(localId, "NOACTION");
516
            String docid = acc.getDocid();
517
            int rev = 1;
518
            if(acc.getRev() != null)
519
            {
520
              rev = (new Integer(acc.getRev()).intValue());
521
            }
522

    
523
            // Get a database connection from the pool
524
            dbConn = 
525
                DBConnectionPool.getDBConnection("IdentifierManager.createMapping");
526
            serialNumber = dbConn.getCheckOutSerialNumber();
527

    
528
            // Execute the insert statement
529
            String query = "update systemmetadata set (docid, rev) = (?, ?) where guid='" + guid + "'";
530
            //System.out.println("query: " + query + " for params: (guid:" + guid + ", docid=" + docid + ", rev=" + rev + ")");
531
            PreparedStatement stmt = dbConn.prepareStatement(query);
532
            stmt.setString(1, docid);
533
            stmt.setInt(2, rev);
534
            int rows = stmt.executeUpdate();
535

    
536
            stmt.close();
537
        } catch (SQLException e) {
538
            e.printStackTrace();
539
            logMetacat.error("SQL error while updating a mapping to the system metadata identifier: " 
540
                    + e.getMessage());
541
        } catch (NumberFormatException e) {
542
            e.printStackTrace();
543
            logMetacat.error("NumberFormat error while updating a mapping to the system metadata identifier: " 
544
                    + e.getMessage());
545
        } catch (AccessionNumberException e) {
546
            e.printStackTrace();
547
            logMetacat.error("AccessionNumber error while updating a mapping to the system metadata identifier: " 
548
                    + e.getMessage());
549
        } finally {
550
            // Return database connection to the pool
551
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
552
        }
553
    }
554
    
555
    /**
556
     * return the localId of a system metadata document based on its guid
557
     */
558
    public String getSystemMetadataLocalId(String guid)
559
      throws McdbDocNotFoundException
560
    {
561
      return this.getLocalId(guid, TYPE_SYSTEM_METADATA);
562
    }
563
    
564
    /**
565
     * return a localId based on a guid.  The type can either be 'identifier' 
566
     * to get an id from the identifier table or 'systemmetadata' to get
567
     * the identifier from the systemidentifier table
568
     */
569
    private String getLocalId(String guid, String type)
570
      throws McdbDocNotFoundException
571
    {
572
      if(!type.equals(TYPE_IDENTIFIER) &&
573
        !type.equals(TYPE_SYSTEM_METADATA))
574
      {
575
        throw new RuntimeException("cannot lookup the identifier for type " + type +
576
          ".  Please choose 'identifier' or 'systemmetadata'.");
577
      }
578
      
579
      String db_guid = "";
580
      String docid = "";
581
      int rev = 0;
582
      
583
      String query = "select guid, docid, rev from " + type + " where guid = ?";
584
      //System.out.println("query: " + query + " for params: (" + guid + ")");
585
      
586
      DBConnection dbConn = null;
587
      int serialNumber = -1;
588
      try {
589
          // Get a database connection from the pool
590
          dbConn = DBConnectionPool.getDBConnection("Identifier.getLocalId");
591
          serialNumber = dbConn.getCheckOutSerialNumber();
592
          
593
          // Execute the insert statement
594
          PreparedStatement stmt = dbConn.prepareStatement(query);
595
          stmt.setString(1, guid);
596
          ResultSet rs = stmt.executeQuery();
597
          if (rs.next()) {
598
              db_guid = rs.getString(1);
599
              docid = rs.getString(2);
600
              rev = rs.getInt(3);
601
              assert(db_guid.equals(guid));
602
          } else {
603
              throw new McdbDocNotFoundException("Document not found:" + guid);
604
          }
605
          stmt.close();
606
      } catch (SQLException e) {
607
          logMetacat.error("Error while looking up the local identifier: " 
608
                  + e.getMessage());
609
      } finally {
610
          // Return database connection to the pool
611
          DBConnectionPool.returnDBConnection(dbConn, serialNumber);
612
      }
613
      return docid + "." + rev;
614
    }
615
    
616
    /**
617
     * create a mapping in the systemmetadata or identifier table
618
     * @param guid
619
     * @param localId
620
     * @param type
621
     */
622
    private void createGenericMapping(String guid, String localId, String type)
623
    {        
624
        if(!type.equals(TYPE_IDENTIFIER) &&
625
        !type.equals(TYPE_SYSTEM_METADATA))
626
        {
627
          throw new RuntimeException("Cannot create mapping for type " + type +
628
            ".  Please choose 'identifier' or 'systemmetadata'.");
629
        }
630
        
631
        int serialNumber = -1;
632
        DBConnection dbConn = null;
633
        try {
634

    
635
            // Parse the localId into scope and rev parts
636
            AccessionNumber acc = new AccessionNumber(localId, "NOACTION");
637
            String docid = acc.getDocid();
638
            int rev = 1;
639
            if(acc.getRev() != null)
640
            {
641
              rev = (new Integer(acc.getRev()).intValue());
642
            }
643

    
644
            // Get a database connection from the pool
645
            dbConn = 
646
                DBConnectionPool.getDBConnection("IdentifierManager.createMapping");
647
            serialNumber = dbConn.getCheckOutSerialNumber();
648

    
649
            // Execute the insert statement
650
            String query = "insert into " + type + " (guid, docid, rev) values (?, ?, ?)";
651
            //System.out.println("query: " + query + " for params: (" + guid + ", " + docid + ", " + rev + ")");
652
            PreparedStatement stmt = dbConn.prepareStatement(query);
653
            stmt.setString(1, guid);
654
            stmt.setString(2, docid);
655
            stmt.setInt(3, rev);
656
            int rows = stmt.executeUpdate();
657

    
658
            stmt.close();
659
        } catch (SQLException e) {
660
            e.printStackTrace();
661
            logMetacat.error("SQL error while creating a mapping to the system metadata identifier: " 
662
                    + e.getMessage());
663
        } catch (NumberFormatException e) {
664
            e.printStackTrace();
665
            logMetacat.error("NumberFormat error while creating a mapping to the system metadata identifier: " 
666
                    + e.getMessage());
667
        } catch (AccessionNumberException e) {
668
            e.printStackTrace();
669
            logMetacat.error("AccessionNumber error while creating a mapping to the system metadata identifier: " 
670
                    + e.getMessage());
671
        } finally {
672
            // Return database connection to the pool
673
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
674
        }
675
    }
676
}
677

    
(37-37/65)