Project

General

Profile

1 6179 cjones
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2000-2011 Regents of the University of California and the
4
 *              National Center for Ecological Analysis and Synthesis
5
 *
6
 *   '$Author:  $'
7
 *     '$Date:  $'
8
 *
9
 * This program is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 2 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
 */
23
24
package edu.ucsb.nceas.metacat.dataone;
25
26 6228 cjones
import java.io.IOException;
27 6179 cjones
import java.io.InputStream;
28 6228 cjones
import java.security.NoSuchAlgorithmException;
29 6250 cjones
import java.sql.SQLException;
30 6351 cjones
import java.text.SimpleDateFormat;
31 6179 cjones
import java.util.Date;
32 6250 cjones
import java.util.List;
33 6179 cjones
34 6258 cjones
import org.apache.commons.io.IOUtils;
35 6179 cjones
import org.apache.log4j.Logger;
36 6332 leinfelder
import org.dataone.client.D1Client;
37
import org.dataone.client.MNode;
38 6334 leinfelder
import org.dataone.service.Constants;
39 6179 cjones
import org.dataone.service.exceptions.IdentifierNotUnique;
40
import org.dataone.service.exceptions.InsufficientResources;
41
import org.dataone.service.exceptions.InvalidRequest;
42
import org.dataone.service.exceptions.InvalidSystemMetadata;
43
import org.dataone.service.exceptions.InvalidToken;
44
import org.dataone.service.exceptions.NotAuthorized;
45
import org.dataone.service.exceptions.NotFound;
46
import org.dataone.service.exceptions.NotImplemented;
47
import org.dataone.service.exceptions.ServiceFailure;
48 6185 leinfelder
import org.dataone.service.exceptions.SynchronizationFailed;
49 6179 cjones
import org.dataone.service.exceptions.UnsupportedType;
50 6366 leinfelder
import org.dataone.service.mn.tier1.v1.MNCore;
51
import org.dataone.service.mn.tier1.v1.MNRead;
52
import org.dataone.service.mn.tier2.v1.MNAuthorization;
53
import org.dataone.service.mn.tier3.v1.MNStorage;
54
import org.dataone.service.mn.tier4.v1.MNReplication;
55
import org.dataone.service.types.v1.Checksum;
56
import org.dataone.service.types.v1.ChecksumAlgorithm;
57
import org.dataone.service.types.v1.DescribeResponse;
58
import org.dataone.service.types.v1.Event;
59
import org.dataone.service.types.v1.Group;
60
import org.dataone.service.types.v1.Identifier;
61
import org.dataone.service.types.v1.Log;
62
import org.dataone.service.types.v1.LogEntry;
63
import org.dataone.service.types.v1.MonitorInfo;
64
import org.dataone.service.types.v1.MonitorList;
65
import org.dataone.service.types.v1.Node;
66
import org.dataone.service.types.v1.NodeHealth;
67
import org.dataone.service.types.v1.NodeReference;
68
import org.dataone.service.types.v1.NodeState;
69
import org.dataone.service.types.v1.NodeType;
70
import org.dataone.service.types.v1.ObjectFormat;
71
import org.dataone.service.types.v1.ObjectFormatIdentifier;
72
import org.dataone.service.types.v1.ObjectList;
73
import org.dataone.service.types.v1.Permission;
74
import org.dataone.service.types.v1.Ping;
75
import org.dataone.service.types.v1.Schedule;
76
import org.dataone.service.types.v1.Service;
77
import org.dataone.service.types.v1.Services;
78
import org.dataone.service.types.v1.Session;
79
import org.dataone.service.types.v1.Status;
80
import org.dataone.service.types.v1.Subject;
81
import org.dataone.service.types.v1.Synchronization;
82
import org.dataone.service.types.v1.SystemMetadata;
83
import org.dataone.service.types.v1.util.ChecksumUtil;
84 6228 cjones
import org.dataone.service.types.util.ServiceTypeUtil;
85 6179 cjones
86 6250 cjones
import edu.ucsb.nceas.metacat.DocumentImpl;
87 6234 cjones
import edu.ucsb.nceas.metacat.EventLog;
88 6230 cjones
import edu.ucsb.nceas.metacat.IdentifierManager;
89 6234 cjones
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
90 6250 cjones
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
91 6260 cjones
import edu.ucsb.nceas.metacat.database.DBConnection;
92
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
93 6340 cjones
import edu.ucsb.nceas.metacat.properties.PropertyService;
94
import edu.ucsb.nceas.metacat.util.SystemUtil;
95
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
96 6230 cjones
97 6179 cjones
/**
98
 * Represents Metacat's implementation of the DataONE Member Node
99
 * service API. Methods implement the various MN* interfaces, and methods common
100
 * to both Member Node and Coordinating Node interfaces are found in the
101
 * D1NodeService base class.
102 6288 cjones
 *
103
 * Implements:
104
 * MNCore.ping()
105
 * MNCore.getLogRecords()
106
 * MNCore.getObjectStatistics()
107
 * MNCore.getOperationStatistics()
108
 * MNCore.getStatus()
109
 * MNCore.getCapabilities()
110
 * MNRead.get()
111
 * MNRead.getSystemMetadata()
112
 * MNRead.describe()
113
 * MNRead.getChecksum()
114
 * MNRead.listObjects()
115
 * MNRead.synchronizationFailed()
116
 * MNAuthorization.isAuthorized()
117
 * MNAuthorization.setAccessPolicy()
118
 * MNStorage.create()
119
 * MNStorage.update()
120
 * MNStorage.delete()
121
 * MNReplication.replicate()
122
 *
123 6179 cjones
 */
