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
    
228
    /**
229
     * return the localId of a system metadata document based on its guid
230
     */
231
    public String getSystemMetadataId(String guid)
232
      throws McdbDocNotFoundException
233
    {
234
      return this.getId(guid, TYPE_SYSTEM_METADATA);
235
    }
236
    
237
    /**
238
     * return a localId based on a guid.  The type can either be 'identifier' 
239
     * to get an id from the identifier table or 'systemmetadata' to get
240
     * the identifier from the systemidentifier table
241
     */
242
    private String getId(String guid, String type)
243
      throws McdbDocNotFoundException
244
    {
245
      if(!type.equals(TYPE_IDENTIFIER) &&
246
        !type.equals(TYPE_SYSTEM_METADATA))
247
      {
248
        throw new RuntimeException("cannot lookup the identifier for type " + type +
249
          ".  Please choose 'identifier' or 'systemmetadata'.");
250
      }
251
      
252
      String db_guid = "";
253
      String docid = "";
254
      int rev = 0;
255
      
256
      String query = "select guid, docid, rev from " + type + " where guid = ?";
257
      
258
      DBConnection dbConn = null;
259
      int serialNumber = -1;
260
      try {
261
          // Get a database connection from the pool
262
          dbConn = DBConnectionPool.getDBConnection("Identifier.getLocalId");
263
          serialNumber = dbConn.getCheckOutSerialNumber();
264
          
265
          // Execute the insert statement
266
          PreparedStatement stmt = dbConn.prepareStatement(query);
267
          stmt.setString(1, guid);
268
          ResultSet rs = stmt.executeQuery();
269
          if (rs.next()) {
270
              db_guid = rs.getString(1);
271
              docid = rs.getString(2);
272
              rev = rs.getInt(3);
273
              assert(db_guid.equals(guid));
274
          } else {
275
              throw new McdbDocNotFoundException("Document not found:" + guid);
276
          }
277
          stmt.close();
278
      } catch (SQLException e) {
279
          logMetacat.error("Error while looking up the local identifier: " 
280
                  + e.getMessage());
281
      } finally {
282
          // Return database connection to the pool
283
          DBConnectionPool.returnDBConnection(dbConn, serialNumber);
284
      }
285
      return docid + "." + rev;
286
    }
287
    
288
    private void createGenericMapping(String guid, String localId, String type)
289
    {
290
        if(!type.equals(TYPE_IDENTIFIER) &&
291
        !type.equals(TYPE_SYSTEM_METADATA))
292
        {
293
          throw new RuntimeException("Cannot create mapping for type " + type +
294
            ".  Please choose 'identifier' or 'systemmetadata'.");
295
        }
296
        
297
        int serialNumber = -1;
298
        DBConnection dbConn = null;
299
        try {
300

    
301
            // Parse the localId into scope and rev parts
302
            AccessionNumber acc = new AccessionNumber(localId, "NOACTION");
303
            String docid = acc.getDocid();
304
            int rev = (new Integer(acc.getRev()).intValue());
305

    
306
            // Get a database connection from the pool
307
            dbConn = 
308
                DBConnectionPool.getDBConnection("Identifier.createMapping");
309
            serialNumber = dbConn.getCheckOutSerialNumber();
310

    
311
            // Execute the insert statement
312
            String query = "insert into " + type + " (guid, docid, rev) values (?, ?, ?)";            
313
            PreparedStatement stmt = dbConn.prepareStatement(query);
314
            stmt.setString(1, guid);
315
            stmt.setString(2, docid);
316
            stmt.setInt(3, rev);
317
            int rows = stmt.executeUpdate();
318

    
319
            stmt.close();
320
        } catch (SQLException e) {
321
            logMetacat.error("SQL error while creating a mapping to the system metadata identifier: " 
322
                    + e.getMessage());
323
        } catch (NumberFormatException e) {
324
            logMetacat.error("NumberFormat error while creating a mapping to the system metadata identifier: " 
325
                    + e.getMessage());
326
        } catch (AccessionNumberException e) {
327
            logMetacat.error("AccessionNumber error while creating a mapping to the system metadata identifier: " 
328
                    + e.getMessage());
329
        } finally {
330
            // Return database connection to the pool
331
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
332
        }
333
    }
334
}
(37-37/62)