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

    
468
            // Get a database connection from the pool
469
            dbConn = 
470
                DBConnectionPool.getDBConnection("IdentifierManager.createMapping");
471
            serialNumber = dbConn.getCheckOutSerialNumber();
472

    
473
            // Execute the insert statement
474
            String query = "update systemmetadata set (docid, rev) = (?, ?) where guid='" + guid + "'";
475
            //System.out.println("query: " + query + " for params: (guid:" + guid + ", docid=" + docid + ", rev=" + rev + ")");
476
            PreparedStatement stmt = dbConn.prepareStatement(query);
477
            stmt.setString(1, docid);
478
            stmt.setInt(2, rev);
479
            int rows = stmt.executeUpdate();
480

    
481
            stmt.close();
482
        } catch (SQLException e) {
483
            e.printStackTrace();
484
            logMetacat.error("SQL error while updating a mapping to the system metadata identifier: " 
485
                    + e.getMessage());
486
        } catch (NumberFormatException e) {
487
            e.printStackTrace();
488
            logMetacat.error("NumberFormat error while updating a mapping to the system metadata identifier: " 
489
                    + e.getMessage());
490
        } catch (AccessionNumberException e) {
491
            e.printStackTrace();
492
            logMetacat.error("AccessionNumber error while updating a mapping to the system metadata identifier: " 
493
                    + e.getMessage());
494
        } finally {
495
            // Return database connection to the pool
496
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
497
        }
498
    }
499
    
500
    /**
501
     * return the localId of a system metadata document based on its guid
502
     */
503
    public String getSystemMetadataLocalId(String guid)
504
      throws McdbDocNotFoundException
505
    {
506
      return this.getLocalId(guid, TYPE_SYSTEM_METADATA);
507
    }
508
    
509
    /**
510
     * return a localId based on a guid.  The type can either be 'identifier' 
511
     * to get an id from the identifier table or 'systemmetadata' to get
512
     * the identifier from the systemidentifier table
513
     */
514
    private String getLocalId(String guid, String type)
515
      throws McdbDocNotFoundException
516
    {
517
      if(!type.equals(TYPE_IDENTIFIER) &&
518
        !type.equals(TYPE_SYSTEM_METADATA))
519
      {
520
        throw new RuntimeException("cannot lookup the identifier for type " + type +
521
          ".  Please choose 'identifier' or 'systemmetadata'.");
522
      }
523
      
524
      String db_guid = "";
525
      String docid = "";
526
      int rev = 0;
527
      
528
      String query = "select guid, docid, rev from " + type + " where guid = ?";
529
      //System.out.println("query: " + query + " for params: (" + guid + ")");
530
      
531
      DBConnection dbConn = null;
532
      int serialNumber = -1;
533
      try {
534
          // Get a database connection from the pool
535
          dbConn = DBConnectionPool.getDBConnection("Identifier.getLocalId");
536
          serialNumber = dbConn.getCheckOutSerialNumber();
537
          
538
          // Execute the insert statement
539
          PreparedStatement stmt = dbConn.prepareStatement(query);
540
          stmt.setString(1, guid);
541
          ResultSet rs = stmt.executeQuery();
542
          if (rs.next()) {
543
              db_guid = rs.getString(1);
544
              docid = rs.getString(2);
545
              rev = rs.getInt(3);
546
              assert(db_guid.equals(guid));
547
          } else {
548
              throw new McdbDocNotFoundException("Document not found:" + guid);
549
          }
550
          stmt.close();
551
      } catch (SQLException e) {
552
          logMetacat.error("Error while looking up the local identifier: " 
553
                  + e.getMessage());
554
      } finally {
555
          // Return database connection to the pool
556
          DBConnectionPool.returnDBConnection(dbConn, serialNumber);
557
      }
558
      return docid + "." + rev;
559
    }
560
    
561
    /**
562
     * create a mapping in the systemmetadata or identifier table
563
     * @param guid
564
     * @param localId
565
     * @param type
566
     */
567
    private void createGenericMapping(String guid, String localId, String type)
568
    {
569
        if(!type.equals(TYPE_IDENTIFIER) &&
570
        !type.equals(TYPE_SYSTEM_METADATA))
571
        {
572
          throw new RuntimeException("Cannot create mapping for type " + type +
573
            ".  Please choose 'identifier' or 'systemmetadata'.");
574
        }
575
        
576
        int serialNumber = -1;
577
        DBConnection dbConn = null;
578
        try {
579

    
580
            // Parse the localId into scope and rev parts
581
            AccessionNumber acc = new AccessionNumber(localId, "NOACTION");
582
            String docid = acc.getDocid();
583
            int rev = 1;
584
            if(acc.getRev() != null)
585
            {
586
              rev = (new Integer(acc.getRev()).intValue());
587
            }
588

    
589
            // Get a database connection from the pool
590
            dbConn = 
591
                DBConnectionPool.getDBConnection("IdentifierManager.createMapping");
592
            serialNumber = dbConn.getCheckOutSerialNumber();
593

    
594
            // Execute the insert statement
595
            String query = "insert into " + type + " (guid, docid, rev) values (?, ?, ?)";
596
            //System.out.println("query: " + query + " for params: (" + guid + ", " + docid + ", " + rev + ")");
597
            PreparedStatement stmt = dbConn.prepareStatement(query);
598
            stmt.setString(1, guid);
599
            stmt.setString(2, docid);
600
            stmt.setInt(3, rev);
601
            int rows = stmt.executeUpdate();
602

    
603
            stmt.close();
604
        } catch (SQLException e) {
605
            e.printStackTrace();
606
            logMetacat.error("SQL error while creating a mapping to the system metadata identifier: " 
607
                    + e.getMessage());
608
        } catch (NumberFormatException e) {
609
            e.printStackTrace();
610
            logMetacat.error("NumberFormat error while creating a mapping to the system metadata identifier: " 
611
                    + e.getMessage());
612
        } catch (AccessionNumberException e) {
613
            e.printStackTrace();
614
            logMetacat.error("AccessionNumber error while creating a mapping to the system metadata identifier: " 
615
                    + e.getMessage());
616
        } finally {
617
            // Return database connection to the pool
618
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
619
        }
620
    }
621
}
(37-37/63)