Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A class that gets Accession Number, check for uniqueness
4
 *             and register it into db
5
 *  Copyright: 2000 Regents of the University of California and the
6
 *             National Center for Ecological Analysis and Synthesis
7
 *    Authors: Jivka Bojilova, Matt Jones
8
 *    Release: @release@
9
 *
10
 *   '$Author: berkley $'
11
 *     '$Date: 2000-12-06 09:59:04 -0800 (Wed, 06 Dec 2000) $'
12
 * '$Revision: 582 $'
13
 */
14

    
15
package edu.ucsb.nceas.metacat;
16

    
17
import java.net.*;
18
import java.sql.*;
19

    
20
/**
21
 * (on insert of XML document)
22
 * Generates a unique Accession Number or if provided check it 
23
 * for uniqueness and register it into the db connection 
24
 * (on update or delete of XML document)
25
 * Check for existance of provided Accession Number
26
 * 
27
 */
28
public class AccessionNumber  {
29
  
30
  private Connection conn = null;
31
  private String server = null;
32
  private String defaultGlobalName = null;
33
  private String sep = null;
34
    
35
  /** 
36
   * Construct an AccessionNumber 
37
   */
38
  public AccessionNumber () 
39
         throws SQLException, ClassNotFoundException  {
40
        
41
    MetaCatUtil util = new MetaCatUtil();
42

    
43
    this.server = util.getOption("server");
44
    this.defaultGlobalName = util.getOption("defaultGlobalName");
45
    this.sep = util.getOption("accNumberSeparator");
46

    
47
    // Open a connection to the database
48
    this.conn = util.openDBConnection();
49
  }  
50

    
51
  /** 
52
   * Construct an AccessionNumber
53
   *
54
   * @param conn the db connection to read from and write Accession# to
55
   */
56
  public AccessionNumber (Connection conn) {
57
        
58
    MetaCatUtil util = new MetaCatUtil();
59

    
60
    this.server = util.getOption("server");
61
    this.defaultGlobalName = util.getOption("defaultGlobalName");
62
    this.sep = util.getOption("accNumberSeparator");
63
    this.conn = conn;
64

    
65
  }
66

    
67
  /**
68
   * Generate an Accession Number of form <sidecode>.<createdate>
69
   */
70
  public String generate(String accNumber, String action) 
71
                throws AccessionNumberException, SQLException 
72
  {
73
    try {
74
      // INSERT
75
      if ( action.equals("INSERT")) {
76

    
77
        // get a new Accession#
78
        if ( accNumber == null ) {
79
          String sidecode = getSidecode();
80
          String createdate = getSysdate();
81
          return sidecode + "." + createdate;
82

    
83
        // Accession# is not used; return it
84
        } else if ( !accNumberUsed(accNumber) ) {
85
          return accNumber;
86

    
87
        // Accession# is used; throw an exception to prevent the insertion
88
        } else {
89
          throw new AccessionNumberException
90
                    ("Accession number " + accNumber + " is already in use.");
91
        }
92

    
93
      // UPDATE or DELETE
94
      } else if ( action.equals("UPDATE") || action.equals("DELETE")) {
95

    
96
        // Accession# is not provided; throw an exception to prevent the action
97
        if ( accNumber == null ) {
98
          throw new AccessionNumberException("Accession number is required.");
99

    
100
        // Accession# is not current (not in xml_documents); throw an exception
101
        } else if ( !accNumberIsCurrent(accNumber) ) {
102
          throw new AccessionNumberException
103
          ("Document not found for Accession number " + accNumber);
104

    
105
        // Accession# is current (present in xml_documents); return it
106
        } else {
107
          return accNumber;
108
        }
109
      }
110
 
111
    } catch (SQLException e) {
112
      throw new SQLException
113
      ("Error on AccessionNumber.generate(accNumber, action): "+e.getMessage());
114
    }    
115
 
116
    // never comes here
117
    throw new
118
    AccessionNumberException("Fatal Error in accession number generation.");
119
  }
120
  
121
  // get local server name
122
  private String getSidecode()
123
  {
124
    return this.server;
125
  }
126

    
127
  // get today date from db connection
128
  private String getSysdate () throws SQLException
129
  {
130
    String sysdate;
131
    
132
    try {
133
      PreparedStatement pstmt;
134
      pstmt = conn.prepareStatement
135
              ("SELECT to_char(sysdate,'mmddyyyyhhmiss') FROM dual");
136
      pstmt.execute();
137

    
138
      ResultSet rs = pstmt.getResultSet();
139
      boolean hasRow = rs.next();
140
      sysdate= rs.getString(1);
141
      pstmt.close();
142
      
143
    } catch (SQLException e) {
144
      throw new 
145
      SQLException("Error on AccessionNumber.getSysdate(): " + e.getMessage());
146
    }
147

    
148
    return sysdate;
149
  }
150

    
151

    
152
  ///////////////////////////////////////////////////////////
153
  // THE OLD APPROACH - accession# of form globalname:localid
154
  /**
155
   * Get an Accession Number, check it for uniqueness and 
156
   * register it into db connection. If no Accession Number is
157
   * provided, generate one from the database and return it.
158
   *
159
   * @param accNumber - Accession # if provided or null if not
160
   * @param action - INSERT, UPDATE or DELETE.
161
   * When "INSERT" and accession # provided is not unique, get new one.
162
   * If it is unique, use it.
163
   * When "INSERT" and accession # is null, get a new one.
164
   * When "UPDATE", accession # is required and it is checked for existance.
165
   * When "DELETE", accession # is required and it is checked for existance.
166
   */
167
  public String gen (String accNumber, String action) 
168
         throws AccessionNumberException, SQLException   {
169
        
170
    String globalName = null;
171
    String localId = null;
172

    
173
    try {
174

    
175
      conn.setAutoCommit(true);
176

    
177
      // split the acc # in 2 parts - global name & local id
178
      if ( accNumber != null ) {
179
        globalName = getGlobalName(accNumber, sep);
180
        localId = getLocalId(accNumber, sep);
181
      }    
182

    
183
      // INSERT
184
      if ( action.equals("INSERT")) {
185
        // get a unique Acc#
186
        if ( accNumber == null ) {
187
          return put(defaultGlobalName, null, sep);
188
        // Acc# is not used; return it
189
        } else if ( !accNumberUsed(accNumber) ) {
190
          return put(globalName, localId, sep);
191
        // Acc# is used; throw an exception to prevent the insertion
192
        } else {
193
          throw 
194
          new AccessionNumberException("Accession number is already in use.");
195
        }
196
      // UPDATE or DELETE
197
      } else if ( action.equals("UPDATE") || action.equals("DELETE")) {
198
        // Acc# is not provided; throw an exception to prevent the action
199
        if ( accNumber == null ) {
200
          throw (new AccessionNumberException("Accession number is " +
201
          "required."));
202
        // Acc# is not current (not in xml_documents); throw an exception
203
        } else if (!accNumberIsCurrent(accNumber)) {
204
          throw (new AccessionNumberException("Document " +
205
          "not found for accession #: " + accNumber));
206
        // Acc# is current; return it
207
        } else {
208
          return accNumber;
209
        }
210
      }
211
 
212
      //conn.close();        
213

    
214
    } catch (StringIndexOutOfBoundsException siobe) {
215
      MetaCatUtil.debugMessage(
216
                 "Error on AccessionNumber.generate(): " + 
217
                  siobe.getMessage());
218
      throw (new AccessionNumberException("Accession number invalid, " +
219
                 "expecting character \'" + sep + "'."));
220
    } catch (SQLException e) {
221
      throw new SQLException(
222
                 "Error on AccessionNumber.generate(accNumber, action): " +
223
                  e.getMessage());
224
    }    
225
        
226
    throw (new AccessionNumberException("Fatal Error in " +
227
           "accession number generation"));
228
  }    
229

    
230
  /** put unique accession # into db connection */
231
  private String put (String globalName, String localId, String sep) 
232
                throws SQLException
233
  {
234
        
235
    Integer l = null;
236
    try {
237
      if ( localId == null ) 
238
         l = new Integer(get(globalName) + 1); 
239
      //else if ( accNumberUsed(globalName, localId) )
240
      //   l = new Integer(get(globalName) + 1); 
241
      else
242
         l = new Integer(localId); 
243

    
244
     // insert globalName & l
245
     PreparedStatement pstmt;
246
     pstmt = conn.prepareStatement(
247
             "INSERT INTO xml_acc_numbers (global_name, local_id) " + 
248
             "VALUES (?, ?)");
249
     pstmt.setString(1,globalName);
250
     pstmt.setString(2,l.toString());
251
     pstmt.execute();
252
            
253
     pstmt.close();
254
            
255
    } catch (SQLException e) {
256
      throw new SQLException(
257
            "Error on AccessionNumber.put(globalName, localId, sep): " 
258
            + e.getMessage());
259
    }    
260

    
261
    return globalName + sep + l;
262
  }
263

    
264
  /** check for existence of Accesssion Number xml_acc_numbers table */
265
  public boolean accNumberUsed ( String accNumber )
266
                  throws SQLException {
267
        
268
    boolean hasAccNumber = false;
269
        
270
    try {
271
      PreparedStatement pstmt;
272
      pstmt = conn.prepareStatement(
273
                "SELECT 'x' FROM xml_documents " + 
274
                "WHERE docid LIKE ? " +
275
                "UNION " +
276
                "SELECT 'x' FROM xml_revisions " +
277
                "WHERE docid LIKE ?");
278
      pstmt.setString(1,accNumber);
279
      pstmt.setString(2,accNumber);
280
      pstmt.execute();
281
      ResultSet rs = pstmt.getResultSet();
282
      hasAccNumber = rs.next();
283
      pstmt.close();
284
            
285
    } catch (SQLException e) {
286
      throw new SQLException
287
      ("Error on AccessionNumber.accNumberUsed(accNumber): " + e.getMessage());
288
    }    
289
        
290
    return hasAccNumber;
291
  }    
292
    
293
  /** check for existence of Accesssion Number in xml_documents table */
294
  private boolean accNumberIsCurrent(String accNumber) throws SQLException {
295
        
296
    boolean hasCurrentAccNumber = false;
297
        
298
    try {
299
      PreparedStatement pstmt;
300
      pstmt = conn.prepareStatement(
301
                "SELECT 'x' FROM xml_documents " + 
302
                "WHERE docid LIKE ?");
303
      pstmt.setString(1, accNumber);
304
      pstmt.execute();
305
      ResultSet rs = pstmt.getResultSet();
306
      hasCurrentAccNumber = rs.next();
307
      pstmt.close();
308
            
309
    } catch (SQLException e) {
310
      throw new SQLException(
311
          "Error on AccessionNumber.accNumberIsCurrent(String accNumber): " +
312
          e.getMessage());
313
    }    
314
  
315
    return hasCurrentAccNumber;
316
  }    
317

    
318
  /** get the last in order local ID by a given global name */
319
  private int get (String globalName) throws SQLException  {
320
        
321
    try {
322
      PreparedStatement pstmt;
323
      pstmt = conn.prepareStatement(
324
              "SELECT max(local_id) FROM xml_acc_numbers " + 
325
              "WHERE global_name LIKE ?");
326
      pstmt.setString(1,globalName);
327
      pstmt.execute();
328
      ResultSet rs = pstmt.getResultSet();
329
      boolean hasLocalId = rs.next();
330

    
331
      if (hasLocalId) {
332
        return rs.getInt(1);
333
      }
334

    
335
      pstmt.close();
336
    } catch (SQLException e) {
337
      throw new SQLException(
338
            "Error on AccessionNumber.get(globalName): " + e.getMessage());
339
    }    
340
        
341
    return 0;
342
  }
343

    
344
    
345
  // get the global part of the accession number
346
  private String getGlobalName (String accNumber, String sep) 
347
          throws StringIndexOutOfBoundsException {
348
        
349
    return accNumber.substring(0, accNumber.lastIndexOf(sep));
350
  }    
351

    
352
  // get the local part of the accession number
353
  private String getLocalId (String accNumber, String sep)
354
          throws StringIndexOutOfBoundsException {
355

    
356
    return accNumber.substring(accNumber.lastIndexOf(sep)+1);
357
  }    
358
}
359

    
360
/**
361
 * '$Log$
362
 * 'Revision 1.15  2000/11/30 22:41:32  bojilova
363
 * 'change the generation of Accession# in the form of <sidecode>.<createdate>
364
 * '
365
 * 'Revision 1.14  2000/09/28 18:05:46  bojilova
366
 * 'Changed to prevent the insertion if the provided Accession# is in use as Dan suggested.
367
 * '
368
 * 'Revision 1.13  2000/09/20 20:15:58  bojilova
369
 * 'change Assession# generation to use the same db connection
370
 * '
371
 * 'Revision 1.12  2000/08/30 18:19:41  bojilova
372
 * 'cleared static methods in AccessionNumber classes for fixing bug found
373
 * 'when multiple requests to the servlet at a time.
374
 * '
375
 * 'Revision 1.11  2000/08/14 20:53:33  jones
376
 * 'Added "release" keyword to all metacat source files so that the release
377
 * 'number will be evident in software distributions.
378
 * '
379
 * 'Revision 1.10  2000/06/27 04:31:07  jones
380
 * 'Fixed bugs associated with the new UPDATE and DELETE functions of
381
 * 'DBWriter.  There were problematic interactions between some static
382
 * 'variables used in DBEntityResolver and the way in which the
383
 * 'Servlet objects are re-used across multiple client invocations.
384
 * '
385
 * 'Generally cleaned up error reporting.  Now all errors and success
386
 * 'results are reported as XML documents from MetaCatServlet.  Need
387
 * 'to make the command line tools do the same.
388
 * '
389
 * 'Revision 1.9  2000/06/26 10:35:04  jones
390
 * 'Merged in substantial changes to DBWriter and associated classes and to
391
 * 'the MetaCatServlet in order to accomodate the new UPDATE and DELETE
392
 * 'functions.  The command line tools and the parameters for the
393
 * 'servlet have changed substantially.
394
 * '
395
 * 'Revision 1.8.2.5  2000/06/26 08:38:01  jones
396
 * 'Added DELETE feature to DBWriter.  Now takes an action "DELETE" and a
397
 * 'docid and will move the record from the xml_documents table to the
398
 * 'xml_revisions table.
399
 * 'Modified option parsing to support option symbols on command line.
400
 * '
401
 * 'Revision 1.8.2.4  2000/06/25 23:11:40  jones
402
 * 'Documentation update
403
 * '
404
 * 'Revision 1.8.2.3  2000/06/25 23:08:31  jones
405
 * 'Minor change to excpetion handling
406
 * ''
407
 */
(2-2/38)