124
public class MNodeService extends D1NodeService implements MNAuthorization,
125
  MNCore, MNRead, MNReplication, MNStorage {
126
127 6259 cjones
  /* the instance of the MNodeService object */
128 6179 cjones
  private static MNodeService instance = null;
129
130
  /* the logger instance */
131
  private Logger logMetacat = null;
132
133
  /**
134 6241 cjones
   * Singleton accessor to get an instance of MNodeService.
135
   *
136
   * @return instance - the instance of MNodeService
137 6179 cjones
   */
138 6241 cjones
  public static MNodeService getInstance() {
139 6179 cjones
    if (instance == null) {
140 6241 cjones
141 6254 cjones
      instance = new MNodeService();
142 6241 cjones
143 6179 cjones
    }
144
145
    return instance;
146
  }
147
148
  /**
149
   * Constructor, private for singleton access
150
   */
151 6254 cjones
  private MNodeService() {
152 6259 cjones
    super();
153 6179 cjones
    logMetacat = Logger.getLogger(MNodeService.class);
154
155
  }
156
157 6259 cjones
  /**
158
   * Deletes an object from the Member Node, where the object is either a
159
   * data object or a science metadata object.
160
   *
161
   * @param session - the Session object containing the credentials for the Subject
162
   * @param pid - The object identifier to be deleted
163
   *
164
   * @return pid - the identifier of the object used for the deletion
165
   *
166
   * @throws InvalidToken
167
   * @throws ServiceFailure
168
   * @throws NotAuthorized
169
   * @throws NotFound
170
   * @throws NotImplemented
171
   * @throws InvalidRequest
172
   */
173 6179 cjones
  @Override
174 6259 cjones
  public Identifier delete(Session session, Identifier pid)
175 6179 cjones
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
176
    NotImplemented, InvalidRequest {
177
178 6250 cjones
    String localId = null;
179
    boolean allowed = false;
180 6334 leinfelder
    String username = Constants.PUBLIC_SUBJECT;
181
    String[] groupnames = null;
182
    if (session != null ) {
183
    	username = session.getSubject().getValue();
184
    	if (session.getSubjectList() != null) {
185
    		List<Group> groupList = session.getSubjectList().getGroupList();
186
    		if (groupList != null) {
187
    			groupnames = new String[groupList.size()];
188
    			for (int i = 0; i > groupList.size(); i++ ) {
189
    				groupnames[i] = groupList.get(i).getGroupName();
190
    			}
191
    		}
192
    	}
193 6310 cjones
    }
194 6250 cjones
195
    // do we have a valid pid?
196
    if ( pid == null || pid.getValue().trim().equals("") ) {
197
      throw new InvalidRequest("1322", "The provided identifier was invalid.");
198
    }
199
200
    // check for the existing identifier
201
    try {
202 6334 leinfelder
      localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
203 6250 cjones
204
    } catch (McdbDocNotFoundException e) {
205
      throw new InvalidRequest("1322", "The object with the provided " +
206 6259 cjones
        "identifier was not found.");
207 6250 cjones
208
    }
209
210
    // does the subject have DELETE (a D1 CHANGE_PERMISSION level) priveleges on the pid?
211
    allowed = isAuthorized(session, pid, Permission.CHANGE_PERMISSION);
212
213
    if ( allowed ) {
214
      try {
215
        // delete the document
216 6334 leinfelder
        DocumentImpl.delete(localId, username, groupnames, null);
217
        EventLog.getInstance().log(metacatUrl, username, localId, Event.DELETE.toString());
218 6254 cjones
219 6250 cjones
      } catch (McdbDocNotFoundException e) {
220
        throw new InvalidRequest("1322", "The provided identifier was invalid.");
221
222
      } catch (SQLException e) {
223
        throw new ServiceFailure("1350", "There was a problem deleting the object." +
224 6259 cjones
          "The error message was: " + e.getMessage());
225 6250 cjones
226
      } catch (InsufficientKarmaException e) {
227
        throw new NotAuthorized("1320", "The provided identity does not have " +
228
        "permission to DELETE objects on the Member Node.");
229
230
      } catch (Exception e) { // for some reason DocumentImpl throws a general Exception
231
        throw new ServiceFailure("1350", "There was a problem deleting the object." +
232 6259 cjones
            "The error message was: " + e.getMessage());
233 6250 cjones
234
      }
235
236
    } else {
237 6251 cjones
      throw new NotAuthorized("1320", "The provided identity does not have " +
238
      "permission to DELETE objects on the Member Node.");
239 6259 cjones
240 6250 cjones
    }
241
242 6259 cjones
    return pid;
243
  }
244 6179 cjones
245 6251 cjones
246 6179 cjones
  /**
247
   * Updates an existing object by creating a new object identified by
248
   * newPid on the Member Node which explicitly obsoletes the object
249
   * identified by pid through appropriate changes to the SystemMetadata
250
   * of pid and newPid
251
   *
252 6259 cjones
   * @param session - the Session object containing the credentials for the Subject
253
   * @param pid - The identifier of the object to be updated
254
   * @param object - the new object bytes
255
   * @param sysmeta - the new system metadata describing the object
256
   *
257
   * @return newPid - the identifier of the new object
258
   *
259
   * @throws InvalidToken
260
   * @throws ServiceFailure
261
   * @throws NotAuthorized
262
   * @throws NotFound
263
   * @throws NotImplemented
264
   * @throws IdentifierNotUnique
265
   * @throws UnsupportedType
266
   * @throws InsufficientResources
267
   * @throws InvalidSystemMetadata
268
   * @throws InvalidRequest
269 6179 cjones
   */
270
  @Override
271 6259 cjones
  public Identifier update(Session session, Identifier pid, InputStream object,
272
    Identifier newPid, SystemMetadata sysmeta)
273 6179 cjones
    throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique,
274
    UnsupportedType, InsufficientResources, NotFound, InvalidSystemMetadata,
275
    NotImplemented, InvalidRequest {
276 6341 leinfelder
277
	// check if the pid has been reserved
278
	try {
279
		boolean hasReservation = D1Client.getCN().hasReservation(session, pid);
280
		if (!hasReservation) {
281
			throw new NotAuthorized("", "No reservation for pid: " + pid.getValue());
282
		}
283
	} catch (NotFound e) {
284
		// okay to continue
285
	}
286 6179 cjones
287 6259 cjones
    String localId = null;
288
    boolean allowed = false;
289
    boolean isScienceMetadata = false;
290
    Subject subject = session.getSubject();
291 6334 leinfelder
292 6259 cjones
    // do we have a valid pid?
293
    if ( pid == null || pid.getValue().trim().equals("") ) {
294
      throw new InvalidRequest("1202", "The provided identifier was invalid.");
295 6258 cjones
296 6259 cjones
    }
297 6258 cjones
298 6259 cjones
    // check for the existing identifier
299
    try {
300 6334 leinfelder
      localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
301 6258 cjones
302 6259 cjones
    } catch (McdbDocNotFoundException e) {
303
      throw new InvalidRequest("1202", "The object with the provided " +
304
        "identifier was not found.");
305 6258 cjones
306 6259 cjones
    }
307 6258 cjones
308 6259 cjones
    // does the subject have WRITE ( == update) priveleges on the pid?
309
    allowed = isAuthorized(session, pid, Permission.WRITE);
310 6258 cjones
311 6259 cjones
    if ( allowed ) {
312
313
      // get the existing system metadata for the object
314
      SystemMetadata existingSysMeta = getSystemMetadata(session, pid);
315
316 6354 cjones
      // add the newPid to the obsoletedBy list for the existing sysmeta
317 6366 leinfelder
      existingSysMeta.setObsoletedBy(newPid);
318 6259 cjones
319
      // then update the existing system metadata
320
      updateSystemMetadata(existingSysMeta);
321 6354 cjones
322
      // prep the new system metadata, add pid to the affected lists
323 6366 leinfelder
      sysmeta.setObsoletes(pid);
324
      //sysmeta.addDerivedFrom(pid);
325 6354 cjones
326 6259 cjones
      // and insert the new system metadata
327
      insertSystemMetadata(sysmeta);
328
329
      isScienceMetadata = isScienceMetadata(sysmeta);
330
331
      // do we have XML metadata or a data object?
332
      if ( isScienceMetadata ) {
333
334
        // update the science metadata XML document
335
        // TODO: handle non-XML metadata/data documents (like netCDF)
336
        // TODO: don't put objects into memory using stream to string
337
        String objectAsXML = "";
338
        try {
339
          objectAsXML = IOUtils.toString(object, "UTF-8");
340
          localId = insertOrUpdateDocument(objectAsXML, newPid, session, "update");
341
          // register the newPid and the generated localId
342
          if ( newPid != null ) {
343 6334 leinfelder
        	  IdentifierManager.getInstance().createMapping(newPid.getValue(), localId);
344 6259 cjones
345
          }
346
347
        } catch (IOException e) {
348
          String msg = "The Node is unable to create the object. " +
349 6258 cjones
          "There was a problem converting the object to XML";
350 6259 cjones
          logMetacat.info(msg);
351 6258 cjones
          throw new ServiceFailure("1310", msg + ": " + e.getMessage());
352 6259 cjones
353
        }
354
355
      } else {
356
357
        // update the data object
358
        localId = insertDataObject(object, newPid, session);
359
360
      }
361
      // log the update event
362 6258 cjones
      EventLog.getInstance().log(metacatUrl, subject.getValue(), localId, "update");
363
364 6259 cjones
    } else {
365
      throw new NotAuthorized("1200", "The provided identity does not have " +
366
      "permission to UPDATE the object identified by " +
367
      pid.getValue() + " on the Member Node.");
368
369
    }
370 6254 cjones
371 6354 cjones
    return newPid;
372 6259 cjones
  }
373 6341 leinfelder
374
  public Identifier create(Session session, Identifier pid,
375
			InputStream object, SystemMetadata sysmeta) throws InvalidToken,
376
			ServiceFailure, NotAuthorized, IdentifierNotUnique,
377
			UnsupportedType, InsufficientResources, InvalidSystemMetadata,
378
			NotImplemented, InvalidRequest {
379 6179 cjones
380 6341 leinfelder
		// check if the pid has been reserved
381
		try {
382
			boolean hasReservation = D1Client.getCN().hasReservation(session, pid);
383
			if (!hasReservation) {
384
				throw new NotAuthorized("", "No reservation for pid: " + pid.getValue());
385
			}
386
		} catch (NotFound e) {
387
			// okay to continue
388
		}
389
390
		return super.create(session, pid, object, sysmeta);
391
	}
392
393 6179 cjones
  /**
394
   * Called by a Coordinating Node to request that the Member Node create a
395
   * copy of the specified object by retrieving it from another Member
396
   * Node and storing it locally so that it can be made accessible to
397
   * the DataONE system.
398
   *
399 6259 cjones
   * @param session - the Session object containing the credentials for the Subject
400
   * @param sysmeta - Copy of the CN held system metadata for the object
401
   * @param sourceNode - A reference to node from which the content should be
402
   *                     retrieved. The reference should be resolved by
403
   *                     checking the CN node registry.
404
   *
405
   * @return true if the replication succeeds
406
   *
407
   * @throws ServiceFailure
408
   * @throws NotAuthorized
409
   * @throws NotImplemented
410
   * @throws UnsupportedType
411
   * @throws InsufficientResources
412
   * @throws InvalidRequest
413 6179 cjones
   */
414
  @Override
415 6259 cjones
  public boolean replicate(Session session, SystemMetadata sysmeta,
416
    NodeReference sourceNode)
417
    throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest,
418
    InsufficientResources, UnsupportedType {
419 6179 cjones
420 6332 leinfelder
    boolean result = false;
421
422
    // TODO: check credentials
423
424
    // get the referenced object
425
    Identifier pid = sysmeta.getIdentifier();
426
427
    // get from the membernode
428
    // TODO: switch credentials for the server retrieval?
429
    MNode mn = D1Client.getMN(sourceNode);
430
    InputStream object = null;
431
432
	try {
433
		object = mn.get(session, pid);
434
	} catch (InvalidToken e) {
435
		e.printStackTrace();
436
		throw new ServiceFailure("2151", "Could not retrieve object to replicate (InvalidToken): " + e.getMessage());
437
	} catch (NotFound e) {
438
		e.printStackTrace();
439
		throw new ServiceFailure("2151", "Could not retrieve object to replicate (NotFound): " + e.getMessage());
440
	}
441
442
    // add it to local store
443
	Identifier retPid;
444
	try {
445
		retPid = create(session, pid, object, sysmeta);
446
		result = (retPid.getValue().equals(pid.getValue()));
447
	} catch (InvalidToken e) {
448
		e.printStackTrace();
449
		throw new ServiceFailure("2151", "Could not save object to local store (InvalidToken): " + e.getMessage());
450
	} catch (IdentifierNotUnique e) {
451
		e.printStackTrace();
452
		throw new ServiceFailure("2151", "Could not save object to local store (IdentifierNotUnique): " + e.getMessage());
453
	} catch (InvalidSystemMetadata e) {
454
		e.printStackTrace();
455
		throw new ServiceFailure("2151", "Could not save object to local store (InvalidSystemMetadata): " + e.getMessage());
456
	}
457
458
    return result;
459
460 6259 cjones
  }
461 6179 cjones
462
  /**
463
   * This method provides a lighter weight mechanism than
464
   * MN_read.getSystemMetadata() for a client to determine basic
465
   * properties of the referenced object.
466
   *
467 6259 cjones
   * @param session - the Session object containing the credentials for the Subject
468
   * @param pid - the identifier of the object to be described
469
   *
470
   * @return describeResponse - A set of values providing a basic description
471
   *                            of the object.
472
   *
473
   * @throws InvalidToken
474
   * @throws ServiceFailure
475
   * @throws NotAuthorized
476
   * @throws NotFound
477
   * @throws NotImplemented
478
   * @throws InvalidRequest
479 6179 cjones
   */
480
  @Override
481 6259 cjones
  public DescribeResponse describe(Session session, Identifier pid)
482
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
483
    NotImplemented, InvalidRequest {
484 6229 cjones
485 6259 cjones
    if(session == null) {
486 6229 cjones
      throw new InvalidToken("1370", "The session object is null");
487
488
    }
489
490
    if(pid == null || pid.getValue().trim().equals(""))
491
    {
492
      throw new InvalidRequest("1362", "The object identifier is null. " +
493
        "A valid identifier is required.");
494
495
    }
496
497
    SystemMetadata sysmeta = getSystemMetadata(session, pid);
498
    DescribeResponse describeResponse =
499 6259 cjones
      new DescribeResponse(sysmeta.getObjectFormat(),
500 6229 cjones
      sysmeta.getSize(), sysmeta.getDateSysMetadataModified(), sysmeta.getChecksum());
501
502
    return describeResponse;
503 6179 cjones
504 6259 cjones
  }
505 6179 cjones
506 6259 cjones
  /**
507
   * Return the object identified by the given object identifier
508
   *
509
   * @param session - the Session object containing the credentials for the Subject
510
   * @param pid - the object identifier for the given object
511
   *
512
   * @return inputStream - the input stream of the given object
513
   *
514
   * @throws InvalidToken
515
   * @throws ServiceFailure
516
   * @throws NotAuthorized
517
   * @throws InvalidRequest
518
   * @throws NotImplemented
519
   */
520 6179 cjones
  @Override
521 6259 cjones
  public InputStream get(Session session, Identifier pid)
522
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
523
    NotImplemented, InvalidRequest {
524
525
    return super.get(session, pid);
526
527
  }
528 6179 cjones
529 6259 cjones
  /**
530
   * Returns a Checksum for the specified object using an accepted hashing algorithm
531
   *
532
   * @param session - the Session object containing the credentials for the Subject
533
   * @param pid - the object identifier for the given object
534
   * @param algorithm -  the name of an algorithm that will be used to compute
535
   *                     a checksum of the bytes of the object
536
   *
537
   * @return checksum - the checksum of the given object
538
   *
539
   * @throws InvalidToken
540
   * @throws ServiceFailure
541
   * @throws NotAuthorized
542
   * @throws NotFound
543
   * @throws InvalidRequest
544
   * @throws NotImplemented
545
   */
546 6179 cjones
  @Override
547 6259 cjones
  public Checksum getChecksum(Session session, Identifier pid, String algorithm)
548
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
549
    InvalidRequest, NotImplemented {
550 6179 cjones
551 6259 cjones
    Checksum checksum = null;
552 6228 cjones
553 6259 cjones
    InputStream inputStream = get(session, pid);
554
555
    try {
556
      checksum =
557 6366 leinfelder
    	  ChecksumUtil.checksum(inputStream, ChecksumAlgorithm.convert(algorithm));
558 6259 cjones
559
    } catch (NoSuchAlgorithmException e) {
560 6228 cjones
      throw new ServiceFailure("1410", "The checksum for the object specified by " +
561
        pid.getValue() +
562
        "could not be returned due to an internal error: " +
563
        e.getMessage());
564
565
    } catch (IOException e) {
566
      throw new ServiceFailure("1410", "The checksum for the object specified by " +
567
        pid.getValue() +
568
        "could not be returned due to an internal error: " +
569
        e.getMessage());
570
571
    }
572 6259 cjones
573 6228 cjones
    if ( checksum == null ) {
574
      throw new ServiceFailure("1410", "The checksum for the object specified by " +
575
        pid.getValue() +
576
        "could not be returned.");
577
578
    }
579
580 6259 cjones
    return checksum;
581
  }
582 6179 cjones
583 6259 cjones
  /**
584
   * Return the system metadata for a given object
585
   *
586
   * @param session - the Session object containing the credentials for the Subject
587
   * @param pid - the object identifier for the given object
588
   *
589
   * @return inputStream - the input stream of the given system metadata object
590
   *
591
   * @throws InvalidToken
592
   * @throws ServiceFailure
593
   * @throws NotAuthorized
594
   * @throws NotFound
595
   * @throws InvalidRequest
596
   * @throws NotImplemented
597
   */
598 6179 cjones
  @Override
599 6259 cjones
  public SystemMetadata getSystemMetadata(Session session, Identifier pid)
600
      throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
601
      InvalidRequest, NotImplemented {
602 6179 cjones
603 6259 cjones
    return super.getSystemMetadata(session, pid);
604
  }
605 6179 cjones
606 6259 cjones
  /**
607
   * Retrieve the list of objects present on the MN that match the calling parameters
608
   *
609
   * @param session - the Session object containing the credentials for the Subject
610
   * @param startTime - Specifies the beginning of the time range from which
611
   *                    to return object (>=)
612
   * @param endTime - Specifies the beginning of the time range from which
613
   *                  to return object (>=)
614
   * @param objectFormat - Restrict results to the specified object format
615
   * @param replicaStatus - Indicates if replicated objects should be returned in the list
616
   * @param start - The zero-based index of the first value, relative to the
617
   *                first record of the resultset that matches the parameters.
618
   * @param count - The maximum number of entries that should be returned in
619
   *                the response. The Member Node may return less entries
620
   *                than specified in this value.
621
   *
622
   * @return objectList - the list of objects matching the criteria
623
   *
624
   * @throws InvalidToken
625
   * @throws ServiceFailure
626
   * @throws NotAuthorized
627
   * @throws InvalidRequest
628
   * @throws NotImplemented
629
   */
630 6179 cjones
  @Override
631 6259 cjones
  public ObjectList listObjects(Session session, Date startTime, Date endTime,
632 6337 leinfelder
    ObjectFormatIdentifier objectFormatId, Boolean replicaStatus, Integer start, Integer count)
633 6259 cjones
    throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure,
634
    InvalidToken {
635 6179 cjones
636 6259 cjones
    ObjectList objectList = null;
637
638 6300 leinfelder
    try {
639
	    objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime,
640 6337 leinfelder
	        objectFormatId, replicaStatus, start, count);
641 6300 leinfelder
    } catch (Exception e) {
642
		throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
643
	}
644 6259 cjones
645
    return objectList;
646
  }
