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
        Hashtable<String, Object> h = new Hashtable<String, Object>();
94
        String sql = "select docname, doctype, user_owner, user_updated, " +
95
            "server_location, rev, date_created, date_updated from " + 
96
            "xml_documents where docid like '" + localId + "'";
97
        DBConnection dbConn = null;
98
        int serialNumber = -1;
99
        try 
100
        {
101
            // Get a database connection from the pool
102
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getDocumentInfo");
103
            serialNumber = dbConn.getCheckOutSerialNumber();
104

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

    
190
            // Execute the insert statement
191
            PreparedStatement stmt = dbConn.prepareStatement(sql);
192
            ResultSet rs = stmt.executeQuery();
193
            if (rs.next()) 
194
            {
195
                rev = rs.getInt(1);
196
                stmt.close();
197
            } 
198
            else
199
            {
200
                stmt.close();
201
                DBConnectionPool.returnDBConnection(dbConn, serialNumber);
202
                throw new McdbDocNotFoundException("Could not find document " + localId);
203
            }
204
        } 
205
        catch (SQLException e) 
206
        {
207
            logMetacat.error("Error while looking up the guid: " 
208
                    + e.getMessage());
209
        } 
210
        finally 
211
        {
212
            // Return database connection to the pool
213
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
214
        }
215
        return rev;
216
    }
217
    
218
    /**
219
     * return all local ids in the object store that do not have associated
220
     * system metadata and are not themselves system metadata
221
     */
222
    public List<String> getLocalIdsWithNoSystemMetadata()
223
    {
224
        Vector<String> ids = new Vector<String>();
225
        //String sql = "select docid from identifier where guid in (select guid from systemmetadata)"; 
226
        String sql = "select docid from xml_documents where docid not " +
227
            "in (select docid from identifier where guid in (select guid from systemmetadata)) " +
228
            "and doctype not like '" + DATAONE_SM_DOCTYPE + "'";
229
        DBConnection dbConn = null;
230
        int serialNumber = -1;
231
        try 
232
        {
233
            // Get a database connection from the pool
234
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getLocalIdsWithNoSystemMetadata");
235
            serialNumber = dbConn.getCheckOutSerialNumber();
236

    
237
            // Execute the insert statement
238
            PreparedStatement stmt = dbConn.prepareStatement(sql);
239
            ResultSet rs = stmt.executeQuery();
240
            while (rs.next()) 
241
            {
242
                String localid = rs.getString(1);
243
                ids.add(localid);
244
            } 
245
            stmt.close();
246
        } 
247
        catch (SQLException e) 
248
        {
249
            logMetacat.error("Error while looking up the guid: " 
250
                    + e.getMessage());
251
        } 
252
        finally 
253
        {
254
            // Return database connection to the pool
255
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
256
        }
257
        
258
        return ids;
259
    }
260
    
261
    /**
262
     * return a listing of all local ids in the object store
263
     * @return a list of all local ids in metacat
264
     */
265
    public List<String> getAllLocalIds()
266
       throws Exception
267
    {
268
        Vector<String> ids = new Vector<String>();
269
        String sql = "select docid from xml_documents";
270
        DBConnection dbConn = null;
271
        int serialNumber = -1;
272
        try 
273
        {
274
            // Get a database connection from the pool
275
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getAllLocalIds");
276
            serialNumber = dbConn.getCheckOutSerialNumber();
277

    
278
            // Execute the insert statement
279
            PreparedStatement stmt = dbConn.prepareStatement(sql);
280
            ResultSet rs = stmt.executeQuery();
281
            while (rs.next()) 
282
            {
283
                String localid = rs.getString(1);
284
                ids.add(localid);
285
            } 
286
            stmt.close();
287
        } 
288
        catch (SQLException e) 
289
        {
290
            logMetacat.error("Error while looking up the guid: " 
291
                    + e.getMessage());
292
        } 
293
        finally 
294
        {
295
            // Return database connection to the pool
296
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
297
        }
298
        return ids;
299
    }
300
    
301
    /**
302
     * Lookup a GUID from the database, and return the associated LocalID. If
303
     * the identifier is not found, throw an exception.
304
     * 
305
     * @param guid the global identifier to look up
306
     * @return String containing the corresponding LocalId
307
     * @throws McdbDocNotFoundException if the identifier is not found
308
     */
