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_acccess " +
137
            "where docid like '" + localId + "'";
138
            PreparedStatement stmt2 = dbConn.prepareStatement(sql2);
139
            rs = stmt2.executeQuery();
140
            Vector accessVector = new Vector();
141
            if(rs.next())
142
            {
143
                Hashtable accessHash = new Hashtable();
144
                String principal_name = rs.getString(1);
145
                String permission = rs.getString(2);
146
                String permissionType = rs.getString(3);
147
                String permissionOrder = rs.getString(4);
148
                accessHash.put("principal_name", principal_name);
149
                accessHash.put("permission", permission);
150
                accessHash.put("permission_type", permissionType);
151
                accessHash.put("permission_order", permissionOrder);
152
                accessVector.add(accessHash);
153
            }
154
            h.put("access", accessVector);
155
        } 
156
        catch (SQLException e) 
157
        {
158
            logMetacat.error("Error while getting document info for localid " + localId + " : "  
159
                    + e.getMessage());
160
        } 
161
        finally 
162
        {
163
            // Return database connection to the pool
164
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
165
        }
166
        return h;
167
    }
168
    
169
    /**
170
     * return the newest rev for a given localId
171
     * @param localId
172
     * @return
173
     */
174
    public int getLatestRevForLocalId(String localId)
175
        throws McdbDocNotFoundException
176
    {
177
        int rev = 0;
178
        String sql = "select rev from xml_documents where docid like '" + localId + "'";
179
        DBConnection dbConn = null;
180
        int serialNumber = -1;
181
        try 
182
        {
183
            // Get a database connection from the pool
184
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getLatestRevForLocalId");
185
            serialNumber = dbConn.getCheckOutSerialNumber();
186

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

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

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

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

    
465
            // Get a database connection from the pool
466
            dbConn = 
467
                DBConnectionPool.getDBConnection("IdentifierManager.createMapping");
468
            serialNumber = dbConn.getCheckOutSerialNumber();
469

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

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

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

    
586
            // Get a database connection from the pool
587
            dbConn = 
588
                DBConnectionPool.getDBConnection("IdentifierManager.createMapping");
589
            serialNumber = dbConn.getCheckOutSerialNumber();
590

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

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