647 6179 cjones
648 6259 cjones
  /**
649
   * Retrieve the list of objects present on the MN that match the calling parameters
650
   *
651
   * @return node - the technical capabilities of the Member Node
652
   *
653
   * @throws ServiceFailure
654
   * @throws NotAuthorized
655
   * @throws InvalidRequest
656
   * @throws NotImplemented
657
   */
658 6179 cjones
  @Override
659 6259 cjones
  public Node getCapabilities() throws NotImplemented, NotAuthorized,
660
      ServiceFailure, InvalidRequest {
661 6340 cjones
662
  	String nodeName = null;
663
    String nodeId = null;
664
    String nodeUrl = null;
665
    String nodeDesc = null;
666
    String nodeType = null;
667 6343 cjones
    String mnCoreServiceVersion = null;
668
    String mnReadServiceVersion = null;
669
    String mnAuthorizationServiceVersion = null;
670
    String mnStorageServiceVersion = null;
671
    String mnReplicationServiceVersion = null;
672 6179 cjones
673 6347 cjones
    boolean nodeSynchronize = false;
674
    boolean nodeReplicate = false;
675 6345 cjones
    boolean mnCoreServiceAvailable = false;
676
    boolean mnReadServiceAvailable = false;
677
    boolean mnAuthorizationServiceAvailable = false;
678
    boolean mnStorageServiceAvailable = false;
679
    boolean mnReplicationServiceAvailable = false;
680
681 6340 cjones
    try
682
    {
683 6347 cjones
    	// get the properties of the node based on configuration information
684 6345 cjones
      nodeId = PropertyService.getProperty("dataone.memberNodeId");
685
      nodeName = PropertyService.getProperty("dataone.nodeName");
686
      nodeUrl = SystemUtil.getContextURL() + "/d1/";
687
      nodeDesc = PropertyService.getProperty("dataone.nodeDescription");
688
      nodeType = PropertyService.getProperty("dataone.nodeType");
689 6347 cjones
      nodeSynchronize =
690
      	new Boolean(PropertyService.getProperty(
691
      		"dataone.nodeSynchronize")).booleanValue();
692
      nodeReplicate =
693
      	new Boolean(PropertyService.getProperty(
694
      		"dataone.nodeReplicate")).booleanValue();
695
696 6345 cjones
      mnCoreServiceVersion =
697 6351 cjones
      	PropertyService.getProperty("dataone.mnCore.serviceVersion");
698 6345 cjones
      mnReadServiceVersion =
699 6351 cjones
      	PropertyService.getProperty("dataone.mnRead.serviceVersion");
700 6345 cjones
      mnAuthorizationServiceVersion =
701 6351 cjones
      	PropertyService.getProperty("dataone.mnAuthorization.serviceVersion");
702 6345 cjones
      mnStorageServiceVersion =
703 6351 cjones
      	PropertyService.getProperty("dataone.mnStorage.serviceVersion");
704 6345 cjones
      mnReplicationServiceVersion =
705 6351 cjones
      	PropertyService.getProperty("dataone.mnReplication.serviceVersion");
706 6345 cjones
707
      mnCoreServiceAvailable = new Boolean(
708 6351 cjones
      	PropertyService.getProperty("dataone.mnCore.serviceAvailable")).booleanValue();
709 6345 cjones
      mnReadServiceAvailable =  new Boolean(
710
      	PropertyService.getProperty(
711 6351 cjones
      		"dataone.mnRead.serviceAvailable")).booleanValue();
712 6345 cjones
      mnAuthorizationServiceAvailable =  new Boolean(
713
      	PropertyService.getProperty(
714 6351 cjones
      		"dataone.mnAuthorization.serviceAvailable")).booleanValue();
715 6345 cjones
      mnStorageServiceAvailable =  new Boolean(
716
      	PropertyService.getProperty(
717 6351 cjones
      	  "dataone.mnStorage.serviceAvailable")).booleanValue();
718 6345 cjones
      mnReplicationServiceAvailable =  new Boolean(
719
      	PropertyService.getProperty(
720 6351 cjones
      	  "dataone.mnReplication.serviceAvailable")).booleanValue();
721 6345 cjones
722 6340 cjones
    } catch(PropertyNotFoundException pnfe) {
723
        logMetacat.error("MNodeService.getCapabilities(): " +
724
          "property not found: " + pnfe.getMessage());
725
726
    }
727
728 6351 cjones
  	// Set the properties of the node based on configuration information and
729
    // calls to current status methods
730 6330 leinfelder
	  Node node = new Node();
731 6340 cjones
	  node.setBaseURL(metacatUrl + "/" + nodeType);
732
	  node.setDescription(nodeDesc);
733 6351 cjones
734
	  // set the node's health information
735 6330 leinfelder
	  NodeHealth health = new NodeHealth();
736 6351 cjones
	  NodeState state = NodeState.UP;
737
	  health.setState(state);
738
	  // set the ping response to the current value
739
	  Ping canPing = new Ping();
740
	  canPing.setSuccess(false);
741
	  try {
742
	    canPing.setSuccess(ping());
743
    } catch (InsufficientResources e) {
744
	    e.printStackTrace();
745
746
    } catch (UnsupportedType e) {
747
	    e.printStackTrace();
748
749
    }
750
	  health.setPing(canPing);
751
	  // TODO: getStatus() should be consulted here when it's implemented
752
	  Status nodeStatus = new Status();
753
	  nodeStatus.setSuccess(true);
754
	  nodeStatus.setDateChecked(new Date());
755
	  health.setStatus(nodeStatus);
756 6340 cjones
	  node.setHealth(health);
757 6351 cjones
758 6330 leinfelder
	  NodeReference identifier = new NodeReference();
759 6340 cjones
	  identifier.setValue(nodeId);
760 6330 leinfelder
	  node.setIdentifier(identifier);
761 6340 cjones
	  node.setName(nodeName + " -- WAR version WARVERSION");
762 6347 cjones
	  node.setReplicate(new Boolean(nodeReplicate).booleanValue());
763
	  node.setSynchronize(new Boolean(nodeSynchronize).booleanValue());
764
765 6330 leinfelder
	  // services: MNAuthorization, MNCore, MNRead, MNReplication, MNStorage
766
	  Services services = new Services();
767 6351 cjones
768
	  Service sMNCore = new Service();
769
	  sMNCore.setName("MNCore");
770
	  sMNCore.setVersion(mnCoreServiceVersion);
771
	  sMNCore.setAvailable(mnCoreServiceAvailable);
772
773 6330 leinfelder
	  Service sMNRead = new Service();
774
	  sMNRead.setName("MNRead");
775 6343 cjones
	  sMNRead.setVersion(mnReadServiceVersion);
776 6345 cjones
	  sMNRead.setAvailable(mnReadServiceAvailable);
777 6347 cjones
778 6330 leinfelder
	  Service sMNAuthorization = new Service();
779
	  sMNAuthorization.setName("MNAuthorization");
780 6343 cjones
	  sMNAuthorization.setVersion(mnAuthorizationServiceVersion);
781 6345 cjones
	  sMNAuthorization.setAvailable(mnAuthorizationServiceAvailable);
782 6347 cjones
783 6330 leinfelder
	  Service sMNStorage = new Service();
784
	  sMNStorage.setName("MNStorage");
785 6343 cjones
	  sMNStorage.setVersion(mnStorageServiceVersion);
786 6345 cjones
	  sMNStorage.setAvailable(mnStorageServiceAvailable);
787 6347 cjones
788 6330 leinfelder
	  Service sMNReplication = new Service();
789
	  sMNReplication.setName("MNReplication");
790 6343 cjones
	  sMNReplication.setVersion(mnReplicationServiceVersion);
791 6345 cjones
	  sMNReplication.setAvailable(mnReplicationServiceAvailable);
792 6347 cjones
793 6330 leinfelder
	  services.addService(sMNRead);
794
	  services.addService(sMNCore);
795
	  services.addService(sMNAuthorization);
796
	  services.addService(sMNStorage);
797
	  services.addService(sMNReplication);
798 6340 cjones
	  node.setServices(services);
799 6347 cjones
800 6351 cjones
	  // TODO: Determine the synchronization info without mock values
801 6330 leinfelder
	  Synchronization synchronization = new Synchronization();
802 6351 cjones
	  Schedule schedule = new Schedule();
803
	  Date now = new Date();
804
	  schedule.setYear(new SimpleDateFormat("yyyy").format(now));
805
	  schedule.setMon(new SimpleDateFormat("MM").format(now));
806
	  schedule.setMday(new SimpleDateFormat("dd").format(now));
807
	  schedule.setWday(new SimpleDateFormat("dd").format(now));
808
	  schedule.setHour(new SimpleDateFormat("HH").format(now));
809
	  schedule.setMin(new SimpleDateFormat("mm").format(now));
810
	  schedule.setSec(new SimpleDateFormat("ss").format(now));
811
	  synchronization.setSchedule(schedule);
812
	  synchronization.setLastHarvested(now);
813
	  synchronization.setLastCompleteHarvest(now);
814 6340 cjones
	  node.setSynchronization(synchronization);
815 6330 leinfelder
	  node.setSynchronize(false);
816
	  node.setType(NodeType.MN);
817
818
	  return node;
819 6259 cjones
  }
