Project

General

Profile

1
/**
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
import java.io.InputStream;
27
import java.util.Date;
28
import java.util.List;
29

    
30
import org.apache.log4j.Logger;
31
import org.dataone.service.cn.CNAuthorization;
32
import org.dataone.service.cn.CNCore;
33
import org.dataone.service.cn.CNRead;
34
import org.dataone.service.cn.CNReplication;
35
import org.dataone.service.exceptions.IdentifierNotUnique;
36
import org.dataone.service.exceptions.InsufficientResources;
37
import org.dataone.service.exceptions.InvalidRequest;
38
import org.dataone.service.exceptions.InvalidSystemMetadata;
39
import org.dataone.service.exceptions.InvalidToken;
40
import org.dataone.service.exceptions.NotAuthorized;
41
import org.dataone.service.exceptions.NotFound;
42
import org.dataone.service.exceptions.NotImplemented;
43
import org.dataone.service.exceptions.ServiceFailure;
44
import org.dataone.service.exceptions.UnsupportedType;
45
import org.dataone.service.types.Checksum;
46
import org.dataone.service.types.Identifier;
47
import org.dataone.service.types.Node;
48
import org.dataone.service.types.NodeList;
49
import org.dataone.service.types.NodeReference;
50
import org.dataone.service.types.ObjectFormat;
51
import org.dataone.service.types.ObjectFormatIdentifier;
52
import org.dataone.service.types.ObjectFormatList;
53
import org.dataone.service.types.ObjectList;
54
import org.dataone.service.types.ObjectLocation;
55
import org.dataone.service.types.ObjectLocationList;
56
import org.dataone.service.types.Permission;
57
import org.dataone.service.types.QueryType;
58
import org.dataone.service.types.Replica;
59
import org.dataone.service.types.ReplicationPolicy;
60
import org.dataone.service.types.ReplicationStatus;
61
import org.dataone.service.types.Session;
62
import org.dataone.service.types.Subject;
63
import org.dataone.service.types.SystemMetadata;
64

    
65
import edu.ucsb.nceas.metacat.EventLog;
66
import edu.ucsb.nceas.metacat.IdentifierManager;
67
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
68
import edu.ucsb.nceas.metacat.replication.ForceReplicationSystemMetadataHandler;
69

    
70
/**
71
 * Represents Metacat's implementation of the DataONE Coordinating Node 
72
 * service API. Methods implement the various CN* interfaces, and methods common
73
 * to both Member Node and Coordinating Node interfaces are found in the
74
 * D1NodeService super class.
75
 *
76
 */
77
public class CNodeService extends D1NodeService implements CNAuthorization,
78
    CNCore, CNRead, CNReplication {
79

    
80
	/* the instance of the CNodeService object */
81
  private static CNodeService instance = null;
82
  
83
  /* the logger instance */
84
  private Logger logMetacat = null;
85

    
86
  /**
87
   * singleton accessor
88
   */
89
  public static CNodeService getInstance() 
90
  {
91
    if (instance == null) {
92
      instance = new CNodeService();
93
    }
94
    
95
    return instance;
96
  }
97
  
98
  /**
99
   * Constructor, private for singleton access
100
   */
101
  private CNodeService() {
102
  	super();
103
    logMetacat = Logger.getLogger(CNodeService.class);
104
        
105
  }
106
    
107
	/**
108
	 * Set the replication policy for an object given the object identifier
109
	 * 
110
	 * @param session - the Session object containing the credentials for the Subject
111
	 * @param pid - the object identifier for the given object
112
	 * @param policy - the replication policy to be applied
113
	 * 
114
	 * @return true or false
115
	 * 
116
	 * @throws NotImplemented
117
	 * @throws NotAuthorized
118
	 * @throws ServiceFailure
119
	 * @throws InvalidRequest
120
	 * 
121
	 */
122
	@Override
123
	public boolean setReplicationPolicy(Session session, Identifier pid,
124
	  ReplicationPolicy policy) 
125
	  throws NotImplemented, NotFound, NotAuthorized, ServiceFailure, InvalidRequest, InvalidToken {
126

    
127
		// get the subject
128
		Subject subject = session.getSubject();
129
		// get the system metadata
130
		String guid = pid.getValue();
131
		
132
		// are we allowed to do this?
133
		if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
134
			throw new NotAuthorized("4881", Permission.CHANGE_PERMISSION + " not allowed by " + subject.getValue() + " on " + guid);	
135
		}
136
		
137
		SystemMetadata systemMetadata = null;
138
		try {
139
			systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
140
		} catch (McdbDocNotFoundException e) {
141
			throw new NotFound("4884", "No record found for: " + guid);
142
		}
143
				
144
		// set the new policy
145
		systemMetadata.setReplicationPolicy(policy);
146
		
147
		// update the metadata
148
		try {
149
			IdentifierManager.getInstance().updateSystemMetadata(systemMetadata);
150
		} catch (McdbDocNotFoundException e) {
151
			throw new ServiceFailure("4882", e.getMessage());
152
		}
153

    
154
		return true;
155
	}
