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.sql.PreparedStatement;
28
import java.sql.ResultSet;
29
import java.sql.SQLException;
30

    
31
import org.apache.log4j.Logger;
32

    
33
import edu.ucsb.nceas.metacat.database.DBConnection;
34
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
35
import edu.ucsb.nceas.metacat.util.DocumentUtil;
36

    
37
/**
38
 * Manage the relationship between Metacat local identifiers (LocalIDs) that are
39
 * codified as the (docid, rev) pair with globally uniqe string identifiers
40
 * (GUIDs) that are opaque strings.  This class provides methods to manage these
41
 * identifiers, and to search for and look up LocalIDs based on their GUID and
42
 * vice versa. IdentifierManager is a singleton.
43
 * 
44
 * @author Matthew Jones
45
 */
46
public class IdentifierManager {
47
    
48
    public static final String TYPE_SYSTEM_METADATA = "systemmetadata";
49
    public static final String TYPE_IDENTIFIER = "identifier";
50
  
51
    /**
52
     * The single instance of the manager that is always returned.
53
     */
54
    private static IdentifierManager self = null;
55
    private Logger logMetacat = Logger.getLogger(EventLog.class);
56

    
57
    /**
58
     * A private constructor that initializes the class when getInstance() is
59
     * called.
60
     */
61
    private IdentifierManager()
62
    {
63
    }
64

    
65
    /**
66
     * Return the single instance of the manager after initializing it if it
67
     * wasn't previously initialized.
68
     * 
69
     * @return the single IdentifierManager instance
70
     */
71
    public static IdentifierManager getInstance()
72
    {
73
        if (self == null) {
74
            self = new IdentifierManager();
75
        }
76
        return self;
77
    }
78
    
79
    /**
80
     * Lookup a GUID from the database, and return the associated LocalID. If
81
     * the identifier is not found, throw an exception.
82
     * 
83
     * @param guid the global identifier to look up
84
     * @return String containing the corresponding LocalId
85
     * @throws McdbDocNotFoundException if the identifier is not found
86
     */
87
    public String getLocalId(String guid) throws McdbDocNotFoundException
88
    {
89
        return this.getId(guid, TYPE_IDENTIFIER);
90
    }
91
    
92
    /**
93
     * Determine if an identifier exists already, returning true if so.
94
     * 
95
     * @param guid the global identifier to look up
96
     * @return boolean true if the identifier exists
97
     */
98
    public boolean identifierExists(String guid)
99
    {
100
        boolean idExists = false;
101
        try {
102
            String id = getLocalId(guid);
103
            if (id != null) {
104
                idExists = true;
105
            }
106
        } catch (McdbDocNotFoundException e) {
107
            idExists = false;
108
        }
109
        return idExists;
110
    }
111
    
112
    /**
113
     * Create a mapping between two identifiers, one representing an 
114
     * unconstrained, globally unique string (guid), and one a localId 
115
     * in the Metacat docid format (scope.id.revision). 
116
     * This mapping allows one to find the global id (guid) from the localId.
117
     * 
118
     * @param guid the global string identifier to be mapped
119
     * @param localId the local identifier to be mapped
120
     */
121
    public void createMapping(String guid, String localId)
122
    {   
123
        createGenericMapping(guid, localId, TYPE_IDENTIFIER);
124
    }
125

    
126
    /**
127
     * Given a global identifier (guid), create a suitable local identifier that
128
     * follows Metacat's docid semantics and format (scope.id.rev), and create
129
     * a mapping between these two identifiers.  This effectively reserves both
130
     * the global and the local identifier, as they will now be present in the
131
     * identifier mapping table.  If the incoming guid has the syntax of a
132
     * Metacat docid (scope.id.rev), then simply use it.
133
     * 
134
     * @param guid the global string identifier
135
     * @param rev the revision number to be used in the localId
136
     * @return String containing the localId to be used for Metacat operations
137
     */
138
    public String generateLocalId(String guid, int rev) 
139
    {
140
        String localId = "";
141
        boolean conformsToDocidFormat = false;
142
        
143
        // Check if the guid passed in is already in docid (scope.id.rev) format
144
        try {
145
            AccessionNumber acc = new AccessionNumber(guid, "NONE");
146
            if (new Integer(acc.getRev()).intValue() > 0) {
147
                conformsToDocidFormat = true;
148
            }
149
        } catch (NumberFormatException e) {
150
            // No action needed, simply detecting invalid AccessionNumbers
151
        } catch (AccessionNumberException e) {
152
            // No action needed, simply detecting invalid AccessionNumbers
153
        } catch (SQLException e) {
154
            // No action needed, simply detecting invalid AccessionNumbers
155
        }
156
        
157
        if (conformsToDocidFormat) {
158
            // if it conforms, use it for both guid and localId
159
            localId = guid;
160
        } else {
161
            // if not, then generate a new unique localId
162
            localId = DocumentUtil.generateDocumentId(rev);
163
        }
164
        
165
        // Register this new pair in the identifier mapping table
166
        createMapping(guid, localId);
167
        
168
        return localId;
169
    }
170
    
171
    /**
172
     * given a local identifer, look up the guid.  Throw McdbDocNotFoundException
173
     * if the docid, rev is not found in the identifiers table
174
     *
175
     * @param docid the docid to look up
176
     * @param rev the revision of the docid to look up
177
     * @return String containing the mapped guid
178
     * @throws McdbDocNotFoundException if the docid, rev is not found
179
     */
180
    public String getGUID(String docid, int rev)
181
      throws McdbDocNotFoundException
182
    {
183
        String query = "select guid from identifier where docid = ? and rev = ?";
184
        String guid = null;
185
        
186
        DBConnection dbConn = null;
187
        int serialNumber = -1;
188
        try {
189
            // Get a database connection from the pool
190
            dbConn = DBConnectionPool.getDBConnection("Identifier.getGUID");
191
            serialNumber = dbConn.getCheckOutSerialNumber();
192
            
193
            // Execute the insert statement
194
            PreparedStatement stmt = dbConn.prepareStatement(query);
195
            stmt.setString(1, docid);
196
            stmt.setInt(2, rev);
197
            ResultSet rs = stmt.executeQuery();
198
            if (rs.next()) {
199
                guid = rs.getString(1);
200
                
201
            } else {
202
                throw new McdbDocNotFoundException("Document not found:" + guid);
203
            }
204
            stmt.close();
205
        } catch (SQLException e) {
206
            logMetacat.error("Error while looking up the guid: " 
207
                    + e.getMessage());
208
        } finally {
209
            // Return database connection to the pool
210
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
211
        }
212
        
213
        return guid;
214
    }
215
    
216
    /**
217
     * insert a system metadata id for a local id
218
     * 
219
     * @param guid the id to insert
220
     * @param localId the systemMetadata object to get the local id for
221
     */
222
    public void createSystemMetadataMapping(String guid, String localId)
223
    {
224
        createGenericMapping(guid, localId, TYPE_SYSTEM_METADATA);
225
    }
226
    
227
    public void updateSystemMetadataMapping(String guid, String localId)
228
    {
229
        int serialNumber = -1;
230
        DBConnection dbConn = null;
231
        try {
232
            // Parse the localId into scope and rev parts
233
            AccessionNumber acc = new AccessionNumber(localId, "NOACTION");
234
            String docid = acc.getDocid();
235
            int rev = 1;
236
            if(acc.getRev() != null)
237
            {
238
              rev = (new Integer(acc.getRev()).intValue());
239
            }
240

    
241
            // Get a database connection from the pool
242
            dbConn = 
243
                DBConnectionPool.getDBConnection("Identifier.createMapping");
244
            serialNumber = dbConn.getCheckOutSerialNumber();
245

    
246
            // Execute the insert statement
247
            String query = "update systemmetadata set (docid, rev) = (?, ?) where guid='" + guid + "'";
248
            //System.out.println("query: " + query + " for params: (guid:" + guid + ", docid=" + docid + ", rev=" + rev + ")");
249
            PreparedStatement stmt = dbConn.prepareStatement(query);
250
            stmt.setString(1, docid);
251
            stmt.setInt(2, rev);
252
            int rows = stmt.executeUpdate();
253

    
254
            stmt.close();
255
        } catch (SQLException e) {
256
            e.printStackTrace();
257
            logMetacat.error("SQL error while updating a mapping to the system metadata identifier: " 
258
                    + e.getMessage());
259
        } catch (NumberFormatException e) {
260
            e.printStackTrace();
261
            logMetacat.error("NumberFormat error while updating a mapping to the system metadata identifier: " 
262
                    + e.getMessage());
263
        } catch (AccessionNumberException e) {
264
            e.printStackTrace();
265
            logMetacat.error("AccessionNumber error while updating a mapping to the system metadata identifier: " 
266
                    + e.getMessage());
267
        } finally {
268
            // Return database connection to the pool
269
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
270
        }
271
    }
272
    
273
    
274
    /**
275
     * return the localId of a system metadata document based on its guid
276
     */
277
    public String getSystemMetadataId(String guid)
278
      throws McdbDocNotFoundException
279
    {
280
      return this.getId(guid, TYPE_SYSTEM_METADATA);
281
    }
282
    
283
    /**
284
     * return a localId based on a guid.  The type can either be 'identifier' 
285
     * to get an id from the identifier table or 'systemmetadata' to get
286
     * the identifier from the systemidentifier table
287
     */
288
    private String getId(String guid, String type)
289
      throws McdbDocNotFoundException
290
    {
291
      if(!type.equals(TYPE_IDENTIFIER) &&
292
        !type.equals(TYPE_SYSTEM_METADATA))
293
      {
294
        throw new RuntimeException("cannot lookup the identifier for type " + type +
295
          ".  Please choose 'identifier' or 'systemmetadata'.");
296
      }
297
      
298
      String db_guid = "";
299
      String docid = "";
300
      int rev = 0;
301
      
302
      String query = "select guid, docid, rev from " + type + " where guid = ?";
303
      //System.out.println("query: " + query + " for params: (" + guid + ")");
304
      
305
      DBConnection dbConn = null;
306
      int serialNumber = -1;
307
      try {
308
          // Get a database connection from the pool
309
          dbConn = DBConnectionPool.getDBConnection("Identifier.getLocalId");
310
          serialNumber = dbConn.getCheckOutSerialNumber();
311
          
312
          // Execute the insert statement
313
          PreparedStatement stmt = dbConn.prepareStatement(query);
314
          stmt.setString(1, guid);
315
          ResultSet rs = stmt.executeQuery();
316
          if (rs.next()) {
317
              db_guid = rs.getString(1);
318
              docid = rs.getString(2);
319
              rev = rs.getInt(3);
320
              assert(db_guid.equals(guid));
321
          } else {
322
              throw new McdbDocNotFoundException("Document not found:" + guid);
323
          }
324
          stmt.close();
325
      } catch (SQLException e) {
326
          logMetacat.error("Error while looking up the local identifier: " 
327
                  + e.getMessage());
328
      } finally {
329
          // Return database connection to the pool
330
          DBConnectionPool.returnDBConnection(dbConn, serialNumber);
331
      }
332
      return docid + "." + rev;
333
    }
334
    
335
    private void createGenericMapping(String guid, String localId, String type)
336
    {
337
        if(!type.equals(TYPE_IDENTIFIER) &&
338
        !type.equals(TYPE_SYSTEM_METADATA))
339
        {
340
          throw new RuntimeException("Cannot create mapping for type " + type +
341
            ".  Please choose 'identifier' or 'systemmetadata'.");
342
        }
343
        
344
        int serialNumber = -1;
345
        DBConnection dbConn = null;
346
        try {
347

    
348
            // Parse the localId into scope and rev parts
349
            AccessionNumber acc = new AccessionNumber(localId, "NOACTION");
350
            String docid = acc.getDocid();
351
            int rev = 1;
352
            if(acc.getRev() != null)
353
            {
354
              rev = (new Integer(acc.getRev()).intValue());
355
            }
356

    
357
            // Get a database connection from the pool
358
            dbConn = 
359
                DBConnectionPool.getDBConnection("Identifier.createMapping");
360
            serialNumber = dbConn.getCheckOutSerialNumber();
361

    
362
            // Execute the insert statement
363
            String query = "insert into " + type + " (guid, docid, rev) values (?, ?, ?)";
364
            //System.out.println("query: " + query + " for params: (" + guid + ", " + docid + ", " + rev + ")");
365
            PreparedStatement stmt = dbConn.prepareStatement(query);
366
            stmt.setString(1, guid);
367
            stmt.setString(2, docid);
368
            stmt.setInt(3, rev);
369
            int rows = stmt.executeUpdate();
370

    
371
            stmt.close();
372
        } catch (SQLException e) {
373
            e.printStackTrace();
374
            logMetacat.error("SQL error while creating a mapping to the system metadata identifier: " 
375
                    + e.getMessage());
376
        } catch (NumberFormatException e) {
377
            e.printStackTrace();
378
            logMetacat.error("NumberFormat error while creating a mapping to the system metadata identifier: " 
379
                    + e.getMessage());
380
        } catch (AccessionNumberException e) {
381
            e.printStackTrace();
382
            logMetacat.error("AccessionNumber error while creating a mapping to the system metadata identifier: " 
383
                    + e.getMessage());
384
        } finally {
385
            // Return database connection to the pool
386
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
387
        }
388
    }
389
}
(37-37/63)