309
    public String getLocalId(String guid) throws McdbDocNotFoundException
310
    {
311
        return this.getLocalId(guid, TYPE_IDENTIFIER);
312
    }
313
    
314
    /**
315
     * Determine if an identifier exists already, returning true if so.
316
     * 
317
     * @param guid the global identifier to look up
318
     * @return boolean true if the identifier exists
319
     */
320
    public boolean identifierExists(String guid)
321
    {
322
        boolean idExists = false;
323
        try {
324
            String id = getLocalId(guid);
325
            if (id != null) {
326
                idExists = true;
327
            }
328
        } catch (McdbDocNotFoundException e) {
329
            idExists = false;
330
        }
331
        return idExists;
332
    }
333
    
334
    /**
335
     * Create a mapping between two identifiers, one representing an 
336
     * unconstrained, globally unique string (guid), and one a localId 
337
     * in the Metacat docid format (scope.id.revision). 
338
     * This mapping allows one to find the global id (guid) from the localId.
339
     * 
340
     * @param guid the global string identifier to be mapped
341
     * @param localId the local identifier to be mapped
342
     */
343
    public void createMapping(String guid, String localId)
344
    {   
345
        createGenericMapping(guid, localId, TYPE_IDENTIFIER);
346
    }
347
    
348
    /**
349
     * 
350
     * @param guid
351
     * @param rev
352
     * @return
353
     */
354
    public String generateLocalId(String guid, int rev)
355
    {
356
        return generateLocalId(guid, rev, false);
357
    }
358

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

    
498
            // Get a database connection from the pool
499
            dbConn = 
500
                DBConnectionPool.getDBConnection("IdentifierManager.createMapping");
501
            serialNumber = dbConn.getCheckOutSerialNumber();
502

    
503
            // Execute the insert statement
504
            String query = "update systemmetadata set (docid, rev) = (?, ?) where guid='" + guid + "'";
505
            //System.out.println("query: " + query + " for params: (guid:" + guid + ", docid=" + docid + ", rev=" + rev + ")");
506
            PreparedStatement stmt = dbConn.prepareStatement(query);
507
            stmt.setString(1, docid);
508
            stmt.setInt(2, rev);
509
            int rows = stmt.executeUpdate();
510

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

    
610
            // Parse the localId into scope and rev parts
611
            AccessionNumber acc = new AccessionNumber(localId, "NOACTION");
612
            String docid = acc.getDocid();
613
            int rev = 1;
614
            if(acc.getRev() != null)
615
            {
616
              rev = (new Integer(acc.getRev()).intValue());
617
            }
618

    
619
            // Get a database connection from the pool
620
            dbConn = 
621
                DBConnectionPool.getDBConnection("IdentifierManager.createMapping");
622
            serialNumber = dbConn.getCheckOutSerialNumber();
623

    
624
            // Execute the insert statement
625
            String query = "insert into " + type + " (guid, docid, rev) values (?, ?, ?)";
626
            //System.out.println("query: " + query + " for params: (" + guid + ", " + docid + ", " + rev + ")");
627
            PreparedStatement stmt = dbConn.prepareStatement(query);
628
            stmt.setString(1, guid);
629
            stmt.setString(2, docid);
630
            stmt.setInt(3, rev);
631
            int rows = stmt.executeUpdate();
632

    
633
            stmt.close();
634
        } catch (SQLException e) {
635
            e.printStackTrace();
636
            logMetacat.error("SQL error while creating a mapping to the system metadata identifier: " 
637
                    + e.getMessage());
638
        } catch (NumberFormatException e) {
639
            e.printStackTrace();
640
            logMetacat.error("NumberFormat error while creating a mapping to the system metadata identifier: " 
641
                    + e.getMessage());
642
        } catch (AccessionNumberException e) {
643
            e.printStackTrace();
644
            logMetacat.error("AccessionNumber error while creating a mapping to the system metadata identifier: " 
645
                    + e.getMessage());
646
        } finally {
647
            // Return database connection to the pool
648
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
649
        }
650
    }
651
}
(37-37/65)