820 6179 cjones
821 6259 cjones
  /**
822
   * Returns the number of operations that have been serviced by the node
823
   * over time periods of one and 24 hours.
824
   *
825
   * @param session - the Session object containing the credentials for the Subject
826
   * @param period - An ISO8601 compatible DateTime range specifying the time
827
   *                 range for which to return operation statistics.
828
   * @param requestor - Limit to operations performed by given requestor identity.
829
   * @param event -  Enumerated value indicating the type of event being examined
830
   * @param format - Limit to events involving objects of the specified format
831
   *
832
   * @return the desired log records
833
   *
834
   * @throws InvalidToken
835
   * @throws ServiceFailure
836
   * @throws NotAuthorized
837
   * @throws InvalidRequest
838
   * @throws NotImplemented
839
   */
840 6179 cjones
  @Override
841 6298 cjones
  public MonitorList getOperationStatistics(Session session, Date startTime,
842
  		Date endTime, Subject requestor, Event event, ObjectFormatIdentifier formatId)
843 6179 cjones
    throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest,
844
    InsufficientResources, UnsupportedType {
845 6298 cjones
846 6331 leinfelder
	  MonitorList monitorList = new MonitorList();
847
848
	  try {
849
850
		  // get log records first
851
		  Log logs = getLogRecords(session, startTime, endTime, event, 0, null);
852
853
		  // TODO: aggregate by day or hour -- needs clarification
854
		  int count = 1;
855
		  for (LogEntry logEntry: logs.getLogEntryList()) {
856
			  Identifier pid = logEntry.getIdentifier();
857
			  Date logDate = logEntry.getDateLogged();
858
			  // if we are filtering by format
859
			  if (formatId != null) {
860
				  SystemMetadata sysmeta = IdentifierManager.getInstance().getSystemMetadata(pid.getValue());
861
				  if (!sysmeta.getObjectFormat().getFmtid().getValue().equals(formatId.getValue())) {
862
					  // does not match
863
					  continue;
864
				  }
865
			  }
866
			  MonitorInfo item = new MonitorInfo();
867
			  item.setCount(count);
868
			  item.setDate(new java.sql.Date(logDate.getTime()));
869
			  monitorList.addMonitorInfo(item);
870
871
		  }
872
	} catch (Exception e) {
873
		e.printStackTrace();
874
		throw new ServiceFailure("2081", "Could not retrieve statistics: " + e.getMessage());
875
	}
876
877
	return monitorList;
878
879 6259 cjones
  }