156

    
157
	/**
158
	 * Set the replication status for an object given the object identifier
159
	 * 
160
	 * @param session - the Session object containing the credentials for the Subject
161
	 * @param pid - the object identifier for the given object
162
	 * @param status - the replication status to be applied
163
	 * 
164
	 * @return true or false
165
	 * 
166
	 * @throws NotImplemented
167
	 * @throws NotAuthorized
168
	 * @throws ServiceFailure
169
	 * @throws InvalidRequest
170
	 * @throws InvalidToken
171
	 * @throws NotFound
172
	 * 
173
	 */
174
	@Override
175
	public boolean setReplicationStatus(Session session, Identifier pid,
176
	  ReplicationStatus status) 
177
	  throws ServiceFailure, NotImplemented, InvalidToken, NotAuthorized, 
178
	  InvalidRequest, NotFound {
179

    
180
		// get the subject
181
		Subject subject = session.getSubject();
182
		// get the system metadata
183
		String guid = pid.getValue();
184
		
185
		// are we allowed to do this?
186
		if (!isAuthorized(session, pid, Permission.WRITE)) {
187
			throw new NotAuthorized("4720", Permission.WRITE + " not allowed by " + subject.getValue() + " on " + guid);	
188
		}
189
		
190
		SystemMetadata systemMetadata = null;
191
		try {
192
			systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
193
		} catch (McdbDocNotFoundException e) {
194
			throw new NotFound("4740", "No record found for: " + guid);
195
		}
196
				
197
		// set the status for each replica
198
		// TODO: should this method select a certain replica?
199
		List<Replica> replicas = systemMetadata.getReplicaList();
200
		for (Replica replica: replicas) {
201
			replica.setReplicationStatus(status);
202
		}
203
		
204
		// [re]set the list -- redundant?
205
		systemMetadata.setReplicaList(replicas);
206
		
207
		// update the metadata
208
		try {
209
			IdentifierManager.getInstance().updateSystemMetadata(systemMetadata);
210
		} catch (McdbDocNotFoundException e) {
211
			throw new ServiceFailure("4700", e.getMessage());
212
		}
213

    
214
		return true;
215
	}
216

    
217
	/**
218
	 * Test that the specified relationship between pidOfSubject and pidOfObject exists
219
	 * 
220
	 * @param session - the Session object containing the credentials for the Subject
221
	 * @param node - the node information for the given node be modified
222
	 * 
223
	 * @return true if the relationship exists
224
	 * 
225
	 * @throws InvalidToken
226
	 * @throws ServiceFailure
227
	 * @throws NotAuthorized
228
	 * @throws NotFound
229
	 * @throws InvalidRequest
230
	 * @throws NotImplemented
231
	 */
232
	@Override
