Project

General

Profile

1 5282 jones
/**
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 5377 berkley
import java.util.*;
28
29 5282 jones
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 5286 jones
import edu.ucsb.nceas.metacat.util.DocumentUtil;
38 5282 jones
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 5334 berkley
    public static final String TYPE_SYSTEM_METADATA = "systemmetadata";
51
    public static final String TYPE_IDENTIFIER = "identifier";
52 5377 berkley
    public static final String DATAONE_SM_DOCTYPE = "http://dataone.org/service/types/SystemMetadata/0.1";
53 5334 berkley
54 5282 jones
    /**
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 5378 berkley
     * 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 5441 berkley
    public Hashtable<String, Object> getDocumentInfo(String localId)
91 5378 berkley
        throws McdbDocNotFoundException
92
    {
93 5441 berkley
        Hashtable<String, Object> h = new Hashtable<String, Object>();
94 5378 berkley
        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 5441 berkley
136 5444 berkley
            String sql2 = "select principal_name, permission, perm_type, perm_order from xml_access " +
137 5441 berkley
            "where docid like '" + localId + "'";
138 5444 berkley
            System.out.println("executing sql: " + sql2);
139 5441 berkley
            PreparedStatement stmt2 = dbConn.prepareStatement(sql2);
140 5442 berkley
            rs = stmt2.executeQuery();
141 5441 berkley
            Vector accessVector = new Vector();
142 5445 berkley
            while(rs.next())
143 5441 berkley
            {
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 5444 berkley
                System.out.println("accessHash: " + accessHash.toString());
154 5441 berkley
                accessVector.add(accessHash);
155
            }
156
            h.put("access", accessVector);
157 5378 berkley
        }
158
        catch (SQLException e)
159
        {
160 5444 berkley
            e.printStackTrace();
161 5378 berkley
            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 5377 berkley
     * 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 5282 jones
     * 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 5424 berkley
        return this.getLocalId(guid, TYPE_IDENTIFIER);
312 5282 jones
    }
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 5334 berkley
        createGenericMapping(guid, localId, TYPE_IDENTIFIER);
346 5282 jones
    }
347 5453 berkley
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 5286 jones
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 5453 berkley
    public String generateLocalId(String guid, int rev, boolean isSystemMetadata)
372 5286 jones
    {
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 5453 berkley
        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 5286 jones
405
        return localId;
406
    }
407 5322 berkley
408
    /**
409
     * given a local identifer, look up the guid.  Throw McdbDocNotFoundException
410 5452 berkley
     * if the docid, rev is not found in the identifiers or systemmetadata tables
411 5322 berkley
     *
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 5451 berkley
        boolean found = false;
423 5322 berkley
424
        DBConnection dbConn = null;
425
        int serialNumber = -1;
426
        try {
427
            // Get a database connection from the pool
428 5377 berkley
            dbConn = DBConnectionPool.getDBConnection("IdentifierManager.getGUID");
429 5322 berkley
            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 5451 berkley
            if (rs.next())
437
            {
438 5322 berkley
                guid = rs.getString(1);
439 5451 berkley
            }
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 5452 berkley
                rs = stmt.executeQuery();
447 5451 berkley
                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 5322 berkley
            }
456 5451 berkley
457 5322 berkley
        } 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 5333 berkley
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 5334 berkley
        createGenericMapping(guid, localId, TYPE_SYSTEM_METADATA);
477
    }
478
479 5377 berkley
    /**
480
     * update a mapping
481
     * @param guid
482
     * @param localId
483
     */
484 5350 berkley
    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 5377 berkley
                DBConnectionPool.getDBConnection("IdentifierManager.createMapping");
501 5350 berkley
            serialNumber = dbConn.getCheckOutSerialNumber();
502
503
            // Execute the insert statement
504
            String query = "update systemmetadata set (docid, rev) = (?, ?) where guid='" + guid + "'";
505 5352 berkley
            //System.out.println("query: " + query + " for params: (guid:" + guid + ", docid=" + docid + ", rev=" + rev + ")");
506 5350 berkley
            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 5334 berkley
530
    /**
531
     * return the localId of a system metadata document based on its guid
532
     */
533 5424 berkley
    public String getSystemMetadataLocalId(String guid)
534 5334 berkley
      throws McdbDocNotFoundException
535
    {
536 5424 berkley
      return this.getLocalId(guid, TYPE_SYSTEM_METADATA);
537 5334 berkley
    }
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 5424 berkley
    private String getLocalId(String guid, String type)
545 5334 berkley
      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 5352 berkley
      //System.out.println("query: " + query + " for params: (" + guid + ")");
560 5334 berkley
561
      DBConnection dbConn = null;
562 5333 berkley
      int serialNumber = -1;
563 5334 berkley
      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 5377 berkley
    /**
592
     * create a mapping in the systemmetadata or identifier table
593
     * @param guid
594
     * @param localId
595
     * @param type
596
     */
597 5334 berkley
    private void createGenericMapping(String guid, String localId, String type)
598 5453 berkley
    {
599 5334 berkley
        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 5333 berkley
        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 5344 berkley
            int rev = 1;
614
            if(acc.getRev() != null)
615
            {
616
              rev = (new Integer(acc.getRev()).intValue());
617
            }
618 5333 berkley
619
            // Get a database connection from the pool
620
            dbConn =
621 5377 berkley
                DBConnectionPool.getDBConnection("IdentifierManager.createMapping");
622 5333 berkley
            serialNumber = dbConn.getCheckOutSerialNumber();
623
624
            // Execute the insert statement
625 5344 berkley
            String query = "insert into " + type + " (guid, docid, rev) values (?, ?, ?)";
626 5352 berkley
            //System.out.println("query: " + query + " for params: (" + guid + ", " + docid + ", " + rev + ")");
627 5333 berkley
            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 5344 berkley
            e.printStackTrace();
636 5333 berkley
            logMetacat.error("SQL error while creating a mapping to the system metadata identifier: "
637
                    + e.getMessage());
638
        } catch (NumberFormatException e) {
639 5344 berkley
            e.printStackTrace();
640 5333 berkley
            logMetacat.error("NumberFormat error while creating a mapping to the system metadata identifier: "
641
                    + e.getMessage());
642
        } catch (AccessionNumberException e) {
643 5344 berkley
            e.printStackTrace();
644 5333 berkley
            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 5282 jones
}