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, String> getDocumentInfo(String localId)
91
        throws McdbDocNotFoundException
92
    {
93
        Hashtable<String, String> h = new Hashtable<String, String>();
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
        catch (SQLException e) 
137
        {
138
            logMetacat.error("Error while getting document info for localid " + localId + " : "  
139
                    + e.getMessage());
140
        } 
141
        finally 
142
        {
143
            // Return database connection to the pool
144
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
145
        }
146
        return h;
147
    }
148
    
149
    /**
150
     * return the newest rev for a given localId
151
     * @param localId
152
     * @return
153
     */
154
    public int getLatestRevForLocalId(String localId)
155
        throws McdbDocNotFoundException
156
    {
157
        int rev = 0;
158
        String sql = "select rev from xml_documents where docid like '" + localId + "'";
159
        DBConnection dbConn = null;
160
        int serialNumber = -1;
161
        try 
162
        {
163
            // Get a database connection from the pool
164
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getLatestRevForLocalId");
165
            serialNumber = dbConn.getCheckOutSerialNumber();
166

    
167
            // Execute the insert statement
168
            PreparedStatement stmt = dbConn.prepareStatement(sql);
169
            ResultSet rs = stmt.executeQuery();
170
            if (rs.next()) 
171
            {
172
                rev = rs.getInt(1);
173
                stmt.close();
174
            } 
175
            else
176
            {
177
                stmt.close();
178
                DBConnectionPool.returnDBConnection(dbConn, serialNumber);
179
                throw new McdbDocNotFoundException("Could not find document " + localId);
180
            }
181
        } 
182
        catch (SQLException e) 
183
        {
184
            logMetacat.error("Error while looking up the guid: " 
185
                    + e.getMessage());
186
        } 
187
        finally 
188
        {
189
            // Return database connection to the pool
190
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
191
        }
192
        return rev;
193
    }
194
    
195
    /**
196
     * return all local ids in the object store that do not have associated
197
     * system metadata and are not themselves system metadata
198
     */
199
    public List<String> getLocalIdsWithNoSystemMetadata()
200
    {
201
        Vector<String> ids = new Vector<String>();
202
        //String sql = "select docid from identifier where guid in (select guid from systemmetadata)"; 
203
        String sql = "select docid from xml_documents where docid not " +
204
            "in (select docid from identifier where guid in (select guid from systemmetadata)) " +
205
            "and doctype not like '" + DATAONE_SM_DOCTYPE + "'";
206
        DBConnection dbConn = null;
207
        int serialNumber = -1;
208
        try 
209
        {
210
            // Get a database connection from the pool
211
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getLocalIdsWithNoSystemMetadata");
212
            serialNumber = dbConn.getCheckOutSerialNumber();
213

    
214
            // Execute the insert statement
215
            PreparedStatement stmt = dbConn.prepareStatement(sql);
216
            ResultSet rs = stmt.executeQuery();
217
            while (rs.next()) 
218
            {
219
                String localid = rs.getString(1);
220
                ids.add(localid);
221
            } 
222
            stmt.close();
223
        } 
224
        catch (SQLException e) 
225
        {
226
            logMetacat.error("Error while looking up the guid: " 
227
                    + e.getMessage());
228
        } 
229
        finally 
230
        {
231
            // Return database connection to the pool
232
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
233
        }
234
        
235
        return ids;
236
    }
237
    
238
    /**
239
     * return a listing of all local ids in the object store
240
     * @return a list of all local ids in metacat
241
     */
242
    public List<String> getAllLocalIds()
243
       throws Exception
244
    {
245
        Vector<String> ids = new Vector<String>();
246
        String sql = "select docid from xml_documents";
247
        DBConnection dbConn = null;
248
        int serialNumber = -1;
249
        try 
250
        {
251
            // Get a database connection from the pool
252
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getAllLocalIds");
253
            serialNumber = dbConn.getCheckOutSerialNumber();
254

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

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

    
445
            // Get a database connection from the pool
446
            dbConn = 
447
                DBConnectionPool.getDBConnection("IdentifierManager.createMapping");
448
            serialNumber = dbConn.getCheckOutSerialNumber();
449

    
450
            // Execute the insert statement
451
            String query = "update systemmetadata set (docid, rev) = (?, ?) where guid='" + guid + "'";
452
            //System.out.println("query: " + query + " for params: (guid:" + guid + ", docid=" + docid + ", rev=" + rev + ")");
453
            PreparedStatement stmt = dbConn.prepareStatement(query);
454
            stmt.setString(1, docid);
455
            stmt.setInt(2, rev);
456
            int rows = stmt.executeUpdate();
457

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

    
558
            // Parse the localId into scope and rev parts
559
            AccessionNumber acc = new AccessionNumber(localId, "NOACTION");
560
            String docid = acc.getDocid();
561
            int rev = 1;
562
            if(acc.getRev() != null)
563
            {
564
              rev = (new Integer(acc.getRev()).intValue());
565
            }
566

    
567
            // Get a database connection from the pool
568
            dbConn = 
569
                DBConnectionPool.getDBConnection("IdentifierManager.createMapping");
570
            serialNumber = dbConn.getCheckOutSerialNumber();
571

    
572
            // Execute the insert statement
573
            String query = "insert into " + type + " (guid, docid, rev) values (?, ?, ?)";
574
            //System.out.println("query: " + query + " for params: (" + guid + ", " + docid + ", " + rev + ")");
575
            PreparedStatement stmt = dbConn.prepareStatement(query);
576
            stmt.setString(1, guid);
577
            stmt.setString(2, docid);
578
            stmt.setInt(3, rev);
579
            int rows = stmt.executeUpdate();
580

    
581
            stmt.close();
582
        } catch (SQLException e) {
583
            e.printStackTrace();
584
            logMetacat.error("SQL error while creating a mapping to the system metadata identifier: " 
585
                    + e.getMessage());
586
        } catch (NumberFormatException e) {
587
            e.printStackTrace();
588
            logMetacat.error("NumberFormat error while creating a mapping to the system metadata identifier: " 
589
                    + e.getMessage());
590
        } catch (AccessionNumberException e) {
591
            e.printStackTrace();
592
            logMetacat.error("AccessionNumber error while creating a mapping to the system metadata identifier: " 
593
                    + e.getMessage());
594
        } finally {
595
            // Return database connection to the pool
596
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
597
        }
598
    }
599
}
(37-37/63)