233
	public boolean assertRelation(Session session, Identifier pidOfSubject, 
234
		String relationship, Identifier pidOfObject) 
235
	  throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
236
	  InvalidRequest, NotImplemented {
237
		
238
		
239
		// get the system metadata
240
		String guid1 = pidOfSubject.getValue();
241
		// are we allowed to do this?
242
		if (!isAuthorized(session, pidOfSubject, Permission.READ)) {
243
			throw new NotAuthorized("4881", Permission.READ + " not allowed on " + guid1);	
244
		}
245
		
246
		SystemMetadata systemMetadata = null;
247
		try {
248
			systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid1);
249
		} catch (McdbDocNotFoundException e) {
250
			throw new NotFound("4884", "No record found for: " + guid1);
251
		}
252
				
253
		// check relationships
254
		// TODO: make this more strongly typed?
255
		if (relationship.equalsIgnoreCase("describes")) {
256
			return systemMetadata.getDescribeList().contains(pidOfObject);
257
		}
258
		if (relationship.equalsIgnoreCase("describedBy")) {
259
			return systemMetadata.getDescribedByList().contains(pidOfObject);
260
		}
261
		if (relationship.equalsIgnoreCase("derivedFrom")) {
262
			return systemMetadata.getDerivedFromList().contains(pidOfObject);
263
		}
264
		if (relationship.equalsIgnoreCase("obsoletes")) {
265
			return systemMetadata.getObsoleteList().contains(pidOfObject);
266
		}
267
		if (relationship.equalsIgnoreCase("obsoletedBy")) {
268
			return systemMetadata.getObsoletedByList().contains(pidOfObject);
269
		}
270

    
271
		return false;
272
	}
273
	
274
	/**
275
	 * Return the checksum of the object given the identifier 
276
	 * 
277
	 * @param session - the Session object containing the credentials for the Subject
278
	 * @param pid - the object identifier for the given object
279
	 * 
280
	 * @return checksum - the checksum of the object
281
	 * 
282
	 * @throws InvalidToken
283
	 * @throws ServiceFailure
284
	 * @throws NotAuthorized
285
	 * @throws NotFound
286
	 * @throws InvalidRequest
287
	 * @throws NotImplemented
288
	 */
289
	@Override
290
	public Checksum getChecksum(Session session, Identifier pid)
291
	  throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
292
	  InvalidRequest, NotImplemented {
293
		
294
		if (!isAuthorized(session, pid, Permission.READ)) {
295
			throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());	
296
		}
297
		SystemMetadata systemMetadata = null;
298
		try {
299
			systemMetadata = IdentifierManager.getInstance().getSystemMetadata(pid.getValue());
300
		} catch (McdbDocNotFoundException e) {
301
			throw new NotFound("1420", "No record found for: " + pid.getValue());
302
		}
303
		Checksum checksum = systemMetadata.getChecksum();
304
		
305
		return checksum;
306
	}
307

    
308
	/**
309
	 * Resolve the location of a given object
310
	 * 
311
	 * @param session - the Session object containing the credentials for the Subject
312
	 * @param pid - the object identifier for the given object
313
	 * 
314
	 * @return objectLocationList - the list of nodes known to contain the object
315
	 * 
316
	 * @throws InvalidRequest
317
	 * @throws InvalidToken
318
	 * @throws ServiceFailure
319
	 * @throws NotAuthorized
320
	 * @throws NotFound
321
	 * @throws NotImplemented
322
	 */
323
	@Override
324
	public ObjectLocationList resolve(Session session, Identifier pid)
325
	  throws InvalidRequest, InvalidToken, ServiceFailure, NotAuthorized,
