Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: Implements a service for managing object formats
4
 *  Copyright: 2011 Regents of the University of California and the
5
 *             National Center for Ecological Analysis and Synthesis
6
 *    Authors: Christopher Jones
7
 * 
8
 *   '$Author: $'
9
 *     '$Date: $'
10
 * '$Revision: $'
11
 *
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 2 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program; if not, write to the Free Software
24
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
 */
26

    
27
package edu.ucsb.nceas.metacat.service;
28

    
29
import java.io.ByteArrayInputStream;
30
import java.io.FileInputStream;
31
import java.io.FileNotFoundException;
32
import java.io.InputStream;
33
import java.sql.SQLException;
34
import java.util.HashMap;
35
import java.util.List;
36

    
37
import edu.ucsb.nceas.metacat.DBUtil;
38
import edu.ucsb.nceas.metacat.DocumentImpl;
39
import edu.ucsb.nceas.metacat.McdbException;
40
import edu.ucsb.nceas.metacat.properties.PropertyService;
41
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
42
import edu.ucsb.nceas.metacat.shared.BaseService;
43
import edu.ucsb.nceas.metacat.shared.ServiceException;
44
import edu.ucsb.nceas.metacat.util.SystemUtil;
45
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
46

    
47
import org.apache.log4j.Logger;
48

    
49
import org.dataone.client.CNode;
50
import org.dataone.client.D1Client;
51
import org.dataone.service.exceptions.InsufficientResources;
52
import org.dataone.service.exceptions.InvalidRequest;
53
import org.dataone.service.exceptions.NotFound;
54
import org.dataone.service.exceptions.NotImplemented;
55
import org.dataone.service.exceptions.ServiceFailure;
56
import org.dataone.service.types.Identifier;
57
import org.dataone.service.types.ObjectFormat;
58
import org.dataone.service.types.ObjectFormatList;
59
import org.dataone.service.types.util.ServiceTypeUtil;
60
import org.jibx.runtime.JiBXException;
61

    
62
/**
63
 *  The ObjectFormatService manages the list of object formats registered
64
 *  within Metacat.  This includes schema types, mime types, and other
65
 *  information related to a particular format.  The service provides
66
 *  functionality for the DataONE MemberNode and CoordinatingNode components,
67
 *  with CoordinatingNodes providing the authoritative list of object formats.
68
 */