880 6179 cjones
881
  /**
882
   * Low level “are you alive” operation. A valid ping response is
883
   * indicated by a HTTP status of 200.
884
   *
885
   * @return true if the service is alive
886
   *
887 6259 cjones
   * @throws InvalidToken
888
   * @throws ServiceFailure
889
   * @throws NotAuthorized
890
   * @throws InvalidRequest
891
   * @throws NotImplemented
892 6179 cjones
   */
893 6259 cjones
  @Override
894
  public boolean ping()
895
    throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest,
896
    InsufficientResources, UnsupportedType {
897 6179 cjones
898 6260 cjones
    // test if we can get a database connection
899
    boolean alive = false;
900
    int serialNumber = -1;
901
    DBConnection dbConn = null;
902
    try {
903
      dbConn = DBConnectionPool
904
      .getDBConnection("MNodeService.ping");
905
      serialNumber = dbConn.getCheckOutSerialNumber();
906
      alive = true;
907
908
    } catch (SQLException e) {
909
      return alive;
910
911
    } finally {
912
      // Return the database connection
913
      DBConnectionPool.returnDBConnection(dbConn, serialNumber);
914
915
    }
916
917
    return alive;
918 6259 cjones
  }
919 6179 cjones
920 6213 cjones
  /**
921
   * A callback method used by a CN to indicate to a MN that it cannot
922 6234 cjones
   * complete synchronization of the science metadata identified by pid.  Log
923
   * the event in the metacat event log.
924 6213 cjones
   *
925
   * @param session
926
   * @param syncFailed
927 6259 cjones
   *
928
   * @throws ServiceFailure
929
   * @throws NotAuthorized
930
   * @throws InvalidRequest
931
   * @throws NotImplemented
932 6213 cjones
   */