326
	  NotFound, NotImplemented {
327

    
328
		ObjectLocationList objLocList = new ObjectLocationList();
329
		objLocList.setIdentifier(pid);
330
		
331
		// get the system metadata
332
		String guid = pid.getValue();
333
		
334
		// are we allowed to do this?
335
		if (!isAuthorized(session, pid, Permission.READ)) {
336
			throw new NotAuthorized("4720", Permission.READ + " not allowed on " + guid);	
337
		}
338
		
339
		SystemMetadata systemMetadata = null;
340
		try {
341
			systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
342
		} catch (McdbDocNotFoundException e) {
343
			throw new NotFound("4740", "No record found for: " + guid);
344
		}
345
			
346
		NodeList nodes = listNodes();
347
		
348
		// get the locations
349
		List<Replica> replicas = systemMetadata.getReplicaList();
350
		for (Replica replica: replicas) {
351
			NodeReference nodeReference = replica.getReplicaMemberNode();
352
			ObjectLocation objectLocation = new ObjectLocation();
353
			objectLocation.setNodeIdentifier(nodeReference);
354
			// get the node
355
			Node node = null;
356
			for (Node n: nodes.getNodeList()) {
357
				if (n.getIdentifier().equals(nodeReference)) {
358
					node = n;
359
					break;
360
				}
361
			}
362
			objectLocation.setBaseURL(node.getBaseURL());
363
			// TODO set URL
364
			// TODO set preference
365
			objLocList.addObjectLocation(objectLocation);
366
		}
367
		
368
		return objLocList;
369
	}
370

    
371
	/**
372
	 * Search the metadata catalog for identifiers that match the criteria
373
	 * 
374
	 * @param session - the Session object containing the credentials for the Subject
375
	 * @param queryType - An identifier for the type of query expression 
376
	 *                    provided in the query
377
	 * @param query -  The criteria for matching the characteristics of the 
378
	 *                 metadata objects of interest
379
	 * 
380
	 * @return objectList - the list of objects matching the criteria
381
	 * 
382
	 * @throws InvalidToken
383
	 * @throws ServiceFailure
384
	 * @throws NotAuthorized
385
	 * @throws InvalidRequest
386
	 * @throws NotImplemented
387
	 */
388
	@Override
389
	public ObjectList search(Session session, QueryType queryType, String query)
390
	  throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
391
	  NotImplemented {
392

    
393
		throw new NotImplemented("4281", "search not implemented");
394

    
395
	}
396

    
397
	/**
398
	 * Used internally within a Coordinating Node to add a new object 
399
	 * to the object store
400
	 * 
401
	 * @param session - the Session object containing the credentials for the Subject
402
	 * @param pid - The object identifier to be created
403
	 * @param object - the object bytes
404
	 * @param sysmeta - the system metadata that describes the object  
405
	 * 
406
	 * @return pid - the object identifier created
407
	 * 
408
	 * @throws InvalidToken
409
	 * @throws ServiceFailure
410
	 * @throws NotAuthorized
411
	 * @throws IdentifierNotUnique
412
	 * @throws UnsupportedType
413
	 * @throws InsufficientResources
414
	 * @throws InvalidSystemMetadata
415
	 * @throws NotImplemented
416
	 * @throws InvalidRequest
417
	 */
418
	@Override
419
	public Identifier create(Session session, Identifier pid, InputStream object,
420
	  SystemMetadata sysmeta) 
421
	  throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
422
	  UnsupportedType, InsufficientResources, InvalidSystemMetadata, 
423
	  NotImplemented, InvalidRequest {
424

    
425
		throw new NotImplemented("4890", "create not implemented");
426
	}
427
	
428
	/**
429
	 * Returns the object format registered in the DataONE Object Format 
430
	 * Vocabulary for the given format identifier
431
	 * 
432
	 * @param fmtid - the identifier of the format requested
433
	 * 
434
	 * @return objectFormat - the object format requested
435
	 * 
436
	 * @throws InvalidRequest
437
	 * @throws ServiceFailure
438
	 * @throws NotFound
439
	 * @throws InsufficientResources
440
	 * @throws NotImplemented
441
	 */
442
	@Override
443
	public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
444
	  throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources,