69
public class ObjectFormatService extends BaseService {
70
  
71
  /* The instance of the logging class */
72
  private static Logger logMetacat = Logger.getLogger(ObjectFormatService.class);
73
  
74
  /* The singleton instance of the object format service */
75
  private static ObjectFormatService objectFormatService = null;
76
  
77
  /* The scope of the object formats docid used as the metacat identifier */
78
  private final String OBJECT_FORMAT_DOCID = "OBJECT_FORMAT_LIST.103";
79
  
80
  /* The revision of the object formats document */
81
  private int rev;
82
  
83
  /* The separator of the object formats document */
84
  private String separator = ".";
85

    
86
  /* The accession number of the object formats document */
87
  private String accNumber = null;
88

    
89
  /* The list of object formats */
90
  private static ObjectFormatList objectFormatList = null;
91
  
92
  /* the searchable map of object formats */
93
  private static HashMap<String, ObjectFormat> objectFormatMap = 
94
    new HashMap<String, ObjectFormat>();
95
  
96
  /* A hash map of object format identifiers
97
  /* The D1 coordinating node reference object used for object format lookups */
98
  private String coordinatingNodeBaseURL = null;
99
  
100
  /* The D1 coordinating node reference object used for object format lookups */
101
  private CNode cn = null;
102

    
103
  /* The D1 node type for this instance of metacat */
104
  private String nodeType;
105
  
106
  /*
107
   * Constructor: Creates an instance of the object format service. Since
108
   * this uses a singleton pattern, use getInstance() to gain the instance.
109
   */
110
  private ObjectFormatService() {
111
    
112
    super();
113
    _serviceName="ObjectFormatService";
114
    
115
    try {
116
      doRefresh();
117
      
118
    } catch (ServiceException se) {
119
      logMetacat.debug("There was a problem creating the ObjectFormatService. " +
120
                       "The error message was: " + se.getMessage());
121
      
122
    }
123
    
124
  }
125
  
126
  /**
127
   *  Get the instance of the ObjectFormatService that has been instantiated,
128
   *  or instantiate one if it has not been already.
129
   *
130
   * @return objectFormatService - The instance of the object format service
131
   */
132
  public static ObjectFormatService getInstance(){
133
    
134
    if ( objectFormatService == null ) {
135
      
136
      objectFormatService = new ObjectFormatService();
137
      
138
    }
139
    return objectFormatService;
140
  }
141
  
142
  /**
143
   * Refreshes the object format service by manually updating the object
144
   * format document.
145
   */
146
  public void doRefresh() throws ServiceException {
147
    
148
    logMetacat.debug("ObjectFormatService.doRefresh() called.");
149
    logMetacat.info("Refreshing the object format list.");
150
    
151
    // create the in-memory list of object formats
152
    populateObjectFormatList();
153
    
154
    return;
155
  }
156
  
157
  /**
158
   * Populate the ObjectFormatService's objectFormatList. If this metacat
159
   * installation is a D1 Coordinating Node, object formats are populated
160
   * from a cached document in the Metacat database. Otherwise, if it is 
161
   * a Member Node, it attempts to get the authoritative list from the
162
   * defined Coordinating Node, failing back to a cached version.
163
   * 
164
   * @throws ServiceException
165
   */
166
  private void populateObjectFormatList() throws ServiceException {
167
    
168
    try {
169
      
170
      coordinatingNodeBaseURL = 
171
        PropertyService.getProperty("dataone.coordinatingNodeBaseURL");
172
      nodeType = 
173
        PropertyService.getProperty("dataone.nodeType");
174
      
175
      
176
    } catch ( PropertyNotFoundException pnfe ) {
177
      
178
      String message = "There was a problem finding the coordinating node " +
179
                       "URL in the properties file.  The message was: "     +
180
                       pnfe.getMessage();
181
      logMetacat.error(message);
182
      throw new ServiceException(message);
183
    }
184
    
185
    // For DataONE Member Nodes, get the authoritative list from the 
186
    // Coordinating Node, otherwise, get the cached list
187
    if ( this.nodeType.equals("mn") ) {
188
      
189
      /*
190
       *try {
191
       *  
192
       *  // try to get the authoritative list remotely
193
       *  D1Client d1Client = new D1Client(coordinatingNodeBaseURL);
194
       *  this.cn = D1Client.getCN();
195
       *  this.objectFormatList = cn.listFormats();
196
       *
197
       *} catch (ServiceFailure e) {
198
       *  
199
       *  logMetacat.debug("There was a getting the object format list from " +
200
       *    "the coordinating node. The message was: " +
201
       *    e.getMessage());
202
       *  this.getCachedList();  
203
       *  
204
       *} catch (NotImplemented e) {
205
       *  
206
       *  logMetacat.debug("There was a getting the object format list from " +
207
       *    "the coordinating node. The message was: " + e.getMessage());
208
       *  this.getCachedList();  
209
       *  
210
       *} catch (InvalidRequest e) {
211
       *  
212
       *  logMetacat.debug("There was a getting the object format list from " +
213
       *    "the coordinating node. The message was: " + e.getMessage());
214
       *  this.getCachedList();  
215
       *  
216
       *} catch (NotFound e) {
217
       *  
218
       *  logMetacat.debug("There was a getting the object format list from " +
219
       *    "the coordinating node. The message was: " + e.getMessage());
220
       *  this.getCachedList();  
221
       *  
222
       *} catch (InsufficientResources e) {
223
       *  
224
       *  logMetacat.debug("There was a getting the object format list from " +
225
       *    "the coordinating node. The message was: " + e.getMessage());
226
       *  this.getCachedList();  
227
       *  
228
       *}
229
       */
230
      
231
      // this will be replaced with external lookup code above
232
      this.getCachedList();
233
      
234
    } else if ( this.nodeType.equals("cn") ) {
235
      
236
      // get the list from the local metacat database
237
      this.getCachedList();  
238

    
239
    } else {
240
      
241
      // the node type is not recognized
242
      throw new ServiceException("An error occurred in initializing the " +
243
        "ObjectFormatService. The DataONE node type needs to be either "  +
244
        "'cn' or 'mn', but instead was: " + this.nodeType);
245
    }
246
    
247
    // index the object format list based on the format identifier
248
    int listSize = ObjectFormatService.objectFormatList.sizeObjectFormats();
249
    
250
    for (int i = 0; i < listSize; i++ ) {
251
      
252
      ObjectFormat objectFormat = 
253
        ObjectFormatService.objectFormatList.getObjectFormat(i);
254
      String identifier = objectFormat.getFmtid();
255
      ObjectFormatService.objectFormatMap.put(identifier, objectFormat);
256
      
257
    }
258
        
259

    
260
    
261
  }
262

    
263
  /**
264
   * Indicate whether or not this service is refreshable.  In this case it is,
265
   * where the ObjectFormatService can manually get the latest object format
266
   * document from Metacat.
267
   *
268
   * @return refreshable - the boolean refreshable status
269
   */
270
  public boolean refreshable() {
271
    return true;
272
    
273
  }
274
  
275
  /**
276
   * Stop the ObjectFormatService. When stopped, the service will no longer
277
   * respond to requests. Not implemented yet.
278
   */
279
  public void stop() throws ServiceException {
280
    
281
    // stub only 
282
    return;
283
  }
284
  
285
  /**
286
   * List the object formats registered with the object format service.
287
   * 
288
   * @return objectFormatList - the list of object formats
289
   */
290
  public ObjectFormatList listFormats() {
291
    
292
    return ObjectFormatService.objectFormatList;
293
    
294
  }
295
  
296
  /**
297
   * Get the object format based on the given identifier as a string.
298
   * 
299
   * @param format - the object format as a string
300
   * @return objectFormat - the ObjectFormat represented by the format string
301
   */
302
  public static ObjectFormat getFormat(String format) {
303
    
304
    ObjectFormat objectFormat = null;
305
    objectFormat = ObjectFormatService.objectFormatMap.get(format);
306
    
307
    return objectFormat;
308
    
309
  }
310
  /**
311
   * Get the object format list cached in Metacat, or on failure, via a
312
   * cached file on disk
313
   * 
314
   * @return objectFormatList - the cached object format list
315
   */
316
  private void getCachedList()
317
    throws ServiceException {
318
          
319
    try {
320
      
321
      // reset the accession number separator in case it is 
322
      // different than the default
323
      try {
324
        
325
        this.separator = PropertyService.getProperty("document.accNumSeparator");
326
        
327
      } catch ( PropertyNotFoundException pnfe ) {
328
        
329
        // use the default separator, but log the issue
330
        logMetacat.debug("There was a problem finding the document " +
331
          "separator property. The error message was: " + pnfe.getMessage());
332
      }
333
      
334
      // get the latest accession number if it is in Metacat
335
      this.rev = 
336
        DBUtil.getLatestRevisionInDocumentTable(this.OBJECT_FORMAT_DOCID);
337
      
338
      if ( this.rev != -1 ) {
339
        this.accNumber = this.OBJECT_FORMAT_DOCID + 
340
                         this.separator + 
341
                         this.rev;
342
        DocumentImpl objectFormatsDocument = new DocumentImpl(accNumber, false);
343
        ByteArrayInputStream bais = 
344
          new ByteArrayInputStream(objectFormatsDocument.toString().getBytes());
345
        ObjectFormatService.objectFormatList = 
346
          this.deserializeObjectFormatList(bais);
347

    
348
      } else {
349
        
350
        logMetacat.info("The latest revision number of "   + 
351
          this.OBJECT_FORMAT_DOCID                         +
352
          " could not be found. Reverting to the on-disk " +
353
          "object format list");
354
        try {
355
          
356
          ObjectFormatService.objectFormatList = this.getListFromDisk();
357
        
358
        } catch (ServiceException se) {
359
          
360
          throw new ServiceException(se.getMessage());
361
        
362
        }
363
  
364
      }
365
      
366
    } catch ( SQLException sqle ) {
367
      
368
      // we either have a metacat error or the docid was not inserted yet
369
      logMetacat.info("The latest revision number of "   + 
370
        this.OBJECT_FORMAT_DOCID                         +
371
        " could not be found. Reverting to the on-disk " +
372
        "object format list");
373
      
374
      try {
375
        
376
        ObjectFormatService.objectFormatList = this.getListFromDisk();
377
      
378
      } catch (ServiceException se) {
379
        
380
        throw se; 
381
        
382
      }
383
      
384
    } catch (McdbException mcdbe) {
385
      
386
      // we have a metacat doc not found error
387
      logMetacat.info( this.OBJECT_FORMAT_DOCID          +
388
        " could not be found. Reverting to the on-disk " +
389
        "object format list");
390
      
391
      try {
392
        
393
        ObjectFormatService.objectFormatList = this.getListFromDisk();
394
      
395
      } catch (ServiceException se) {
396
        
397
        throw se; 
398
        
399
      }
400
      
401

    
402
    }
403

    
404
    return;
405
    
406
  }
407

    
408
  /**
409
   * Get the object format list cached on disk as stated in the metacat
410
   * properties file.
411
   * 
412
   * @return objectFormatList - the on-disk object format list
413
   * @throws ServiceException
414
   */
415
  private ObjectFormatList getListFromDisk() 
416
    throws ServiceException {
417
    
418
    // the filesystem location of the object format list xml file
419
    String objectFormatFilePath = null;
420
    
421
    // get the object format list from disk and parse it
422
    try {
423
      objectFormatFilePath = SystemUtil.getContextDir() + "/WEB-INF/" +
424
        PropertyService.getProperty("dataone.objectFormatListName");
425
            
426
      FileInputStream fileInputStream = new FileInputStream(objectFormatFilePath);         
427

    
428
      ObjectFormatService.objectFormatList = 
429
        this.deserializeObjectFormatList(fileInputStream);
430

    
431
    } catch (PropertyNotFoundException pnfe) {
432
      
433
      logMetacat.error("The object format list file could not be parsed. " +
434
        "The error message was: " + pnfe.getMessage());
435
      throw new ServiceException(pnfe.getMessage());
436
      
437
    } catch ( FileNotFoundException fnfe ) {
438
      
439
      logMetacat.debug("There was an error reading the file " +
440
        objectFormatFilePath + ". The error was: "            +
441
        fnfe.getMessage());
442
      throw new ServiceException(fnfe.getMessage());
443
          
444
    }
445
    
446
    return ObjectFormatService.objectFormatList;
447
  }
448

    
449
  /**
450
   * Deserialize the object format list file to an object format list object
451
   * 
452
   * @param fileInputStream - the file input stream of the XML object format list file
453
   * @return objectFormatList - the ObjectFormatList object from the XML stream
454
   * @throws ServiceException
455
   */
456
  private ObjectFormatList deserializeObjectFormatList(InputStream inputStream) 
457
    throws ServiceException {
458
    
459
    ObjectFormatList objectFormatList;
460
    try {
461
      objectFormatList = (ObjectFormatList)
462
      ServiceTypeUtil.deserializeServiceType(ObjectFormatList.class, inputStream);
463
    
464
    } catch ( JiBXException jbxe ) {
465
      
466
      logMetacat.debug("There was an error deserializing the input stream. " +
467
        "The error was: " + jbxe.getMessage());
468
      jbxe.printStackTrace();
469
      throw new ServiceException(jbxe.getMessage());
470
    }
471
    
472
    return objectFormatList;
473
  }
474
  
475
}
(1-1/6)