933 6259 cjones
  @Override
934 6213 cjones
  public void synchronizationFailed(Session session, SynchronizationFailed syncFailed)
935
      throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest {
936 6179 cjones
937 6259 cjones
    String localId;
938
939
    try {
940 6366 leinfelder
      localId = IdentifierManager.getInstance().getLocalId(syncFailed.getPid());
941 6234 cjones
    } catch (McdbDocNotFoundException e) {
942
      throw new ServiceFailure("2161", "The identifier specified by " +
943 6366 leinfelder
          syncFailed.getPid() +
944 6259 cjones
          " was not found on this node.");
945 6234 cjones
946
    }
947 6259 cjones
    // TODO: update the CN URL below when the CNRead.SynchronizationFailed
948 6234 cjones
    // method is changed to include the URL as a parameter
949
    logMetacat.debug("Synchronization for the object identified by " +
950 6366 leinfelder
      syncFailed.getPid() +
951 6259 cjones
      " failed from " +
952
      "CN URL WILL GO HERE." +
953
      " Logging the event to the Metacat EventLog as a 'syncFailed' event.");
954 6288 cjones
    // TODO: use the event type enum when the SYNCHRONIZATION_FAILED event is added
955 6234 cjones
    EventLog.getInstance().log("CN URL WILL GO HERE",
956 6288 cjones
      session.getSubject().getValue(), localId, "synchronization_failed");
957
    //EventLog.getInstance().log("CN URL WILL GO HERE",
958
    //  session.getSubject().getValue(), localId, Event.SYNCHRONIZATION_FAILED);
959 6234 cjones
960 6213 cjones
  }
961 6185 leinfelder
962 6179 cjones
}