445
	  NotImplemented {
446
		 
447
	  	return ObjectFormatService.getInstance().getFormat(fmtid);
448
			
449
	}
450

    
451
	/**
452
   * Returns a list of all object formats registered in the DataONE Object 
453
   * Format Vocabulary
454
 	 * 
455
	 * @return objectFormatList - The list of object formats registered in 
456
	 *                            the DataONE Object Format Vocabulary
457
	 * 
458
	 * @throws InvalidRequest
459
	 * @throws ServiceFailure
460
	 * @throws NotImplemented
461
	 * @throws NotFound
462
	 * @throws InsufficientResources
463
	 */
464
	@Override
465
	public ObjectFormatList listFormats() 
466
	  throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources, 
467
	  NotImplemented {
468

    
469
		return ObjectFormatService.getInstance().listFormats();
470
	}
471

    
472
	/**
473
   * Returns a list of nodes that have been registered with the DataONE infrastructure
474
 	 * 
475
	 * @return nodeList - List of nodes from the registry
476
	 * 
477
	 * @throws ServiceFailure
478
	 * @throws NotImplemented
479
	 */
480
	@Override
481
	public NodeList listNodes() 
482
	  throws NotImplemented, ServiceFailure {
483

    
484
		throw new NotImplemented("4800", "listNodes not implemented");
485
	}
486

    
487
	/**
488
   * Provides a mechanism for adding system metadata independently of its 
489
   * associated object, such as when adding system metadata for data objects.
490
 	 * 
491
	 * @param session - the Session object containing the credentials for the Subject
492
	 * @param pid - The identifier of the object to register the system metadata against
493
	 * @param sysmeta - The system metadata to be registered
494
	 * 
495
	 * @return true if the registration succeeds
496
	 * 
497
	 * @throws NotImplemented
498
	 * @throws NotAuthorized
499
	 * @throws ServiceFailure
500
	 * @throws InvalidRequest
501
	 * @throws InvalidSystemMetadata
502
	 */
503
	@Override
504
	public boolean registerSystemMetadata(Session session, Identifier guid,
505
	  SystemMetadata sysmeta) 
506
	  throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
507
	  InvalidSystemMetadata {
508

    
509
		// TODO: control who can call this?
510
	      if (session == null) {
511
	          //TODO: many of the thrown exceptions do not use the correct error codes
512
	          //check these against the docs and correct them
513
	          throw new NotAuthorized("4861", "No Session - could not authorize for registration." +
514
	                  "  If you are not logged in, please do so and retry the request.");
515
	      }
516
	      
517
	      // verify that guid == SystemMetadata.getIdentifier()
518
	      logMetacat.debug("Comparing guid|sysmeta_guid: " + guid.getValue() + "|" + sysmeta.getIdentifier().getValue());
519
	      if (!guid.getValue().equals(sysmeta.getIdentifier().getValue())) {
520
	          throw new InvalidRequest("4863", 
521
	              "GUID in method call (" + guid.getValue() + ") does not match GUID in system metadata (" +
522
	              sysmeta.getIdentifier().getValue() + ").");
523
	      }
524

    
525
	      logMetacat.debug("Checking if identifier exists...");
526
	      // Check that the identifier does not already exist
527
	      IdentifierManager im = IdentifierManager.getInstance();
528
	      if (im.identifierExists(guid.getValue())) {
529
	          throw new InvalidRequest("4863", 
530
	              "GUID is already in use by an existing object.");
531
	      }
532

    
533
	      // insert the system metadata into the object store
534
	      logMetacat.debug("Starting to insert SystemMetadata...");
535
	      sysmeta.setDateSysMetadataModified(new Date());
536
	      try {
537
			    IdentifierManager.getInstance().createSystemMetadata(sysmeta);
538
			    IdentifierManager.getInstance().updateSystemMetadata(sysmeta);
539
			    // force replication of this record
540
			    ForceReplicationSystemMetadataHandler forceReplication = 
541
			    	new ForceReplicationSystemMetadataHandler(guid.getValue(), null);
542
		    } catch (Exception e) {
543
	          throw new ServiceFailure("4862", "Error inserting system metadata: " + e.getClass() + ": " + e.getMessage());
544
		    }
545
	      
546
	      logMetacat.debug("Returning from registerSystemMetadata");
547
	      EventLog.getInstance().log(null, session.getSubject().getValue(), guid.getValue(), "registerSystemMetadata");
548
	      return true;
549
	}
550

    
551
	/**
552
   * Given an optional scope and format, reserves and returns an identifier 
553
   * within that scope and format that is unique and will not be 
554
   * used by any other sessions. 
555
 	 * 
556
	 * @param session - the Session object containing the credentials for the Subject
557
	 * @param pid - The identifier of the object to register the system metadata against
558
	 * @param scope - An optional string to be used to qualify the scope of 
559
	 *                the identifier namespace, which is applied differently 
560
	 *                depending on the format requested. If scope is not 
561
	 *                supplied, a default scope will be used.
562
	 * @param format - The optional name of the identifier format to be used, 
563
	 * 								 drawn from a DataONE-specific vocabulary of identifier 
564
	 *                 format names, including several common syntaxes such 
565
	 *                 as DOI, LSID, UUID, and LSRN, among others. If the 
566
	 *                 format is not supplied by the caller, the CN service 
567
	 *                 will use a default identifier format, which may change 
568
	 *                 over time.
569
	 * 
570
	 * @return true if the registration succeeds
571
	 * 
572
	 * @throws InvalidToken
573
	 * @throws ServiceFailure
574
	 * @throws NotAuthorized
575
	 * @throws IdentifierNotUnique
576
	 * @throws NotImplemented
577
	 */
578
	@Override
579
	public Identifier reserveIdentifier(Session session, Identifier pid,
580
	  String scope, String format) 
581
	  throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
582
	  NotImplemented, InvalidRequest {
583

    
584
		throw new NotImplemented("4191", "reserveIdentifier not implemented");
585

    
586
	}
587

    
588
	/**
589
   * Changes ownership (RightsHolder) of the specified object to the 
590
   * subject specified by userId
591
 	 * 
592
	 * @param session - the Session object containing the credentials for the Subject
593
	 * @param pid - Identifier of the object to be modified
594
	 * @param userId - The subject that will be taking ownership of the specified object.
595
	 *
596
	 * @return pid - the identifier of the modified object
597
	 * 
598
	 * @throws ServiceFailure
599
	 * @throws InvalidToken
600
	 * @throws NotFound
601
	 * @throws NotAuthorized
602
	 * @throws NotImplemented
603
	 * @throws InvalidRequest
604
	 */	
605
	@Override
606
	public Identifier setOwner(Session session, Identifier pid, Subject userId)
607
	  throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
608
	  NotImplemented, InvalidRequest {
609
		
610
		// get the subject
611
		Subject subject = session.getSubject();
612
		// get the system metadata
613
		String guid = pid.getValue();
614
		
615
		// are we allowed to do this?
616
		if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
617
			throw new NotAuthorized("4440", "not allowed by " + subject.getValue() + " on " + guid);	
618
		}
619
		
620
		SystemMetadata systemMetadata = null;
621
		try {
622
			systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
623
		} catch (McdbDocNotFoundException e) {
624
			throw new NotFound("4460", "No record found for: " + guid);
625
		}
626
				
627
		// set the new rights holder
628
		systemMetadata.setRightsHolder(userId);
629
		
630
		// update the metadata
631
		try {
632
			IdentifierManager.getInstance().updateSystemMetadata(systemMetadata);
633
		} catch (McdbDocNotFoundException e) {
634
			throw new ServiceFailure("4490", e.getMessage());
635
		}
636

    
637
		return pid;
638
	}
639

    
640
}
(1-1/8)