Project

General

Profile

1 6174 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 9146 tao
import java.io.ByteArrayOutputStream;
27 6226 cjones
import java.io.File;
28
import java.io.FileNotFoundException;
29
import java.io.FileOutputStream;
30
import java.io.IOException;
31 6174 cjones
import java.io.InputStream;
32 6241 cjones
import java.io.OutputStream;
33 9146 tao
import java.io.OutputStreamWriter;
34
import java.io.Writer;
35 9219 tao
import java.math.BigInteger;
36 10272 tao
import java.security.DigestOutputStream;
37
import java.security.MessageDigest;
38
import java.security.NoSuchAlgorithmException;
39 9277 tao
import java.sql.PreparedStatement;
40
import java.sql.ResultSet;
41 6226 cjones
import java.sql.SQLException;
42 6225 leinfelder
import java.util.ArrayList;
43 6186 leinfelder
import java.util.Calendar;
44 6174 cjones
import java.util.Date;
45 6226 cjones
import java.util.Hashtable;
46 9690 leinfelder
import java.util.Iterator;
47 6194 leinfelder
import java.util.List;
48 7149 leinfelder
import java.util.Set;
49 6226 cjones
import java.util.Timer;
50 9146 tao
import java.util.Vector;
51 8810 leinfelder
import java.util.concurrent.locks.Lock;
52 6174 cjones
53 6542 leinfelder
import javax.servlet.http.HttpServletRequest;
54 10272 tao
import javax.xml.bind.DatatypeConverter;
55 6226 cjones
56 6241 cjones
import org.apache.commons.io.IOUtils;
57 6186 leinfelder
import org.apache.log4j.Logger;
58 8810 leinfelder
import org.dataone.client.v2.CNode;
59
import org.dataone.client.v2.itk.D1Client;
60
import org.dataone.client.v2.formats.ObjectFormatCache;
61 9176 tao
import org.dataone.configuration.Settings;
62 7132 cjones
import org.dataone.service.exceptions.BaseException;
63 6241 cjones
import org.dataone.service.exceptions.IdentifierNotUnique;
64
import org.dataone.service.exceptions.InsufficientResources;
65 6174 cjones
import org.dataone.service.exceptions.InvalidRequest;
66 6241 cjones
import org.dataone.service.exceptions.InvalidSystemMetadata;
67 6174 cjones
import org.dataone.service.exceptions.InvalidToken;
68
import org.dataone.service.exceptions.NotAuthorized;
69
import org.dataone.service.exceptions.NotFound;
70
import org.dataone.service.exceptions.NotImplemented;
71
import org.dataone.service.exceptions.ServiceFailure;
72 6241 cjones
import org.dataone.service.exceptions.UnsupportedType;
73 6366 leinfelder
import org.dataone.service.types.v1.AccessRule;
74 10272 tao
import org.dataone.service.types.v1.Checksum;
75 6803 leinfelder
import org.dataone.service.types.v1.DescribeResponse;
76 7439 leinfelder
import org.dataone.service.types.v1.Group;
77 6366 leinfelder
import org.dataone.service.types.v1.Identifier;
78 9071 tao
import org.dataone.service.types.v1.ObjectFormatIdentifier;
79
import org.dataone.service.types.v1.ObjectList;
80 10205 tao
import org.dataone.service.types.v1.Person;
81 9997 tao
import org.dataone.service.types.v1.SubjectInfo;
82 8810 leinfelder
import org.dataone.service.types.v2.Log;
83
import org.dataone.service.types.v2.Node;
84 9146 tao
import org.dataone.service.types.v2.OptionList;
85 8810 leinfelder
import org.dataone.service.types.v1.Event;
86 6366 leinfelder
import org.dataone.service.types.v1.NodeReference;
87 6865 cjones
import org.dataone.service.types.v1.NodeType;
88 8810 leinfelder
import org.dataone.service.types.v2.ObjectFormat;
89 6366 leinfelder
import org.dataone.service.types.v1.Permission;
90 7132 cjones
import org.dataone.service.types.v1.Replica;
91 6366 leinfelder
import org.dataone.service.types.v1.Session;
92
import org.dataone.service.types.v1.Subject;
93 8810 leinfelder
import org.dataone.service.types.v2.SystemMetadata;
94 7146 leinfelder
import org.dataone.service.types.v1.util.AuthUtils;
95 6445 leinfelder
import org.dataone.service.types.v1.util.ChecksumUtil;
96 7439 leinfelder
import org.dataone.service.util.Constants;
97 6174 cjones
98 9277 tao
import edu.ucsb.nceas.metacat.AccessionNumber;
99 6241 cjones
import edu.ucsb.nceas.metacat.AccessionNumberException;
100 9146 tao
import edu.ucsb.nceas.metacat.DBTransform;
101 6241 cjones
import edu.ucsb.nceas.metacat.DocumentImpl;
102 6186 leinfelder
import edu.ucsb.nceas.metacat.EventLog;
103
import edu.ucsb.nceas.metacat.IdentifierManager;
104 6194 leinfelder
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
105 6226 cjones
import edu.ucsb.nceas.metacat.MetacatHandler;
106 7077 leinfelder
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
107 9146 tao
import edu.ucsb.nceas.metacat.common.query.stream.ContentTypeByteArrayInputStream;
108 6803 leinfelder
import edu.ucsb.nceas.metacat.database.DBConnection;
109
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
110 6447 leinfelder
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
111 8464 leinfelder
import edu.ucsb.nceas.metacat.index.MetacatSolrIndex;
112 6226 cjones
import edu.ucsb.nceas.metacat.properties.PropertyService;
113 6241 cjones
import edu.ucsb.nceas.metacat.replication.ForceReplicationHandler;
114 9146 tao
import edu.ucsb.nceas.metacat.util.SkinUtil;
115 6226 cjones
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
116 6186 leinfelder
117 6174 cjones
public abstract class D1NodeService {
118 8903 tao
119
  public static final String DELETEDMESSAGE = "The object with the PID has been deleted from the node.";
120 9387 walker
121 6226 cjones
  private static Logger logMetacat = Logger.getLogger(D1NodeService.class);
122 6186 leinfelder
123 6542 leinfelder
  /** For logging the operations */
124
  protected HttpServletRequest request;
125 6241 cjones
126 6226 cjones
  /* reference to the metacat handler */
127 6389 leinfelder
  protected MetacatHandler handler;
128 6174 cjones
129 6226 cjones
  /* parameters set in the incoming request */
130 10147 tao
  //private Hashtable<String, String[]> params;
131 7417 leinfelder
132
  /**
133 7439 leinfelder
   * limit paged results sets to a configured maximum
134
   */
135
  protected static int MAXIMUM_DB_RECORD_COUNT = 7000;
136
137
  static {
138
		try {
139
			MAXIMUM_DB_RECORD_COUNT = Integer.valueOf(PropertyService.getProperty("database.webResultsetSize"));
140
		} catch (Exception e) {
141
			logMetacat.warn("Could not set MAXIMUM_DB_RECORD_COUNT", e);
142
		}
143
	}
144
145
  /**
146 7417 leinfelder
   * out-of-band session object to be used when not passed in as a method parameter
147
   */
148
  protected Session session;
149 6226 cjones
150 6241 cjones
  /**
151
   * Constructor - used to set the metacatUrl from a subclass extending D1NodeService
152
   *
153
   * @param metacatUrl - the URL of the metacat service, including the ending /d1
154
   */
155 6542 leinfelder
  public D1NodeService(HttpServletRequest request) {
156
		this.request = request;
157
	}
158 7417 leinfelder
159 6241 cjones
  /**
160 7417 leinfelder
   * retrieve the out-of-band session
161
   * @return
162
   */
163
  	public Session getSession() {
164
		return session;
165
	}
166
167
  	/**
168
  	 * Set the out-of-band session
169
  	 * @param session
170
  	 */
171
	public void setSession(Session session) {
172
		this.session = session;
173
	}
174
175
  /**
176 6803 leinfelder
   * This method provides a lighter weight mechanism than
177
   * getSystemMetadata() for a client to determine basic
178
   * properties of the referenced object.
179
   *
180
   * @param session - the Session object containing the credentials for the Subject
181
   * @param pid - the identifier of the object to be described
182
   *
183
   * @return describeResponse - A set of values providing a basic description
184
   *                            of the object.
185
   *
186
   * @throws InvalidToken
187
   * @throws ServiceFailure
188
   * @throws NotAuthorized
189
   * @throws NotFound
190
   * @throws NotImplemented
191
   * @throws InvalidRequest
192
   */
193
  public DescribeResponse describe(Session session, Identifier pid)
194
      throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
195 9036 tao
196
      String serviceFailureCode = "4931";
197
      Identifier sid = getPIDForSID(pid, serviceFailureCode);
198
      if(sid != null) {
199
          pid = sid;
200
      }
201 6803 leinfelder
202
    // get system metadata and construct the describe response
203
      SystemMetadata sysmeta = getSystemMetadata(session, pid);
204
      DescribeResponse describeResponse =
205
      	new DescribeResponse(sysmeta.getFormatId(), sysmeta.getSize(),
206
      			sysmeta.getDateSysMetadataModified(),
207
      			sysmeta.getChecksum(), sysmeta.getSerialVersion());
208
209
      return describeResponse;
210
211
  }
212
213
  /**
214 7077 leinfelder
   * Deletes an object from the Member Node, where the object is either a
215
   * data object or a science metadata object.
216
   *
217
   * @param session - the Session object containing the credentials for the Subject
218
   * @param pid - The object identifier to be deleted
219
   *
220
   * @return pid - the identifier of the object used for the deletion
221
   *
222
   * @throws InvalidToken
223
   * @throws ServiceFailure
224
   * @throws NotAuthorized
225
   * @throws NotFound
226
   * @throws NotImplemented
227
   * @throws InvalidRequest
228
   */
229
  public Identifier delete(Session session, Identifier pid)
230
      throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
231 9050 tao
232 7077 leinfelder
      String localId = null;
233
      if (session == null) {
234
      	throw new InvalidToken("1330", "No session has been provided");
235
      }
236 7157 leinfelder
      // just for logging purposes
237
      String username = session.getSubject().getValue();
238 7077 leinfelder
239
      // do we have a valid pid?
240
      if (pid == null || pid.getValue().trim().equals("")) {
241
          throw new ServiceFailure("1350", "The provided identifier was invalid.");
242
      }
243
244
      // check for the existing identifier
245
      try {
246
          localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
247
      } catch (McdbDocNotFoundException e) {
248
          throw new NotFound("1340", "The object with the provided " + "identifier was not found.");
249 9024 tao
      } catch (SQLException e) {
250
          throw new ServiceFailure("1350", "The object with the provided " + "identifier "+pid.getValue()+" couldn't be identified since "+e.getMessage());
251 7077 leinfelder
      }
252 7157 leinfelder
253 7077 leinfelder
      try {
254 7157 leinfelder
          // delete the document, as admin
255
          DocumentImpl.delete(localId, null, null, null, true);
256
          EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), username, localId, Event.DELETE.xmlValue());
257
258
          // archive it
259 7430 leinfelder
          // DocumentImpl.delete() now sets this
260
          // see https://redmine.dataone.org/issues/3406
261
//          SystemMetadata sysMeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
262
//          sysMeta.setArchived(true);
263
//          sysMeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
264
//          HazelcastService.getInstance().getSystemMetadataMap().put(pid, sysMeta);
265 7077 leinfelder
266 7157 leinfelder
      } catch (McdbDocNotFoundException e) {
267
          throw new NotFound("1340", "The provided identifier was invalid.");
268 7077 leinfelder
269 7157 leinfelder
      } catch (SQLException e) {
270
          throw new ServiceFailure("1350", "There was a problem deleting the object." + "The error message was: " + e.getMessage());
271 7077 leinfelder
272 7157 leinfelder
      } catch (InsufficientKarmaException e) {
273 7244 cjones
          if ( logMetacat.isDebugEnabled() ) {
274
              e.printStackTrace();
275
          }
276 7157 leinfelder
          throw new NotAuthorized("1320", "The provided identity does not have " + "permission to DELETE objects on the Member Node.");
277 7244 cjones
278 7157 leinfelder
      } catch (Exception e) { // for some reason DocumentImpl throws a general Exception
279
          throw new ServiceFailure("1350", "There was a problem deleting the object." + "The error message was: " + e.getMessage());
280 7077 leinfelder
      }
281
282
      return pid;
283
  }
284
285
  /**
286 6803 leinfelder
   * Low level, "are you alive" operation. A valid ping response is
287
   * indicated by a HTTP status of 200.
288
   *
289
   * @return true if the service is alive
290
   *
291
   * @throws NotImplemented
292
   * @throws ServiceFailure
293
   * @throws InsufficientResources
294
   */
295
  public Date ping()
296
      throws NotImplemented, ServiceFailure, InsufficientResources {
297
298
      // test if we can get a database connection
299
      int serialNumber = -1;
300
      DBConnection dbConn = null;
301
      try {
302
          dbConn = DBConnectionPool.getDBConnection("MNodeService.ping");
303
          serialNumber = dbConn.getCheckOutSerialNumber();
304
      } catch (SQLException e) {
305
      	ServiceFailure sf = new ServiceFailure("", e.getMessage());
306
      	sf.initCause(e);
307
          throw sf;
308
      } finally {
309
          // Return the database connection
310
          DBConnectionPool.returnDBConnection(dbConn, serialNumber);
311
      }
312
313
      return Calendar.getInstance().getTime();
314
  }
315
316
  /**
317 6241 cjones
   * Adds a new object to the Node, where the object is either a data
318
   * object or a science metadata object. This method is called by clients
319
   * to create new data objects on Member Nodes or internally for Coordinating
320
   * Nodes
321
   *
322
   * @param session - the Session object containing the credentials for the Subject
323
   * @param pid - The object identifier to be created
324
   * @param object - the object bytes
325
   * @param sysmeta - the system metadata that describes the object
326
   *
327
   * @return pid - the object identifier created
328
   *
329
   * @throws InvalidToken
330
   * @throws ServiceFailure
331
   * @throws NotAuthorized
332
   * @throws IdentifierNotUnique
333
   * @throws UnsupportedType
334
   * @throws InsufficientResources
335
   * @throws InvalidSystemMetadata
336
   * @throws NotImplemented
337
   * @throws InvalidRequest
338
   */
339
  public Identifier create(Session session, Identifier pid, InputStream object,
340
    SystemMetadata sysmeta)
341
    throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique,
342
    UnsupportedType, InsufficientResources, InvalidSystemMetadata,
343
    NotImplemented, InvalidRequest {
344 6174 cjones
345 6241 cjones
    Identifier resultPid = null;
346
    String localId = null;
347
    boolean allowed = false;
348
349 6530 leinfelder
    // check for null session
350
    if (session == null) {
351
    	throw new InvalidToken("4894", "Session is required to WRITE to the Node.");
352
    }
353
    Subject subject = session.getSubject();
354
355 6688 leinfelder
    Subject publicSubject = new Subject();
356
    publicSubject.setValue(Constants.SUBJECT_PUBLIC);
357
	// be sure the user is authenticated for create()
358 6518 leinfelder
    if (subject == null || subject.getValue() == null ||
359 6688 leinfelder
        subject.equals(publicSubject) ) {
360 6241 cjones
      throw new NotAuthorized("1100", "The provided identity does not have " +
361 6518 leinfelder
        "permission to WRITE to the Node.");
362 6241 cjones
363
    }
364 9068 tao
365 6241 cjones
    // verify that pid == SystemMetadata.getIdentifier()
366
    logMetacat.debug("Comparing pid|sysmeta_pid: " +
367
      pid.getValue() + "|" + sysmeta.getIdentifier().getValue());
368
    if (!pid.getValue().equals(sysmeta.getIdentifier().getValue())) {
369
        throw new InvalidSystemMetadata("1180",
370
            "The supplied system metadata is invalid. " +
371
            "The identifier " + pid.getValue() + " does not match identifier" +
372
            "in the system metadata identified by " +
373
            sysmeta.getIdentifier().getValue() + ".");
374
375
    }
376 9045 tao
377 10288 tao
    if(sysmeta.getChecksum() == null) {
378
        logMetacat.error("D1NodeService.create - the checksum object from the system metadata shouldn't be null for the object "+pid.getValue());
379
        throw new InvalidSystemMetadata("1180", "The checksum object from the system metadata shouldn't be null.");
380
    }
381
382 6241 cjones
383 7122 leinfelder
    logMetacat.debug("Checking if identifier exists: " + pid.getValue());
384 6241 cjones
    // Check that the identifier does not already exist
385 9017 tao
    boolean idExists = false;
386
    try {
387
        idExists = IdentifierManager.getInstance().identifierExists(pid.getValue());
388
    } catch (SQLException e) {
389
        throw new ServiceFailure("1190",
390
                                "The requested identifier " + pid.getValue() +
391
                                " couldn't be determined if it is unique since : "+e.getMessage());
392
    }
393
    if (idExists) {
394 6278 leinfelder
	    	throw new IdentifierNotUnique("1120",
395
			          "The requested identifier " + pid.getValue() +
396
			          " is already used by another object and" +
397
			          "therefore can not be used for this object. Clients should choose" +
398
			          "a new identifier that is unique and retry the operation or " +
399 6518 leinfelder
			          "use CN.reserveIdentifier() to reserve one.");
400 6337 leinfelder
401 6241 cjones
    }
402 6518 leinfelder
403 9045 tao
404 6596 leinfelder
    // TODO: this probably needs to be refined more
405 6241 cjones
    try {
406
      allowed = isAuthorized(session, pid, Permission.WRITE);
407
408
    } catch (NotFound e) {
409
      // The identifier doesn't exist, writing should be fine.
410
      allowed = true;
411
    }
412
413 9219 tao
    if(!allowed) {
414
        throw new NotAuthorized("1100", "Provited Identity doesn't have the WRITE permission on the pid "+pid.getValue());
415
    }
416 10272 tao
417 6445 leinfelder
418 6241 cjones
    // we have the go ahead
419 9219 tao
    //if ( allowed ) {
420 10130 tao
421
422
        logMetacat.debug("Allowed to insert: " + pid.getValue());
423
424 9219 tao
        // save the sysmeta
425
        try {
426
            // lock and unlock of the pid happens in the subclass
427
            HazelcastService.getInstance().getSystemMetadataMap().put(sysmeta.getIdentifier(), sysmeta);
428
429
        } catch (Exception e) {
430 10130 tao
            logMetacat.error("D1Node.create - There was problem to save the system metadata: " + pid.getValue(), e);
431
            throw new ServiceFailure("1190", "There was problem to save the system metadata: " + pid.getValue()+" since "+e.getMessage());
432 9219 tao
        }
433 10130 tao
        boolean isScienceMetadata = false;
434 6241 cjones
      // Science metadata (XML) or science data object?
435
      // TODO: there are cases where certain object formats are science metadata
436
      // but are not XML (netCDF ...).  Handle this.
437
      if ( isScienceMetadata(sysmeta) ) {
438 10130 tao
        isScienceMetadata = true;
439 6241 cjones
        // CASE METADATA:
440 8948 tao
      	//String objectAsXML = "";
441 6241 cjones
        try {
442 8948 tao
	        //objectAsXML = IOUtils.toString(object, "UTF-8");
443 9783 leinfelder
            String formatId = null;
444
            if(sysmeta.getFormatId() != null)  {
445
                formatId = sysmeta.getFormatId().getValue();
446
            }
447 10272 tao
	        localId = insertOrUpdateDocument(object,"UTF-8", pid, session, "insert", formatId, sysmeta.getChecksum());
448 6241 cjones
	        //localId = im.getLocalId(pid.getValue());
449
450
        } catch (IOException e) {
451 10140 tao
            removeSystemMetaAndIdentifier(pid);
452 10130 tao
        	String msg = "The Node is unable to create the object "+pid.getValue() +
453
          " There was a problem converting the object to XML";
454
        	logMetacat.error(msg, e);
455 6241 cjones
          throw new ServiceFailure("1190", msg + ": " + e.getMessage());
456
457 9219 tao
        } catch (ServiceFailure e) {
458 10140 tao
            removeSystemMetaAndIdentifier(pid);
459 10130 tao
            logMetacat.error("D1NodeService.create - the node couldn't create the object "+pid.getValue()+" since "+e.getMessage(), e);
460 9219 tao
            throw e;
461
        } catch (Exception e) {
462 10140 tao
            removeSystemMetaAndIdentifier(pid);
463 10006 tao
            logMetacat.error("The node is unable to create the object: "+pid.getValue()+ " since " + e.getMessage(), e);
464
            throw new ServiceFailure("1190", "The node is unable to create the object: " +pid.getValue()+" since "+ e.getMessage());
465 6241 cjones
        }
466
467 6302 leinfelder
      } else {
468
469
	      // DEFAULT CASE: DATA (needs to be checked and completed)
470 9219 tao
          try {
471 10272 tao
              localId = insertDataObject(object, pid, session, sysmeta.getChecksum());
472 9219 tao
          } catch (ServiceFailure e) {
473 10140 tao
              removeSystemMetaAndIdentifier(pid);
474 9219 tao
              throw e;
475 10272 tao
          } catch (InvalidSystemMetadata e) {
476
              removeSystemMetaAndIdentifier(pid);
477
              throw e;
478 9219 tao
          } catch (Exception e) {
479 10140 tao
              removeSystemMetaAndIdentifier(pid);
480 10130 tao
              throw new ServiceFailure("1190", "The node is unable to create the object "+pid.getValue()+" since " + e.getMessage());
481 9219 tao
          }
482
483 6302 leinfelder
      }
484
485 9219 tao
    //}
486 6313 leinfelder
487 7122 leinfelder
    logMetacat.debug("Done inserting new object: " + pid.getValue());
488
489 10130 tao
    // setting the resulting identifier failed. We will check if the object does exist.
490
    try {
491
        if (localId == null || !IdentifierManager.getInstance().objectFileExists(localId, isScienceMetadata) ) {
492 10140 tao
            removeSystemMetaAndIdentifier(pid);
493 10130 tao
          throw new ServiceFailure("1190", "The Node is unable to create the object. "+pid.getValue());
494
        }
495
    } catch (PropertyNotFoundException e) {
496 10140 tao
        removeSystemMetaAndIdentifier(pid);
497 10130 tao
        throw new ServiceFailure("1190", "The Node is unable to create the object. "+pid.getValue() + " since "+e.getMessage());
498 6241 cjones
    }
499 10130 tao
500 9219 tao
501 10130 tao
502
503
504 9219 tao
    try {
505
        // submit for indexing
506
        MetacatSolrIndex.getInstance().submit(sysmeta.getIdentifier(), sysmeta, null, true);
507
    } catch (Exception e) {
508
        logMetacat.warn("Couldn't create solr index for object "+pid.getValue());
509
    }
510 6313 leinfelder
511 6302 leinfelder
    resultPid = pid;
512
513 10130 tao
    logMetacat.info("create() complete for object: " + pid.getValue());
514 7122 leinfelder
515 6241 cjones
    return resultPid;
516
  }
517 9219 tao
518
  /*
519
   * Roll-back method when inserting data object fails.
520
   */
521 10140 tao
  protected void removeSystemMetaAndIdentifier(Identifier id){
522 10027 tao
      if(id != null) {
523 10130 tao
          logMetacat.debug("D1NodeService.removeSystemMeta - the system metadata of object "+id.getValue()+" will removed from both hazelcast and db tables since the object creation failed");
524
          HazelcastService.getInstance().getSystemMetadataMap().remove(id);
525
          logMetacat.info("D1NodeService.removeSystemMeta - the system metadata of object "+id.getValue()+" has been removed from both hazelcast and db tables since the object creation failed");
526
          try {
527 10140 tao
              if(IdentifierManager.getInstance().mappingExists(id.getValue())) {
528
                 String localId = IdentifierManager.getInstance().getLocalId(id.getValue());
529 10130 tao
                 IdentifierManager.getInstance().removeMapping(id.getValue(), localId);
530
                 logMetacat.info("D1NodeService.removeSystemMeta - the identifier "+id.getValue()+" and local id "+localId+" have been removed from the identifier table since the object creation failed");
531
              }
532
          } catch (Exception e) {
533 10140 tao
              logMetacat.warn("D1NodeService.removeSysteMeta - can't decide if the mapping of  the pid "+id.getValue()+" exists on the identifier table.");
534 10130 tao
          }
535 10027 tao
      }
536 9219 tao
  }
537
538
  /*
539
   * Roll-back method when inserting data object fails.
540
   */
541
  protected void removeSolrIndex(SystemMetadata sysMeta) {
542
      sysMeta.setSerialVersion(sysMeta.getSerialVersion().add(BigInteger.ONE));
543
      sysMeta.setArchived(true);
544
      sysMeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
545
      try {
546
          MetacatSolrIndex.getInstance().submit(sysMeta.getIdentifier(), sysMeta, null, false);
547
      } catch (Exception e) {
548
          logMetacat.warn("Can't remove the solr index for pid "+sysMeta.getIdentifier().getValue());
549
      }
550
551
  }
552 6241 cjones
553 6227 cjones
  /**
554
   * Return the log records associated with a given event between the start and
555
   * end dates listed given a particular Subject listed in the Session
556
   *
557
   * @param session - the Session object containing the credentials for the Subject
558
   * @param fromDate - the start date of the desired log records
559
   * @param toDate - the end date of the desired log records
560
   * @param event - restrict log records of a specific event type
561
   * @param start - zero based offset from the first record in the
562
   *                set of matching log records. Used to assist with
563
   *                paging the response.
564
   * @param count - maximum number of log records to return in the response.
565
   *                Used to assist with paging the response.
566
   *
567
   * @return the desired log records
568
   *
569
   * @throws InvalidToken
570
   * @throws ServiceFailure
571
   * @throws NotAuthorized
572
   * @throws InvalidRequest
573
   * @throws NotImplemented
574
   */
575
  public Log getLogRecords(Session session, Date fromDate, Date toDate,
576 8810 leinfelder
      String event, String pidFilter, Integer start, Integer count) throws InvalidToken, ServiceFailure,
577 6227 cjones
      NotAuthorized, InvalidRequest, NotImplemented {
578 6174 cjones
579 7234 leinfelder
	  // only admin access to this method
580
	  // see https://redmine.dataone.org/issues/2855
581
	  if (!isAdminAuthorized(session)) {
582
		  throw new NotAuthorized("1460", "Only the CN or admin is allowed to harvest logs from this node");
583
	  }
584 9063 tao
    Log log = new Log();
585 6227 cjones
    IdentifierManager im = IdentifierManager.getInstance();
586
    EventLog el = EventLog.getInstance();
587 6353 cjones
    if ( fromDate == null ) {
588 6227 cjones
      logMetacat.debug("setting fromdate from null");
589
      fromDate = new Date(1);
590
    }
591 6353 cjones
    if ( toDate == null ) {
592 6227 cjones
      logMetacat.debug("setting todate from null");
593
      toDate = new Date();
594
    }
595 6186 leinfelder
596 6353 cjones
    if ( start == null ) {
597 7285 leinfelder
    	start = 0;
598 6353 cjones
    }
599
600
    if ( count == null ) {
601
    	count = 1000;
602
    }
603 7439 leinfelder
604
    // safeguard against large requests
605
    if (count > MAXIMUM_DB_RECORD_COUNT) {
606
    	count = MAXIMUM_DB_RECORD_COUNT;
607
    }
608 6353 cjones
609 7184 leinfelder
    String[] filterDocid = null;
610 9063 tao
    if (pidFilter != null && !pidFilter.trim().equals("")) {
611
        //check if the given identifier is a sid. If it is sid, choose the current pid of the sid.
612
        Identifier pid = new Identifier();
613
        pid.setValue(pidFilter);
614
        String serviceFailureCode = "1490";
615
        Identifier sid = getPIDForSID(pid, serviceFailureCode);
616
        if(sid != null) {
617
            pid = sid;
618
        }
619
        pidFilter = pid.getValue();
620 7184 leinfelder
		try {
621
	      String localId = im.getLocalId(pidFilter);
622
	      filterDocid = new String[] {localId};
623
	    } catch (Exception ex) {
624
	    	String msg = "Could not find localId for given pidFilter '" + pidFilter + "'";
625
	        logMetacat.warn(msg, ex);
626
	        //throw new InvalidRequest("1480", msg);
627 9063 tao
	        return log; //return 0 record
628 7184 leinfelder
	    }
629
    }
630
631 6227 cjones
    logMetacat.debug("fromDate: " + fromDate);
632
    logMetacat.debug("toDate: " + toDate);
633 6186 leinfelder
634 9063 tao
    log = el.getD1Report(null, null, filterDocid, event,
635 6227 cjones
        new java.sql.Timestamp(fromDate.getTime()),
636 7285 leinfelder
        new java.sql.Timestamp(toDate.getTime()), false, start, count);
637 7234 leinfelder
638 6227 cjones
    logMetacat.info("getLogRecords");
639
    return log;
640
  }
641 6241 cjones
642 6227 cjones
  /**
643
   * Return the object identified by the given object identifier
644
   *
645
   * @param session - the Session object containing the credentials for the Subject
646
   * @param pid - the object identifier for the given object
647
   *
648
   * TODO: The D1 Authorization API doesn't provide information on which
649
   * authentication system the Subject belongs to, and so it's not possible to
650
   * discern which Person or Group is a valid KNB LDAP DN.  Fix this.
651
   *
652
   * @return inputStream - the input stream of the given object
653
   *
654
   * @throws InvalidToken
655
   * @throws ServiceFailure
656
   * @throws NotAuthorized
657
   * @throws InvalidRequest
658
   * @throws NotImplemented
659
   */
660
  public InputStream get(Session session, Identifier pid)
661
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
662 6609 cjones
    NotImplemented {
663 6226 cjones
664 9033 tao
    String serviceFailureCode = "1030";
665
    Identifier sid = getPIDForSID(pid, serviceFailureCode);
666
    if(sid != null) {
667
        pid = sid;
668 9032 tao
    }
669
670 6227 cjones
    InputStream inputStream = null; // bytes to be returned
671 6226 cjones
    handler = new MetacatHandler(new Timer());
672 6227 cjones
    boolean allowed = false;
673
    String localId; // the metacat docid for the pid
674 6226 cjones
675 6227 cjones
    // get the local docid from Metacat
676
    try {
677
      localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
678
679 6226 cjones
    } catch (McdbDocNotFoundException e) {
680
      throw new NotFound("1020", "The object specified by " +
681 6227 cjones
                         pid.getValue() +
682 6283 leinfelder
                         " does not exist at this node.");
683 9024 tao
    } catch (SQLException e) {
684
        throw new ServiceFailure("1030", "The object specified by "+ pid.getValue()+
685
                                  " couldn't be identified at this node since "+e.getMessage());
686 6226 cjones
    }
687
688
    // check for authorization
689 7029 leinfelder
    try {
690
		allowed = isAuthorized(session, pid, Permission.READ);
691
	} catch (InvalidRequest e) {
692
		throw new ServiceFailure("1030", e.getDescription());
693
	}
694 6227 cjones
695
    // if the person is authorized, perform the read
696 6323 leinfelder
    if (allowed) {
697 6226 cjones
      try {
698 6323 leinfelder
        inputStream = handler.read(localId);
699 9023 tao
      } catch (McdbDocNotFoundException de) {
700
          String error ="";
701
          if(EventLog.getInstance().isDeleted(localId)) {
702
                error=DELETEDMESSAGE;
703
          }
704
          throw new NotFound("1020", "The object specified by " +
705
                           pid.getValue() +
706
                           " does not exist at this node. "+error);
707 6323 leinfelder
      } catch (Exception e) {
708 9023 tao
        throw new ServiceFailure("1030", "The object specified by " +
709 6226 cjones
            pid.getValue() +
710 9023 tao
            " could not be returned due to error: " +
711
            e.getMessage()+". ");
712 6226 cjones
      }
713 6227 cjones
    }
714 6226 cjones
715 6227 cjones
    // if we fail to set the input stream
716
    if ( inputStream == null ) {
717 9023 tao
        String error ="";
718
        if(EventLog.getInstance().isDeleted(localId)) {
719
              error=DELETEDMESSAGE;
720
        }
721
        throw new NotFound("1020", "The object specified by " +
722 6226 cjones
                         pid.getValue() +
723 9023 tao
                         " does not exist at this node. "+error);
724 6227 cjones
    }
725
726 6389 leinfelder
	// log the read event
727 6532 leinfelder
    String principal = Constants.SUBJECT_PUBLIC;
728 6421 leinfelder
    if (session != null && session.getSubject() != null) {
729 6389 leinfelder
    	principal = session.getSubject().getValue();
730
    }
731 6542 leinfelder
    EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), principal, localId, "read");
732 6389 leinfelder
733 6227 cjones
    return inputStream;
734
  }
735 6174 cjones
736 6227 cjones
  /**
737
   * Return the system metadata for a given object
738
   *
739
   * @param session - the Session object containing the credentials for the Subject
740
   * @param pid - the object identifier for the given object
741
   *
742
   * @return inputStream - the input stream of the given system metadata object
743
   *
744
   * @throws InvalidToken
745
   * @throws ServiceFailure
746
   * @throws NotAuthorized
747
   * @throws NotFound
748
   * @throws InvalidRequest
749
   * @throws NotImplemented
750
   */
751 7132 cjones
    public SystemMetadata getSystemMetadata(Session session, Identifier pid)
752
        throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
753
        NotImplemented {
754
755 9037 tao
        String serviceFailureCode = "1090";
756
        Identifier sid = getPIDForSID(pid, serviceFailureCode);
757
        if(sid != null) {
758
            pid = sid;
759
        }
760 7132 cjones
        boolean isAuthorized = false;
761
        SystemMetadata systemMetadata = null;
762
        List<Replica> replicaList = null;
763
        NodeReference replicaNodeRef = null;
764
        List<Node> nodeListBySubject = null;
765
        Subject subject = null;
766 6572 cjones
767 7132 cjones
        if (session != null ) {
768
            subject = session.getSubject();
769
        }
770 6572 cjones
771 7145 leinfelder
        // check normal authorization
772
        BaseException originalAuthorizationException = null;
773
        if (!isAuthorized) {
774
            try {
775
                isAuthorized = isAuthorized(session, pid, Permission.READ);
776 7132 cjones
777 7145 leinfelder
            } catch (InvalidRequest e) {
778
                throw new ServiceFailure("1090", e.getDescription());
779
            } catch (NotAuthorized nae) {
780
            	// catch this for later
781
            	originalAuthorizationException = nae;
782
			}
783
        }
784 7132 cjones
785 7145 leinfelder
        // get the system metadata first because we need the replica list for auth
786
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
787
788
        // check the replica information to expand access to MNs that might need it
789
        if (!isAuthorized) {
790
791
	        try {
792
793
	            // if MNs are listed as replicas, allow access
794
	            if ( systemMetadata != null ) {
795
	                replicaList = systemMetadata.getReplicaList();
796
	                // only check if there are in fact replicas listed
797
	                if ( replicaList != null ) {
798
799
	                    if ( subject != null ) {
800
	                        // get the list of nodes with a matching node subject
801
	                        try {
802
	                            nodeListBySubject = listNodesBySubject(session.getSubject());
803
804
	                        } catch (BaseException e) {
805
	                            // Unexpected error contacting the CN via D1Client
806
	                            String msg = "Caught an unexpected error while trying "
807
	                                    + "to potentially authorize system metadata access "
808
	                                    + "based on the session subject. The error was "
809
	                                    + e.getMessage();
810
	                            logMetacat.error(msg);
811
	                            if (logMetacat.isDebugEnabled()) {
812
	                                e.printStackTrace();
813
814
	                            }
815
	                            // isAuthorized is still false
816
	                        }
817
818
	                    }
819
	                    if (nodeListBySubject != null) {
820
	                        // compare node ids to replica node ids
821
	                        outer: for (Replica replica : replicaList) {
822
	                            replicaNodeRef = replica.getReplicaMemberNode();
823
824
	                            for (Node node : nodeListBySubject) {
825
	                                if (node.getIdentifier().equals(replicaNodeRef)) {
826
	                                    // node id via session subject matches a replica node
827
	                                    isAuthorized = true;
828
	                                    break outer;
829
	                                }
830
	                            }
831
	                        }
832
	                    }
833
	                }
834
	            }
835
836
	            // if we still aren't authorized, then we are done
837
	            if (!isAuthorized) {
838
	                throw new NotAuthorized("1400", Permission.READ
839
	                        + " not allowed on " + pid.getValue());
840
	            }
841 7132 cjones
842 7145 leinfelder
	        } catch (RuntimeException e) {
843
	        	e.printStackTrace();
844
	            // convert hazelcast RuntimeException to ServiceFailure
845
	            throw new ServiceFailure("1090", "Unexpected error getting system metadata for: " +
846
	                pid.getValue());
847
	        }
848
849 7132 cjones
        }
850 7145 leinfelder
851 7132 cjones
        // It wasn't in the map
852
        if ( systemMetadata == null ) {
853 8903 tao
            String error ="";
854
            String localId = null;
855
            try {
856
                localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
857
858
             } catch (Exception e) {
859
                logMetacat.warn("Couldn't find the local id for the pid "+pid.getValue());
860
            }
861
862
            if(localId != null && EventLog.getInstance().isDeleted(localId)) {
863
                error = DELETEDMESSAGE;
864 8971 tao
            } else if (localId == null && EventLog.getInstance().isDeleted(pid.getValue())) {
865
                error = DELETEDMESSAGE;
866 8903 tao
            }
867
            throw new NotFound("1420", "No record found for: " + pid.getValue()+". "+error);
868 7132 cjones
        }
869
870
        return systemMetadata;
871
    }
872 6572 cjones
873 8360 tao
874
    /**
875
     * Test if the specified session represents the authoritative member node for the
876
     * given object specified by the identifier. According the the DataONE documentation,
877
     * the authoritative member node has all the rights of the *rightsHolder*.
878
     * @param session - the Session object containing the credentials for the Subject
879
     * @param pid - the Identifier of the data object
880
     * @return true if the session represents the authoritative mn.
881
     * @throws ServiceFailure
882
     * @throws NotImplemented
883
     */
884
    public boolean isAuthoritativeMNodeAdmin(Session session, Identifier pid) {
885
        boolean allowed = false;
886
        //check the parameters
887
        if(session == null) {
888
            logMetacat.debug("D1NodeService.isAuthoritativeMNodeAdmin - the session object is null and return false.");
889
            return allowed;
890
        } else if (pid == null || pid.getValue() == null || pid.getValue().trim().equals("")) {
891
            logMetacat.debug("D1NodeService.isAuthoritativeMNodeAdmin - the Identifier object is null (not being specified) and return false.");
892
            return allowed;
893
        }
894
895
        //Get the subject from the session
896
        Subject subject = session.getSubject();
897
        if(subject != null) {
898
            //Get the authoritative member node info from the system metadata
899
            SystemMetadata sysMeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
900
            if(sysMeta != null) {
901
                NodeReference authoritativeMNode = sysMeta.getAuthoritativeMemberNode();
902
                if(authoritativeMNode != null) {
903
                        CNode cn = null;
904
                        try {
905
                            cn = D1Client.getCN();
906 8810 leinfelder
                        } catch (BaseException e) {
907 8360 tao
                            logMetacat.error("D1NodeService.isAuthoritativeMNodeAdmin - couldn't connect to the CN since "+
908
                                            e.getDescription()+ ". The false value will be returned for the AuthoritativeMNodeAdmin.");
909
                            return allowed;
910
                        }
911
912
                        if(cn != null) {
913
                            List<Node> nodes = null;
914
                            try {
915
                                nodes = cn.listNodes().getNodeList();
916
                            } catch (NotImplemented e) {
917
                                logMetacat.error("D1NodeService.isAuthoritativeMNodeAdmin - couldn't get the member nodes list from the CN since "+e.getDescription()+
918
                                                ". The false value will be returned for the AuthoritativeMNodeAdmin.");
919
                                return allowed;
920
                            } catch (ServiceFailure ee) {
921
                                logMetacat.error("D1NodeService.isAuthoritativeMNodeAdmin - couldn't get the member nodes list from the CN since "+ee.getDescription()+
922
                                                ". The false value will be returned for the AuthoritativeMNodeAdmin.");
923
                                return allowed;
924
                            }
925
                            if(nodes != null) {
926
                                for(Node node : nodes) {
927
                                    //find the authoritative node and get its subjects
928
                                    if (node.getType() == NodeType.MN && node.getIdentifier() != null && node.getIdentifier().equals(authoritativeMNode)) {
929
                                        List<Subject> nodeSubjects = node.getSubjectList();
930
                                        if(nodeSubjects != null) {
931
                                            // check if the session subject is in the node subject list
932
                                            for (Subject nodeSubject : nodeSubjects) {
933
                                                logMetacat.debug("D1NodeService.isAuthoritativeMNodeAdmin(), comparing subjects: " +
934
                                                    nodeSubject.getValue() + " and " + subject.getValue());
935
                                                if ( nodeSubject != null && nodeSubject.equals(subject) ) {
936
                                                    allowed = true; // subject of session == target node subject
937
                                                    break;
938
                                                }
939
                                            }
940
                                        }
941
942
                                    }
943
                                }
944
                            }
945
                        }
946
                }
947
            }
948
        }
949
        return allowed;
950
    }
951
952
953 6227 cjones
  /**
954 6865 cjones
   * Test if the user identified by the provided token has administrative authorization
955
   *
956
   * @param session - the Session object containing the credentials for the Subject
957
   *
958 9190 tao
   * @return true if the user is admin (mn itself or a cn )
959 6865 cjones
   *
960
   * @throws ServiceFailure
961
   * @throws InvalidToken
962
   * @throws NotFound
963
   * @throws NotAuthorized
964
   * @throws NotImplemented
965
   */
966 7142 leinfelder
  public boolean isAdminAuthorized(Session session)
967 7079 leinfelder
      throws ServiceFailure, InvalidToken, NotAuthorized,
968 6865 cjones
      NotImplemented {
969
970
      boolean allowed = false;
971 7069 leinfelder
972
      // must have a session in order to check admin
973
      if (session == null) {
974 7142 leinfelder
         logMetacat.debug("In isAdminAuthorized(), session is null ");
975
         return false;
976 7069 leinfelder
      }
977
978 7072 cjones
      logMetacat.debug("In isAdminAuthorized(), checking CN or MN authorization for " +
979 7142 leinfelder
           session.getSubject().getValue());
980 7072 cjones
981 7330 leinfelder
      // check if this is the node calling itself (MN)
982 9309 tao
      try {
983
          allowed = isNodeAdmin(session);
984
      } catch (Exception e) {
985
          logMetacat.warn("We can't determine if the session is a node subject. But we will contiune to check if it is a cn subject.");
986
      }
987 6865 cjones
988 9309 tao
989 7330 leinfelder
      // check the CN list
990
      if (!allowed) {
991 9190 tao
	      allowed = isCNAdmin(session);
992
      }
993
994
      return allowed;
995
  }
996
997
  /*
998
   * Determine if the specified session is a CN or not. Return true if it is; otherwise false.
999
   */
1000
  protected boolean isCNAdmin (Session session) {
1001
      boolean allowed = false;
1002
      List<Node> nodes = null;
1003 9328 tao
      logMetacat.debug("D1NodeService.isCNAdmin - the beginning");
1004 9190 tao
      try {
1005
          // are we allowed to do this? only CNs are allowed
1006
          CNode cn = D1Client.getCN();
1007 9328 tao
          logMetacat.debug("D1NodeService.isCNAdmin - after getting the cn.");
1008 9190 tao
          nodes = cn.listNodes().getNodeList();
1009 9328 tao
          logMetacat.debug("D1NodeService.isCNAdmin - after getting the node list.");
1010 6865 cjones
      }
1011 9190 tao
      catch (Throwable e) {
1012 9309 tao
          logMetacat.warn("Couldn't get the node list from the cn since "+e.getMessage()+". So we can't determine if the subject is a CN.");
1013 9190 tao
          return false;
1014
      }
1015
1016
      if ( nodes == null ) {
1017
          return false;
1018
          //throw new ServiceFailure("4852", "Couldn't get node list.");
1019
      }
1020 6865 cjones
1021 9190 tao
      // find the node in the node list
1022
      for ( Node node : nodes ) {
1023
1024
          NodeReference nodeReference = node.getIdentifier();
1025 10021 tao
          logMetacat.debug("In isCNAdmin(), a Node reference from the CN node list is: " + nodeReference.getValue());
1026 9190 tao
1027
          Subject subject = session.getSubject();
1028
1029
          if (node.getType() == NodeType.CN) {
1030
              List<Subject> nodeSubjects = node.getSubjectList();
1031
1032
              // check if the session subject is in the node subject list
1033
              for (Subject nodeSubject : nodeSubjects) {
1034 9328 tao
                  logMetacat.debug("In isCNAdmin(), comparing subjects: " +
1035 10021 tao
                      nodeSubject.getValue() + " and the user " + subject.getValue());
1036 9190 tao
                  if ( nodeSubject.equals(subject) ) {
1037
                      allowed = true; // subject of session == target node subject
1038
                      break;
1039
1040
                  }
1041
              }
1042
          }
1043
      }
1044 9328 tao
      logMetacat.debug("D1NodeService.isCNAdmin. Is it a cn admin? "+allowed);
1045 6865 cjones
      return allowed;
1046
  }
1047
1048
  /**
1049 7162 leinfelder
   * Test if the user identified by the provided token has administrative authorization
1050
   * on this node because they are calling themselves
1051
   *
1052
   * @param session - the Session object containing the credentials for the Subject
1053
   *
1054
   * @return true if the user is this node
1055
   * @throws ServiceFailure
1056
   * @throws NotImplemented
1057
   */
1058
  public boolean isNodeAdmin(Session session) throws NotImplemented, ServiceFailure {
1059
1060
      boolean allowed = false;
1061
1062
      // must have a session in order to check admin
1063
      if (session == null) {
1064
         logMetacat.debug("In isNodeAdmin(), session is null ");
1065
         return false;
1066
      }
1067
1068 10021 tao
      logMetacat.debug("In isNodeAdmin(), MN authorization for the user " +
1069 7162 leinfelder
           session.getSubject().getValue());
1070
1071
      Node node = MNodeService.getInstance(request).getCapabilities();
1072
      NodeReference nodeReference = node.getIdentifier();
1073
      logMetacat.debug("In isNodeAdmin(), Node reference is: " + nodeReference.getValue());
1074
1075
      Subject subject = session.getSubject();
1076
1077
      if (node.getType() == NodeType.MN) {
1078
          List<Subject> nodeSubjects = node.getSubjectList();
1079
1080
          // check if the session subject is in the node subject list
1081
          for (Subject nodeSubject : nodeSubjects) {
1082
              logMetacat.debug("In isNodeAdmin(), comparing subjects: " +
1083 10021 tao
                  nodeSubject.getValue() + " and the user" + subject.getValue());
1084 7162 leinfelder
              if ( nodeSubject.equals(subject) ) {
1085
                  allowed = true; // subject of session == this node's subect
1086
                  break;
1087
              }
1088
          }
1089
      }
1090 9328 tao
      logMetacat.debug("In is NodeAdmin method. Is this a node admin? "+allowed);
1091 7162 leinfelder
      return allowed;
1092
  }
1093
1094
  /**
1095 6227 cjones
   * Test if the user identified by the provided token has authorization
1096 6865 cjones
   * for the operation on the specified object.
1097 9175 tao
   * Allowed subjects include:
1098
   * 1. CNs
1099
   * 2. Authoritative node
1100
   * 3. Owner of the object
1101
   * 4. Users with the specified permission in the access rules.
1102 6227 cjones
   *
1103
   * @param session - the Session object containing the credentials for the Subject
1104
   * @param pid - The identifer of the resource for which access is being checked
1105
   * @param operation - The type of operation which is being requested for the given pid
1106
   *
1107
   * @return true if the operation is allowed
1108
   *
1109
   * @throws ServiceFailure
1110
   * @throws InvalidToken
1111
   * @throws NotFound
1112
   * @throws NotAuthorized
1113
   * @throws NotImplemented
1114
   * @throws InvalidRequest
1115
   */
1116
  public boolean isAuthorized(Session session, Identifier pid, Permission permission)
1117
    throws ServiceFailure, InvalidToken, NotFound, NotAuthorized,
1118 7029 leinfelder
    NotImplemented, InvalidRequest {
1119 6174 cjones
1120 6227 cjones
    boolean allowed = false;
1121
1122 7029 leinfelder
    if (permission == null) {
1123
    	throw new InvalidRequest("1761", "Permission was not provided or is invalid");
1124
    }
1125
1126 7067 cjones
    // always allow CN access
1127 7142 leinfelder
    if ( isAdminAuthorized(session) ) {
1128 7067 cjones
        allowed = true;
1129
        return allowed;
1130
1131
    }
1132
1133 9051 tao
    String serviceFailureCode = "1760";
1134
    Identifier sid = getPIDForSID(pid, serviceFailureCode);
1135
    if(sid != null) {
1136
        pid = sid;
1137
    }
1138
1139 8360 tao
    // the authoritative member node of the pid always has the access as well.
1140
    if (isAuthoritativeMNodeAdmin(session, pid)) {
1141
        allowed = true;
1142
        return allowed;
1143
    }
1144
1145 9190 tao
    //is it the owner of the object or the access rules allow the user?
1146
    allowed = userHasPermission(session,  pid, permission );
1147 6293 leinfelder
1148 6245 leinfelder
    // throw or return?
1149 6227 cjones
    if (!allowed) {
1150 9190 tao
     // track the identities we have checked against
1151
      StringBuffer includedSubjects = new StringBuffer();
1152
      Set<Subject> subjects = AuthUtils.authorizedClientSubjects(session);
1153
      for (Subject s: subjects) {
1154
             includedSubjects.append(s.getValue() + "; ");
1155
        }
1156
      throw new NotAuthorized("1820", permission + " not allowed on " + pid.getValue() + " for subject[s]: " + includedSubjects.toString() );
1157 6227 cjones
    }
1158 6245 leinfelder
1159 6227 cjones
    return allowed;
1160 6241 cjones
1161 6227 cjones
  }
1162
1163 9190 tao
1164 6256 cjones
  /*
1165 9190 tao
   * Determine if a user has the permission to perform the specified permission.
1166
   * 1. Owner can have any permission.
1167
   * 2. Access table allow the user has the permission
1168
   */
1169 9997 tao
  public static boolean userHasPermission(Session userSession, Identifier pid, Permission permission ) throws NotFound, ServiceFailure, NotImplemented, InvalidRequest, InvalidToken, NotAuthorized {
1170 9190 tao
      boolean allowed = false;
1171
      // permissions are hierarchical
1172
      List<Permission> expandedPermissions = null;
1173
      // get the subject[s] from the session
1174
      //defer to the shared util for recursively compiling the subjects
1175
      Set<Subject> subjects = AuthUtils.authorizedClientSubjects(userSession);
1176
1177
      // get the system metadata
1178
      String pidStr = pid.getValue();
1179
      SystemMetadata systemMetadata = null;
1180
      try {
1181
          systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1182
1183
      } catch (Exception e) {
1184
          // convert Hazelcast RuntimeException to NotFound
1185
          logMetacat.error("An error occurred while getting system metadata for identifier " +
1186 10030 tao
              pid.getValue() + ". The error message was: " + e.getMessage(), e);
1187
          throw new ServiceFailure("1090", "Can't get the system metadata for " + pidStr+ " since "+e.getMessage());
1188 9190 tao
1189
      }
1190
1191
      // throw not found if it was not found
1192
      if (systemMetadata == null) {
1193
          String localId = null;
1194
          String error = "No system metadata could be found for given PID: " + pidStr;
1195
          try {
1196
              localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
1197
1198
           } catch (Exception e) {
1199
              logMetacat.warn("Couldn't find the local id for the pid "+pidStr);
1200
          }
1201
1202
          if(localId != null && EventLog.getInstance().isDeleted(localId)) {
1203
              error = error + ". "+DELETEDMESSAGE;
1204
          } else if (localId == null && EventLog.getInstance().isDeleted(pid.getValue())) {
1205
              error = error + ". "+DELETEDMESSAGE;
1206
          }
1207
          throw new NotFound("1800", error);
1208
      }
1209
1210
      // do we own it?
1211
      for (Subject s: subjects) {
1212
        logMetacat.debug("Comparing \t" +
1213
                         systemMetadata.getRightsHolder().getValue() +
1214
                         " \tagainst \t" + s.getValue());
1215
          //includedSubjects.append(s.getValue() + "; ");
1216
          allowed = systemMetadata.getRightsHolder().equals(s);
1217
          if (allowed) {
1218
              return allowed;
1219 9997 tao
          } else {
1220
              //check if the rightHolder is a group name. If it is, any member of the group can be considered a the right holder.
1221
              allowed = expandRightsHolder(systemMetadata.getRightsHolder(), s);
1222
              if(allowed) {
1223
                  return allowed;
1224
              }
1225 9190 tao
          }
1226
      }
1227
1228
      // otherwise check the access rules
1229
      try {
1230
          List<AccessRule> allows = systemMetadata.getAccessPolicy().getAllowList();
1231
          search: // label break
1232
          for (AccessRule accessRule: allows) {
1233
            for (Subject s: subjects) {
1234
              logMetacat.debug("Checking allow access rule for subject: " + s.getValue());
1235
              if (accessRule.getSubjectList().contains(s)) {
1236
                  logMetacat.debug("Access rule contains subject: " + s.getValue());
1237
                  for (Permission p: accessRule.getPermissionList()) {
1238
                      logMetacat.debug("Checking permission: " + p.xmlValue());
1239
                      expandedPermissions = expandPermissions(p);
1240
                      allowed = expandedPermissions.contains(permission);
1241
                      if (allowed) {
1242
                          logMetacat.info("Permission granted: " + p.xmlValue() + " to " + s.getValue());
1243
                          break search; //label break
1244
                      }
1245
                  }
1246
1247
              }
1248
            }
1249
          }
1250
      } catch (Exception e) {
1251
          // catch all for errors - safe side should be to deny the access
1252
          logMetacat.error("Problem checking authorization - defaulting to deny", e);
1253
          allowed = false;
1254
1255
      }
1256
      return allowed;
1257
  }
1258 9997 tao
1259
1260
  /**
1261
   * Check if the given userSession is the member of the right holder group (if the right holder is a group subject).
1262
   * If the right holder is not a group, it will be false of course.
1263
   * @param rightHolder the subject of the right holder.
1264
   * @param userSession the subject will be compared
1265
   * @return true if the user session is a member of the right holder group; false otherwise.
1266
 * @throws NotImplemented
1267
 * @throws ServiceFailure
1268
 * @throws NotAuthorized
1269
 * @throws InvalidToken
1270
 * @throws InvalidRequest
1271
   */
1272
  public static boolean expandRightsHolder(Subject rightHolder, Subject userSession) throws ServiceFailure, NotImplemented, InvalidRequest, InvalidToken, NotAuthorized {
1273
      boolean is = false;
1274
      if(rightHolder != null && userSession != null && rightHolder.getValue() != null && !rightHolder.getValue().trim().equals("") && userSession.getValue() != null && !userSession.getValue().trim().equals("")) {
1275
          CNode cn = D1Client.getCN();
1276 10205 tao
          logMetacat.debug("D1NodeService.expandRightHolder - at the start of method: after getting the cn node and cn node is "+cn.getNodeBaseServiceUrl());
1277 9997 tao
          String query= rightHolder.getValue();
1278
          int start =0;
1279 10205 tao
          int count= 200;
1280 9997 tao
          String status = null;
1281
          Session session = null;
1282
          SubjectInfo subjects = cn.listSubjects(session, query, status, start, count);
1283 10205 tao
1284
          while(subjects != null) {
1285 9997 tao
              logMetacat.debug("D1NodeService.expandRightHolder - search the subject "+query+" in the cn and the returned result is not null");
1286
              List<Group> groups = subjects.getGroupList();
1287 10205 tao
              is = isInGroups(userSession, rightHolder, groups);
1288
              if(is) {
1289
                  //since we find it, return it.
1290
                  return is;
1291
              } else {
1292
                  //decide if we need to try the page query for another trying.
1293
                  int sizeOfGroups = 0;
1294
                  if(groups != null) {
1295
                     sizeOfGroups  = groups.size();
1296 9997 tao
                  }
1297 10205 tao
                  List<Person> persons = subjects.getPersonList();
1298
                  int sizeOfPersons = 0;
1299
                  if(persons != null) {
1300
                      sizeOfPersons = persons.size();
1301
                  }
1302
                  int totalSize = sizeOfGroups+sizeOfPersons;
1303
                  //logMetacat.debug("D1NodeService.expandRightHolder - search the subject "+query+" in the cn and the size of return result is "+totalSize);
1304
                 //we can't find the target on the first query, maybe query again.
1305
                  if(totalSize == count) {
1306
                      start = start+count;
1307
                      logMetacat.debug("D1NodeService.expandRightHolder - search the subject "+query+" in the cn and the size of return result equals the count "+totalSize+" .And we didn't find the target in the this query. So we have to use the page query with the start number "+start);
1308
                      subjects = cn.listSubjects(session, query, status, start, count);
1309
                  } else if (totalSize < count){
1310
                      logMetacat.debug("D1NodeService.expandRightHolder - we are already at the end of the returned restult since the size of returned results "+totalSize+
1311
                          " is less than the count "+count+". So we have to break the loop and finish the try.");
1312
                      break;
1313
                  } else if (totalSize >count) {
1314
                      logMetacat.warn("D1NodeService.expandRightHolder - Something is wrong on the implementation of the method listSubject since the size of returned results "+totalSize+
1315
                              " is greater than the count "+count+". So we have to break the loop and finish the try.");
1316
                      break;
1317
                  }
1318 9997 tao
              }
1319 10205 tao
1320
          }
1321
          //logMetacat.debug("D1NodeService.expandRightHolder - search the subject "+query+" in the cn and the returned result is null");
1322 9997 tao
          if(!is) {
1323
              logMetacat.debug("D1NodeService.expandRightHolder - We can NOT find any member in the group "+query+" (if it is a group) matches the user "+userSession.getValue());
1324
          }
1325
      } else {
1326
          logMetacat.debug("D1NodeService.expandRightHolder - We can't determine if the use subject is a member of the right holder group since one of them is null or blank");
1327
      }
1328
1329
      return is;
1330
  }
1331 10205 tao
1332 9190 tao
  /*
1333 10205 tao
   * If the given useSession is a member of a group which is in the given list of groups and has the name of righHolder.
1334
   */
1335
  private static boolean isInGroups(Subject userSession, Subject rightHolder, List<Group> groups) {
1336
      boolean is = false;
1337
      if(groups != null) {
1338
          logMetacat.debug("D1NodeService.isInGroups -  the given groups' (the returned result including groups) size is "+groups.size());
1339
          for(Group group : groups) {
1340
              //logMetacat.debug("D1NodeService.expandRightHolder - group has the subject "+group.getSubject().getValue());
1341
              if(group != null && group.getSubject() != null && group.getSubject().equals(rightHolder)) {
1342
                  logMetacat.debug("D1NodeService.isInGroups - there is a group in the list having the subjecct "+group.getSubject().getValue()+" which matches the right holder's subject "+rightHolder.getValue());
1343
                  List<Subject> members = group.getHasMemberList();
1344
                  if(members != null ){
1345
                      logMetacat.debug("D1NodeService.isInGroups - the group "+group.getSubject().getValue()+" in the cn has members");
1346
                      for(Subject member : members) {
1347
                          logMetacat.debug("D1NodeService.isInGroups - compare the member "+member.getValue()+" with the user "+userSession.getValue());
1348
                          if(member.getValue() != null && !member.getValue().trim().equals("") && userSession.getValue() != null && member.getValue().equals(userSession.getValue())) {
1349
                              logMetacat.debug("D1NodeService.isInGroups - Find it! The member "+member.getValue()+" in the group "+group.getSubject().getValue()+" matches the user "+userSession.getValue());
1350
                              is = true;
1351
                              return is;
1352
                          }
1353
                      }
1354
                  }
1355
                  break;//we found the group but can't find the member matches the user. so break it.
1356
              }
1357
          }
1358
      } else {
1359
          logMetacat.debug("D1NodeService.isInGroups -  the given group is null (the returned result does NOT have a group");
1360
      }
1361
      return is;
1362
  }
1363
  /*
1364 6227 cjones
   * parse a logEntry and get the relevant field from it
1365
   *
1366
   * @param fieldname
1367
   * @param entry
1368
   * @return
1369
   */
1370
  private String getLogEntryField(String fieldname, String entry) {
1371
    String begin = "<" + fieldname + ">";
1372
    String end = "</" + fieldname + ">";
1373
    // logMetacat.debug("looking for " + begin + " and " + end +
1374
    // " in entry " + entry);
1375
    String s = entry.substring(entry.indexOf(begin) + begin.length(), entry
1376
        .indexOf(end));
1377
    logMetacat.debug("entry " + fieldname + " : " + s);
1378
    return s;
1379
  }
1380 6174 cjones
1381 6257 cjones
  /**
1382 6241 cjones
   * Determine if a given object should be treated as an XML science metadata
1383
   * object.
1384
   *
1385
   * @param sysmeta - the SystemMetadata describing the object
1386
   * @return true if the object should be treated as science metadata
1387
   */
1388 6433 leinfelder
  public static boolean isScienceMetadata(SystemMetadata sysmeta) {
1389 6241 cjones
1390
    ObjectFormat objectFormat = null;
1391
    boolean isScienceMetadata = false;
1392
1393
    try {
1394 6561 leinfelder
      objectFormat = ObjectFormatCache.getInstance().getFormat(sysmeta.getFormatId());
1395 6433 leinfelder
      if ( objectFormat.getFormatType().equals("METADATA") ) {
1396 6402 cjones
      	isScienceMetadata = true;
1397
1398
      }
1399 6241 cjones
1400
1401 9523 tao
    /*} catch (ServiceFailure e) {
1402 6241 cjones
      logMetacat.debug("There was a problem determining if the object identified by" +
1403
          sysmeta.getIdentifier().getValue() +
1404 9523 tao
          " is science metadata: " + e.getMessage());*/
1405 6241 cjones
1406
    } catch (NotFound e) {
1407 9781 cjones
      logMetacat.debug("There was a problem determining if the object identified by" +
1408 6241 cjones
          sysmeta.getIdentifier().getValue() +
1409
          " is science metadata: " + e.getMessage());
1410
1411
    }
1412
1413
    return isScienceMetadata;
1414 6226 cjones
1415 6241 cjones
  }
1416
1417
  /**
1418 7315 leinfelder
   * Check fro whitespace in the given pid.
1419
   * null pids are also invalid by default
1420
   * @param pid
1421
   * @return
1422
   */
1423
  public static boolean isValidIdentifier(Identifier pid) {
1424
	  if (pid != null && pid.getValue() != null && pid.getValue().length() > 0) {
1425 7816 leinfelder
		  return !pid.getValue().matches(".*\\s+.*");
1426 7315 leinfelder
	  }
1427
	  return false;
1428
  }
1429
1430
1431
  /**
1432 6241 cjones
   * Insert or update an XML document into Metacat
1433
   *
1434
   * @param xml - the XML document to insert or update
1435
   * @param pid - the identifier to be used for the resulting object
1436
   *
1437
   * @return localId - the resulting docid of the document created or updated
1438
   *
1439
   */
1440 9783 leinfelder
  public String insertOrUpdateDocument(InputStream xmlStream, String encoding,  Identifier pid,
1441 10272 tao
    Session session, String insertOrUpdate, String formatId, Checksum checksum)
1442 10130 tao
    throws ServiceFailure, IOException, PropertyNotFoundException{
1443 6241 cjones
1444
  	logMetacat.debug("Starting to insert xml document...");
1445
    IdentifierManager im = IdentifierManager.getInstance();
1446
1447
    // generate pid/localId pair for sysmeta
1448
    String localId = null;
1449 9783 leinfelder
    byte[] xmlBytes  = IOUtils.toByteArray(xmlStream);
1450
    IOUtils.closeQuietly(xmlStream);
1451 8948 tao
    String xmlStr = new String(xmlBytes, encoding);
1452 6241 cjones
    if(insertOrUpdate.equals("insert")) {
1453
      localId = im.generateLocalId(pid.getValue(), 1);
1454
1455
    } else {
1456
      //localid should already exist in the identifier table, so just find it
1457
      try {
1458
        logMetacat.debug("Updating pid " + pid.getValue());
1459
        logMetacat.debug("looking in identifier table for pid " + pid.getValue());
1460
1461
        localId = im.getLocalId(pid.getValue());
1462
1463
        logMetacat.debug("localId: " + localId);
1464
        //increment the revision
1465
        String docid = localId.substring(0, localId.lastIndexOf("."));
1466
        String revS = localId.substring(localId.lastIndexOf(".") + 1, localId.length());
1467
        int rev = new Integer(revS).intValue();
1468
        rev++;
1469
        docid = docid + "." + rev;
1470
        localId = docid;
1471
        logMetacat.debug("incremented localId: " + localId);
1472
1473
      } catch(McdbDocNotFoundException e) {
1474
        throw new ServiceFailure("1030", "D1NodeService.insertOrUpdateDocument(): " +
1475
            "pid " + pid.getValue() +
1476
            " should have been in the identifier table, but it wasn't: " +
1477
            e.getMessage());
1478
1479 9024 tao
      } catch (SQLException e) {
1480
          throw new ServiceFailure("1030", "D1NodeService.insertOrUpdateDocument() -"+
1481
                     " couldn't identify if the pid "+pid.getValue()+" is in the identifier table since "+e.getMessage());
1482 6241 cjones
      }
1483
1484
    }
1485
1486 10147 tao
    Hashtable<String, String[]> params = new Hashtable<String, String[]>();
1487 6241 cjones
    String[] action = new String[1];
1488
    action[0] = insertOrUpdate;
1489
    params.put("action", action);
1490
    String[] docid = new String[1];
1491
    docid[0] = localId;
1492
    params.put("docid", docid);
1493
    String[] doctext = new String[1];
1494 8948 tao
    doctext[0] = xmlStr;
1495 6241 cjones
    params.put("doctext", doctext);
1496
1497 6532 leinfelder
    String username = Constants.SUBJECT_PUBLIC;
1498 6241 cjones
    String[] groupnames = null;
1499 6302 leinfelder
    if (session != null ) {
1500
    	username = session.getSubject().getValue();
1501 9709 leinfelder
    	Set<Subject> otherSubjects = AuthUtils.authorizedClientSubjects(session);
1502
    	if (otherSubjects != null) {
1503
			groupnames = new String[otherSubjects.size()];
1504
			int i = 0;
1505
			Iterator<Subject> iter = otherSubjects.iterator();
1506
			while (iter.hasNext()) {
1507
				groupnames[i] = iter.next().getValue();
1508
				i++;
1509
			}
1510 6302 leinfelder
    	}
1511 6241 cjones
    }
1512
1513
    // do the insert or update action
1514 6443 leinfelder
    handler = new MetacatHandler(new Timer());
1515 6542 leinfelder
    String result = handler.handleInsertOrUpdateAction(request.getRemoteAddr(), request.getHeader("User-Agent"), null,
1516 10272 tao
                        null, params, username, groupnames, false, false, xmlBytes, formatId, checksum);
1517 10130 tao
    boolean isScienceMetadata = true;
1518
    if(result.indexOf("<error>") != -1 || !IdentifierManager.getInstance().objectFileExists(localId, isScienceMetadata)) {
1519 6241 cjones
    	String detailCode = "";
1520
    	if ( insertOrUpdate.equals("insert") ) {
1521 7017 leinfelder
    		// make sure to remove the mapping so that subsequent attempts do not fail with IdentifierNotUnique
1522
    		im.removeMapping(pid.getValue(), localId);
1523 6241 cjones
    		detailCode = "1190";
1524
1525
    	} else if ( insertOrUpdate.equals("update") ) {
1526
    		detailCode = "1310";
1527
1528
    	}
1529 10006 tao
    	logMetacat.error("D1NodeService.insertOrUpdateDocument - Error inserting or updating document: "+pid.getValue()+" since "+result);
1530 6241 cjones
        throw new ServiceFailure(detailCode,
1531 10006 tao
          "Error inserting or updating document: " +pid.getValue()+" since "+ result);
1532 6241 cjones
    }
1533 10138 tao
    logMetacat.info("D1NodeService.insertOrUpdateDocument - Finsished inserting xml document with local id " + localId +" and its pid is "+pid.getValue());
1534 6241 cjones
1535
    return localId;
1536
  }
1537
1538
  /**
1539
   * Insert a data document
1540
   *
1541 9781 cjones
   * @param object
1542
   * @param pid
1543
   * @param sessionData
1544
   * @throws ServiceFailure
1545
   * @returns localId of the data object inserted
1546
   */
1547
  public String insertDataObject(InputStream object, Identifier pid,
1548 10272 tao
          Session session, Checksum checksum) throws ServiceFailure, InvalidSystemMetadata {
1549 9781 cjones
1550 6532 leinfelder
    String username = Constants.SUBJECT_PUBLIC;
1551 6241 cjones
    String[] groupnames = null;
1552 6302 leinfelder
    if (session != null ) {
1553
    	username = session.getSubject().getValue();
1554 9783 leinfelder
    	Set<Subject> otherSubjects = AuthUtils.authorizedClientSubjects(session);
1555
    	if (otherSubjects != null) {
1556
			groupnames = new String[otherSubjects.size()];
1557
			int i = 0;
1558
			Iterator<Subject> iter = otherSubjects.iterator();
1559
			while (iter.hasNext()) {
1560
				groupnames[i] = iter.next().getValue();
1561
				i++;
1562
			}
1563 6302 leinfelder
    	}
1564 6241 cjones
    }
1565 6719 leinfelder
1566 9781 cjones
    // generate pid/localId pair for object
1567
    logMetacat.debug("Generating a pid/localId mapping");
1568
    IdentifierManager im = IdentifierManager.getInstance();
1569
    String localId = im.generateLocalId(pid.getValue(), 1);
1570
1571 6719 leinfelder
    // Save the data file to disk using "localId" as the name
1572
    String datafilepath = null;
1573
	try {
1574
		datafilepath = PropertyService.getProperty("application.datafilepath");
1575
	} catch (PropertyNotFoundException e) {
1576
		ServiceFailure sf = new ServiceFailure("1190", "Lookup data file path" + e.getMessage());
1577
		sf.initCause(e);
1578
		throw sf;
1579 9781 cjones
	}
1580 6719 leinfelder
    boolean locked = false;
1581
	try {
1582
		locked = DocumentImpl.getDataFileLockGrant(localId);
1583
	} catch (Exception e) {
1584
		ServiceFailure sf = new ServiceFailure("1190", "Could not lock file for writing:" + e.getMessage());
1585
		sf.initCause(e);
1586
		throw sf;
1587
	}
1588 9781 cjones
1589
    logMetacat.debug("Case DATA: starting to write to disk.");
1590
	if (locked) {
1591
1592
          File dataDirectory = new File(datafilepath);
1593
          dataDirectory.mkdirs();
1594
1595 10272 tao
          File newFile = writeStreamToFile(dataDirectory, localId, object, checksum, pid);
1596 9781 cjones
1597
          // TODO: Check that the file size matches SystemMetadata
1598
          // long size = newFile.length();
1599
          // if (size == 0) {
1600
          //     throw new IOException("Uploaded file is 0 bytes!");
1601
          // }
1602
1603
          // Register the file in the database (which generates an exception
1604
          // if the localId is not acceptable or other untoward things happen
1605
          try {
1606
            logMetacat.debug("Registering document...");
1607
            DocumentImpl.registerDocument(localId, "BIN", localId,
1608
                    username, groupnames);
1609 6241 cjones
            logMetacat.debug("Registration step completed.");
1610 9781 cjones
1611
          } catch (SQLException e) {
1612
            //newFile.delete();
1613
            logMetacat.debug("SQLE: " + e.getMessage());
1614
            e.printStackTrace(System.out);
1615 6241 cjones
            throw new ServiceFailure("1190", "Registration failed: " +
1616
            		e.getMessage());
1617 9781 cjones
1618
          } catch (AccessionNumberException e) {
1619
            //newFile.delete();
1620
            logMetacat.debug("ANE: " + e.getMessage());
1621
            e.printStackTrace(System.out);
1622 6241 cjones
            throw new ServiceFailure("1190", "Registration failed: " +
1623
            	e.getMessage());
1624 9781 cjones
1625
          } catch (Exception e) {
1626
            //newFile.delete();
1627
            logMetacat.debug("Exception: " + e.getMessage());
1628
            e.printStackTrace(System.out);
1629 6241 cjones
            throw new ServiceFailure("1190", "Registration failed: " +
1630 9781 cjones
            	e.getMessage());
1631
          }
1632
1633
          logMetacat.debug("Logging the creation event.");
1634
          EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), username, localId, "create");
1635
1636
          // Schedule replication for this data file, the "insert" action is important here!
1637
          logMetacat.debug("Scheduling replication.");
1638
          ForceReplicationHandler frh = new ForceReplicationHandler(localId, "insert", false, null);
1639 6719 leinfelder
      }
1640 9781 cjones
1641 6719 leinfelder
      return localId;
1642 9781 cjones
1643 9783 leinfelder
  }
1644 6255 cjones
1645 6256 cjones
  /**
1646
   * Insert a systemMetadata document and return its localId
1647
   */
1648 6807 leinfelder
  public void insertSystemMetadata(SystemMetadata sysmeta)
1649 6572 cjones
      throws ServiceFailure {
1650
1651
  	  logMetacat.debug("Starting to insert SystemMetadata...");
1652
      sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
1653
      logMetacat.debug("Inserting new system metadata with modified date " +
1654
          sysmeta.getDateSysMetadataModified());
1655
1656
      //insert the system metadata
1657
      try {
1658
        // note: the calling subclass handles the map hazelcast lock/unlock
1659
      	HazelcastService.getInstance().getSystemMetadataMap().put(sysmeta.getIdentifier(), sysmeta);
1660 7812 leinfelder
      	// submit for indexing
1661 8647 leinfelder
        MetacatSolrIndex.getInstance().submit(sysmeta.getIdentifier(), sysmeta, null, true);
1662 6572 cjones
      } catch (Exception e) {
1663
          throw new ServiceFailure("1190", e.getMessage());
1664
1665
	    }
1666 6256 cjones
  }
1667 9071 tao
1668
  /**
1669
   * Retrieve the list of objects present on the MN that match the calling parameters
1670
   *
1671
   * @param session - the Session object containing the credentials for the Subject
1672
   * @param startTime - Specifies the beginning of the time range from which
1673
   *                    to return object (>=)
1674
   * @param endTime - Specifies the beginning of the time range from which
1675
   *                  to return object (>=)
1676
   * @param objectFormat - Restrict results to the specified object format
1677
   * @param replicaStatus - Indicates if replicated objects should be returned in the list
1678
   * @param start - The zero-based index of the first value, relative to the
1679
   *                first record of the resultset that matches the parameters.
1680
   * @param count - The maximum number of entries that should be returned in
1681
   *                the response. The Member Node may return less entries
1682
   *                than specified in this value.
1683
   *
1684
   * @return objectList - the list of objects matching the criteria
1685
   *
1686
   * @throws InvalidToken
1687
   * @throws ServiceFailure
1688
   * @throws NotAuthorized
1689
   * @throws InvalidRequest
1690
   * @throws NotImplemented
1691
   */
1692 9249 tao
  public ObjectList listObjects(Session session, Date startTime, Date endTime, ObjectFormatIdentifier objectFormatId, Identifier identifier, NodeReference nodeId, Integer start,
1693 9071 tao
          Integer count) throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure, InvalidToken {
1694 6256 cjones
1695 9071 tao
      ObjectList objectList = null;
1696
1697
      try {
1698
          // safeguard against large requests
1699
          if (count == null || count > MAXIMUM_DB_RECORD_COUNT) {
1700
              count = MAXIMUM_DB_RECORD_COUNT;
1701
          }
1702 9072 tao
          boolean isSid = false;
1703
          if(identifier != null) {
1704
              isSid = IdentifierManager.getInstance().systemMetadataSIDExists(identifier);
1705
          }
1706 9249 tao
          objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, objectFormatId, nodeId, start, count, identifier, isSid);
1707 9071 tao
      } catch (Exception e) {
1708
          throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
1709
      }
1710
1711
      return objectList;
1712
  }
1713
1714 9352 walker
1715 6256 cjones
  /**
1716 6255 cjones
   * Update a systemMetadata document
1717
   *
1718
   * @param sysMeta - the system metadata object in the system to update
1719
   */
1720 6858 cjones
    protected void updateSystemMetadata(SystemMetadata sysMeta)
1721
        throws ServiceFailure {
1722
        logMetacat.debug("D1NodeService.updateSystemMetadata() called.");
1723
        try {
1724
            HazelcastService.getInstance().getSystemMetadataMap().lock(sysMeta.getIdentifier());
1725 9335 tao
            boolean needUpdateModificationDate = true;
1726
            updateSystemMetadataWithoutLock(sysMeta, needUpdateModificationDate);
1727 6858 cjones
        } catch (Exception e) {
1728
            throw new ServiceFailure("4862", e.getMessage());
1729
        } finally {
1730
            HazelcastService.getInstance().getSystemMetadataMap().unlock(sysMeta.getIdentifier());
1731
1732
        }
1733
1734
    }
1735 8810 leinfelder
1736 9190 tao
    /**
1737 9335 tao
     * Update system metadata without locking the system metadata in hazelcast server. So the caller should lock it first.
1738
     * @param sysMeta
1739
     * @param needUpdateModificationDate
1740
     * @throws ServiceFailure
1741
     */
1742
    private void updateSystemMetadataWithoutLock(SystemMetadata sysMeta, boolean needUpdateModificationDate) throws ServiceFailure {
1743
        logMetacat.debug("D1NodeService.updateSystemMetadataWithoutLock() called.");
1744
        if(needUpdateModificationDate) {
1745
            logMetacat.debug("D1NodeService.updateSystemMetadataWithoutLock() - update the modification date.");
1746
            sysMeta.setDateSysMetadataModified(new Date());
1747
        }
1748
1749
        // submit for indexing
1750
        try {
1751
            HazelcastService.getInstance().getSystemMetadataMap().put(sysMeta.getIdentifier(), sysMeta);
1752
            MetacatSolrIndex.getInstance().submit(sysMeta.getIdentifier(), sysMeta, null, true);
1753
        } catch (Exception e) {
1754
            throw new ServiceFailure("4862", e.getMessage());
1755
            //logMetacat.warn("D1NodeService.updateSystemMetadataWithoutLock - we can't submit the change of the system metadata to the solr index since "+e.getMessage());
1756
        }
1757
    }
1758
1759
    /**
1760
     * Update the system metadata of the specified pid. The caller of this method should lock the system metadata in hazelcast server.
1761 9190 tao
     * @param session - the identity of the client which calls the method
1762
     * @param pid - the identifier of the object which will be updated
1763
     * @param sysmeta - the new system metadata
1764
     * @return
1765
     * @throws NotImplemented
1766
     * @throws NotAuthorized
1767
     * @throws ServiceFailure
1768
     * @throws InvalidRequest
1769
     * @throws InvalidSystemMetadata
1770
     * @throws InvalidToken
1771
     */
1772 9335 tao
	protected boolean updateSystemMetadata(Session session, Identifier pid,
1773 9354 tao
			SystemMetadata sysmeta, boolean needUpdateModificationDate, SystemMetadata currentSysmeta, boolean fromCN) throws NotImplemented, NotAuthorized,
1774 8810 leinfelder
			ServiceFailure, InvalidRequest, InvalidSystemMetadata, InvalidToken {
1775
1776 9335 tao
	  // The lock to be used for this identifier
1777 8810 leinfelder
      Lock lock = null;
1778 9190 tao
1779 8810 leinfelder
      // verify that guid == SystemMetadata.getIdentifier()
1780
      logMetacat.debug("Comparing guid|sysmeta_guid: " + pid.getValue() +
1781
          "|" + sysmeta.getIdentifier().getValue());
1782
1783
      if (!pid.getValue().equals(sysmeta.getIdentifier().getValue())) {
1784
          throw new InvalidRequest("4863",
1785
              "The identifier in method call (" + pid.getValue() +
1786
              ") does not match identifier in system metadata (" +
1787
              sysmeta.getIdentifier().getValue() + ").");
1788
      }
1789 9280 tao
      //compare serial version.
1790 9073 tao
1791
      //check the sid
1792 9335 tao
      //SystemMetadata currentSysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1793 9258 tao
      logMetacat.debug("The current dateUploaded is ============"+currentSysmeta.getDateUploaded());
1794
      logMetacat.debug("the dateUploaded in the new system metadata is "+sysmeta.getDateUploaded());
1795
      logMetacat.debug("The current dateUploaded is (by time) ============"+currentSysmeta.getDateUploaded().getTime());
1796
      logMetacat.debug("the dateUploaded in the new system metadata is (by time) "+sysmeta.getDateUploaded().getTime());
1797 9073 tao
      if(currentSysmeta == null ) {
1798 9284 tao
          //do we need throw an exception?
1799 9329 tao
          logMetacat.warn("D1NodeService.updateSystemMetadata: Currently there is no system metadata in this node associated with the pid "+pid.getValue());
1800 9073 tao
      } else {
1801 9284 tao
1802 9329 tao
          /*BigInteger newVersion = sysmeta.getSerialVersion();
1803 9284 tao
          if(newVersion == null) {
1804 9320 tao
              throw new InvalidRequest("4869", "The serial version can't be null in the new system metadata");
1805 9284 tao
          }
1806
          BigInteger currentVersion = currentSysmeta.getSerialVersion();
1807
          if(currentVersion != null && newVersion.compareTo(currentVersion) <= 0) {
1808 9320 tao
              throw new InvalidRequest("4869", "The serial version in the new system metadata is "+newVersion.toString()+
1809 9284 tao
                      " which is less than or equals the previous version "+currentVersion.toString()+". This is illegal in the updateSystemMetadata method.");
1810 9329 tao
          }*/
1811 9073 tao
          Identifier currentSid = currentSysmeta.getSeriesId();
1812
          if(currentSid != null) {
1813 9325 tao
              logMetacat.debug("In the branch that the sid is not null in the current system metadata and the current sid is "+currentSid.getValue());
1814 9073 tao
              //new sid must match the current sid
1815
              Identifier newSid = sysmeta.getSeriesId();
1816
              if (!isValidIdentifier(newSid)) {
1817 9320 tao
                  throw new InvalidRequest("4869", "The series id in the system metadata is invalid in the request.");
1818 9073 tao
              } else {
1819
                  if(!newSid.getValue().equals(currentSid.getValue())) {
1820 9320 tao
                      throw new InvalidRequest("4869", "The series id "+newSid.getValue() +" in the system metadata doesn't match the current sid "+currentSid.getValue());
1821 9073 tao
                  }
1822
              }
1823
          } else {
1824
              //current system metadata doesn't have a sid. So we can have those scenarios
1825
              //1. The new sid may be null as well
1826
              //2. If the new sid does exist, it may be an identifier which hasn't bee used.
1827
              //3. If the new sid does exist, it may be an sid which equals the SID it obsoletes
1828
              //4. If the new sid does exist, it may be an sid which equauls the SID it was obsoleted by
1829
              Identifier newSid = sysmeta.getSeriesId();
1830
              if(newSid != null) {
1831
                  //It matches the rules of the checkSidInModifyingSystemMetadata
1832
                  checkSidInModifyingSystemMetadata(sysmeta, "4956", "4868");
1833
              }
1834
          }
1835 9280 tao
          checkModifiedImmutableFields(currentSysmeta, sysmeta);
1836
          checkOneTimeSettableSysmMetaFields(currentSysmeta, sysmeta);
1837
          if(currentSysmeta.getObsoletes() == null && sysmeta.getObsoletes() != null) {
1838
              //we are setting a value to the obsoletes field, so we should make sure if there is not object obsoletes the value
1839
              String obsoletes = existsInObsoletes(sysmeta.getObsoletes());
1840
              if( obsoletes != null) {
1841
                  throw new InvalidSystemMetadata("4956", "There is an object with id "+obsoletes +
1842
                          " already obsoletes the pid "+sysmeta.getObsoletes().getValue() +". You can't set the object "+pid.getValue()+" to obsolete the pid "+sysmeta.getObsoletes().getValue()+" again.");
1843
              }
1844 9277 tao
          }
1845 10106 tao
          checkCircularObsoletesChain(sysmeta);
1846 9280 tao
          if(currentSysmeta.getObsoletedBy() == null && sysmeta.getObsoletedBy() != null) {
1847
              //we are setting a value to the obsoletedBy field, so we should make sure that the no another object obsoletes the pid we are updating.
1848
              String obsoletedBy = existsInObsoletedBy(sysmeta.getObsoletedBy());
1849
              if( obsoletedBy != null) {
1850
                  throw new InvalidSystemMetadata("4956", "There is an object with id "+obsoletedBy +
1851
                          " already is obsoleted by the pid "+sysmeta.getObsoletedBy().getValue() +". You can't set the pid "+pid.getValue()+" to be obsoleted by the pid "+sysmeta.getObsoletedBy().getValue()+" again.");
1852
              }
1853
          }
1854 10106 tao
          checkCircularObsoletedByChain(sysmeta);
1855 9277 tao
      }
1856
1857 8810 leinfelder
      // do the actual update
1858 9354 tao
      if(sysmeta.getArchived() != null && sysmeta.getArchived() == true &&
1859
                 ((currentSysmeta.getArchived() != null && currentSysmeta.getArchived() == false ) || currentSysmeta.getArchived() == null)) {
1860 9373 tao
          boolean logArchive = false;//we log it as the update system metadata event. So don't log it again.
1861 9354 tao
          if(fromCN) {
1862
              logMetacat.debug("D1Node.update - this is to archive a cn object "+pid.getValue());
1863
              try {
1864 9373 tao
                  archiveCNObject(logArchive, session, pid, sysmeta, needUpdateModificationDate);
1865 9354 tao
              } catch (NotFound e) {
1866
                  throw new InvalidRequest("4869", "Can't find the pid "+pid.getValue()+" for archive.");
1867
              }
1868
          } else {
1869
              logMetacat.debug("D1Node.update - this is to archive a MN object "+pid.getValue());
1870
              try {
1871 9373 tao
                  archiveObject(logArchive, session, pid, sysmeta, needUpdateModificationDate);
1872 9354 tao
              } catch (NotFound e) {
1873
                  throw new InvalidRequest("4869", "Can't find the pid "+pid.getValue()+" for archive.");
1874
              }
1875
          }
1876
      } else {
1877
          logMetacat.debug("D1Node.update - regularly update the system metadata of the pid "+pid.getValue());
1878
          updateSystemMetadataWithoutLock(sysmeta, needUpdateModificationDate);
1879
      }
1880
1881 8810 leinfelder
      try {
1882
    	  String localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
1883
    	  EventLog.getInstance().log(request.getRemoteAddr(),
1884
    	          request.getHeader("User-Agent"), session.getSubject().getValue(),
1885
    	          localId, "updateSystemMetadata");
1886
      } catch (McdbDocNotFoundException e) {
1887
    	  // do nothing, no localId to log with
1888
    	  logMetacat.warn("Could not log 'updateSystemMetadata' event because no localId was found for pid: " + pid.getValue());
1889 9024 tao
      } catch (SQLException e) {
1890
          logMetacat.warn("Could not log 'updateSystemMetadata' event because the localId couldn't be identified for the pid: " + pid.getValue());
1891 8810 leinfelder
      }
1892
      return true;
1893
	}
1894 9166 tao
1895 9176 tao
1896 9166 tao
	/*
1897
	 * Check if the newMeta modifies an immutable field.
1898
	 */
1899 9320 tao
	private void checkModifiedImmutableFields(SystemMetadata orgMeta, SystemMetadata newMeta) throws InvalidRequest{
1900 9325 tao
	    logMetacat.debug("in the start of the checkModifiedImmutableFields method");
1901 9166 tao
	    if(orgMeta != null && newMeta != null) {
1902 9325 tao
	        logMetacat.debug("in the checkModifiedImmutableFields method when the org and new system metadata is not null");
1903 9166 tao
	        if(newMeta.getIdentifier() == null) {
1904 9320 tao
	            throw new InvalidRequest("4869", "The new version of the system metadata is invalid since the identifier is null");
1905 9166 tao
	        }
1906
	        if(!orgMeta.getIdentifier().equals(newMeta.getIdentifier())) {
1907 9168 tao
	            throw new InvalidRequest("4869","The request is trying to modify an immutable field in the SystemMeta: the new system meta's identifier "+newMeta.getIdentifier().getValue()+" is "+
1908
	                  "different to the orginal one "+orgMeta.getIdentifier().getValue());
1909 9166 tao
	        }
1910
	        if(newMeta.getSize() == null) {
1911 9320 tao
	            throw new InvalidRequest("4869", "The new version of the system metadata is invalid since the size is null");
1912 9166 tao
	        }
1913
	        if(!orgMeta.getSize().equals(newMeta.getSize())) {
1914 9168 tao
	            throw new InvalidRequest("4869", "The request is trying to modify an immutable field in the SystemMeta: the new system meta's size "+newMeta.getSize().longValue()+" is "+
1915
	                      "different to the orginal one "+orgMeta.getSize().longValue());
1916 9166 tao
	        }
1917
	        if(newMeta.getChecksum()!= null && orgMeta.getChecksum() != null && !orgMeta.getChecksum().getValue().equals(newMeta.getChecksum().getValue())) {
1918
	            logMetacat.error("The request is trying to modify an immutable field in the SystemMeta: the new system meta's checksum "+newMeta.getChecksum().getValue()+" is "+
1919
                        "different to the orginal one "+orgMeta.getChecksum().getValue());
1920 9168 tao
	            throw new InvalidRequest("4869", "The request is trying to modify an immutable field in the SystemMeta: the new system meta's checksum "+newMeta.getChecksum().getValue()+" is "+
1921
                        "different to the orginal one "+orgMeta.getChecksum().getValue());
1922 9166 tao
	        }
1923 9325 tao
	        if(orgMeta.getSubmitter() != null) {
1924
	            logMetacat.debug("in the checkModifiedImmutableFields method and orgMeta.getSubmitter is not null and the orginal submiter is "+orgMeta.getSubmitter().getValue());
1925
	        }
1926
1927
	        if(newMeta.getSubmitter() != null) {
1928
                logMetacat.debug("in the checkModifiedImmutableFields method and newMeta.getSubmitter is not null and the submiter in the new system metadata is "+newMeta.getSubmitter().getValue());
1929
            }
1930 9166 tao
	        if(orgMeta.getSubmitter() != null && newMeta.getSubmitter() != null && !orgMeta.getSubmitter().equals(newMeta.getSubmitter())) {
1931 9168 tao
	            throw new InvalidRequest("4869", "The request is trying to modify an immutable field in the SystemMeta: the new system meta's submitter "+newMeta.getSubmitter().getValue()+" is "+
1932
                        "different to the orginal one "+orgMeta.getSubmitter().getValue());
1933 9166 tao
	        }
1934
1935 9258 tao
	        if(orgMeta.getDateUploaded() != null && newMeta.getDateUploaded() != null && orgMeta.getDateUploaded().getTime() != newMeta.getDateUploaded().getTime()) {
1936 9168 tao
	            throw new InvalidRequest("4869", "The request is trying to modify an immutable field in the SystemMeta: the new system meta's date of uploaded "+newMeta.getDateUploaded()+" is "+
1937
                        "different to the orginal one "+orgMeta.getDateUploaded());
1938 9166 tao
	        }
1939
1940
	        if(orgMeta.getOriginMemberNode() != null && newMeta.getOriginMemberNode() != null && !orgMeta.getOriginMemberNode().equals(newMeta.getOriginMemberNode())) {
1941 9168 tao
	            throw new InvalidRequest("4869", "The request is trying to modify an immutable field in the SystemMeta: the new system meta's orginal member node  "+newMeta.getOriginMemberNode().getValue()+" is "+
1942
                        "different to the orginal one "+orgMeta.getOriginMemberNode().getValue());
1943 9166 tao
	        }
1944
1945 9321 tao
	        if (orgMeta.getOriginMemberNode() != null && newMeta.getOriginMemberNode() == null ) {
1946
	            throw new InvalidRequest("4869", "The request is trying to modify an immutable field in the SystemMeta: the new system meta's orginal member node is null and it "+" is "+
1947
                        "different to the orginal one "+orgMeta.getOriginMemberNode().getValue());
1948
	        }
1949
1950 9166 tao
	        if(orgMeta.getSeriesId() != null && newMeta.getSeriesId() != null && !orgMeta.getSeriesId().equals(newMeta.getSeriesId())) {
1951 9168 tao
                throw new InvalidRequest("4869", "The request is trying to modify an immutable field in the SystemMeta: the new system meta's series id  "+newMeta.getSeriesId().getValue()+" is "+
1952
                        "different to the orginal one "+orgMeta.getSeriesId().getValue());
1953 9166 tao
            }
1954
1955
	    }
1956
	}
1957 9274 tao
1958
	/*
1959
	 * Some fields in the system metadata, such as obsoletes or obsoletedBy can be set only once.
1960
	 * After set, they are not allowed to be changed.
1961
	 */
1962
	private void checkOneTimeSettableSysmMetaFields(SystemMetadata orgMeta, SystemMetadata newMeta) throws InvalidRequest {
1963
	    if(orgMeta.getObsoletedBy() != null ) {
1964
	        if(newMeta.getObsoletedBy() == null || !orgMeta.getObsoletedBy().equals(newMeta.getObsoletedBy())) {
1965 9275 tao
	            throw new InvalidRequest("4869", "The request is trying to reset the obsoletedBy field in the system metadata of the object "
1966
	                    + orgMeta.getIdentifier().getValue() +". This is illegal since the obsoletedBy filed is set, you can't change it again.");
1967 9274 tao
	        }
1968
        }
1969
	    if(orgMeta.getObsoletes() != null) {
1970
	        if(newMeta.getObsoletes() == null || !orgMeta.getObsoletes().equals(newMeta.getObsoletes())) {
1971 9275 tao
	            throw new InvalidRequest("4869", "The request is trying to reset the obsoletes field in the system metadata of the object"+
1972 9277 tao
	               orgMeta.getIdentifier().getValue()+". This is illegal since the obsoletes filed is set, you can't change it again.");
1973 9274 tao
	        }
1974
	    }
1975
	}
1976 10106 tao
1977
1978
	/**
1979
	 * Try to check the scenario of a circular obsoletes chain:
1980
	 * A obsoletes B
1981
	 * B obsoletes C
1982
	 * C obsoletes A
1983
	 * @param sys
1984
	 * @throws InvalidRequest
1985
	 */
1986
	private void checkCircularObsoletesChain(SystemMetadata sys) throws InvalidRequest {
1987
	    if(sys != null && sys.getObsoletes() != null && sys.getObsoletes().getValue() != null && !sys.getObsoletes().getValue().trim().equals("")) {
1988
	        logMetacat.debug("D1NodeService.checkCircularObsoletesChain - the object "+sys.getIdentifier().getValue() +" obsoletes "+sys.getObsoletes().getValue());
1989
	        if(sys.getObsoletes().getValue().equals(sys.getIdentifier().getValue())) {
1990
	            // the obsoletes field points to itself and creates a circular chain
1991
	            throw new InvalidRequest("4869", "The obsoletes field and identifier of the system metadata has the same value "+sys.getObsoletes().getValue()+
1992
	                    ". This creates a circular chain and it is illegal.");
1993
	        } else {
1994
	            Vector <Identifier> pidList = new Vector<Identifier>();
1995
	            pidList.add(sys.getIdentifier());
1996
	            SystemMetadata obsoletesSym = HazelcastService.getInstance().getSystemMetadataMap().get(sys.getObsoletes());
1997
	            while (obsoletesSym != null && obsoletesSym.getObsoletes() != null && obsoletesSym.getObsoletes().getValue() != null && !obsoletesSym.getObsoletes().getValue().trim().equals("")) {
1998
	                pidList.add(obsoletesSym.getIdentifier());
1999
	                logMetacat.debug("D1NodeService.checkCircularObsoletesChain - the object "+obsoletesSym.getIdentifier().getValue() +" obsoletes "+obsoletesSym.getObsoletes().getValue());
2000
	                /*for(Identifier id: pidList) {
2001
	                    logMetacat.debug("D1NodeService.checkCircularObsoletesChain - the pid in the chanin"+id.getValue());
2002
	                }*/
2003
	                if(pidList.contains(obsoletesSym.getObsoletes())) {
2004
	                    logMetacat.error("D1NodeService.checkCircularObsoletesChain - when Metacat updated the system metadata of object "+sys.getIdentifier().getValue()+", it found the obsoletes field value "+sys.getObsoletes().getValue()+
2005
	                            " in its new system metadata creating a circular chain at the object "+obsoletesSym.getObsoletes().getValue()+". This is illegal");
2006
	                    throw new InvalidRequest("4869", "When Metacat updated the system metadata of object "+sys.getIdentifier().getValue()+", it found the obsoletes field value "+sys.getObsoletes().getValue()+
2007
                                " in its new system metadata creating a circular chain at the object "+obsoletesSym.getObsoletes().getValue()+". This is illegal");
2008
	                } else {
2009
	                    obsoletesSym = HazelcastService.getInstance().getSystemMetadataMap().get(obsoletesSym.getObsoletes());
2010
	                }
2011
	            }
2012
	        }
2013
	    }
2014
	}
2015
2016
2017
	/**
2018
     * Try to check the scenario of a circular obsoletedBy chain:
2019
     * A obsoletedBy B
2020
     * B obsoletedBy C
2021
     * C obsoletedBy A
2022
     * @param sys
2023
     * @throws InvalidRequest
2024
     */
2025
    private void checkCircularObsoletedByChain(SystemMetadata sys) throws InvalidRequest {
2026
        if(sys != null && sys.getObsoletedBy() != null && sys.getObsoletedBy().getValue() != null && !sys.getObsoletedBy().getValue().trim().equals("")) {
2027
            logMetacat.debug("D1NodeService.checkCircularObsoletedByChain - the object "+sys.getIdentifier().getValue() +" is obsoletedBy "+sys.getObsoletedBy().getValue());
2028
            if(sys.getObsoletedBy().getValue().equals(sys.getIdentifier().getValue())) {
2029
                // the obsoletedBy field points to itself and creates a circular chain
2030
                throw new InvalidRequest("4869", "The obsoletedBy field and identifier of the system metadata has the same value "+sys.getObsoletedBy().getValue()+
2031
                        ". This creates a circular chain and it is illegal.");
2032
            } else {
2033
                Vector <Identifier> pidList = new Vector<Identifier>();
2034
                pidList.add(sys.getIdentifier());
2035
                SystemMetadata obsoletedBySym = HazelcastService.getInstance().getSystemMetadataMap().get(sys.getObsoletedBy());
2036
                while (obsoletedBySym != null && obsoletedBySym.getObsoletedBy() != null && obsoletedBySym.getObsoletedBy().getValue() != null && !obsoletedBySym.getObsoletedBy().getValue().trim().equals("")) {
2037
                    pidList.add(obsoletedBySym.getIdentifier());
2038
                    logMetacat.debug("D1NodeService.checkCircularObsoletedByChain - the object "+obsoletedBySym.getIdentifier().getValue() +" is obsoletedBy "+obsoletedBySym.getObsoletedBy().getValue());
2039
                    /*for(Identifier id: pidList) {
2040
                        logMetacat.debug("D1NodeService.checkCircularObsoletedByChain - the pid in the chanin"+id.getValue());
2041
                    }*/
2042
                    if(pidList.contains(obsoletedBySym.getObsoletedBy())) {
2043
                        logMetacat.error("D1NodeService.checkCircularObsoletedByChain - When Metacat updated the system metadata of object "+sys.getIdentifier().getValue()+", it found the obsoletedBy field value "+sys.getObsoletedBy().getValue()+
2044
                                " in its new system metadata creating a circular chain at the object "+obsoletedBySym.getObsoletedBy().getValue()+". This is illegal");
2045
                        throw new InvalidRequest("4869",  "When Metacat updated the system metadata of object "+sys.getIdentifier().getValue()+", it found the obsoletedBy field value "+sys.getObsoletedBy().getValue()+
2046
                                " in its new system metadata creating a circular chain at the object "+obsoletedBySym.getObsoletedBy().getValue()+". This is illegal");
2047
                    } else {
2048
                        obsoletedBySym = HazelcastService.getInstance().getSystemMetadataMap().get(obsoletedBySym.getObsoletedBy());
2049
                    }
2050
                }
2051
            }
2052
        }
2053
    }
2054 6816 leinfelder
2055
  /**
2056
   * Given a Permission, returns a list of all permissions that it encompasses
2057
   * Permissions are hierarchical so that WRITE also allows READ.
2058
   * @param permission
2059
   * @return list of included Permissions for the given permission
2060
   */
2061 9452 leinfelder
  protected static List<Permission> expandPermissions(Permission permission) {
2062 6816 leinfelder
	  	List<Permission> expandedPermissions = new ArrayList<Permission>();
2063
	    if (permission.equals(Permission.READ)) {
2064
	    	expandedPermissions.add(Permission.READ);
2065
	    }
2066
	    if (permission.equals(Permission.WRITE)) {
2067
	    	expandedPermissions.add(Permission.READ);
2068
	    	expandedPermissions.add(Permission.WRITE);
2069
	    }
2070
	    if (permission.equals(Permission.CHANGE_PERMISSION)) {
2071
	    	expandedPermissions.add(Permission.READ);
2072
	    	expandedPermissions.add(Permission.WRITE);
2073
	    	expandedPermissions.add(Permission.CHANGE_PERMISSION);
2074
	    }
2075
	    return expandedPermissions;
2076
  }
2077 6255 cjones
2078
  /*
2079 6241 cjones
   * Write a stream to a file
2080
   *
2081
   * @param dir - the directory to write to
2082
   * @param fileName - the file name to write to
2083
   * @param data - the object bytes as an input stream
2084
   *
2085
   * @return newFile - the new file created
2086
   *
2087
   * @throws ServiceFailure
2088
   */
2089 10272 tao
  private File writeStreamToFile(File dir, String fileName, InputStream dataStream, Checksum checksum, Identifier pid)
2090
    throws ServiceFailure, InvalidSystemMetadata {
2091 6241 cjones
2092
    File newFile = new File(dir, fileName);
2093 10272 tao
    logMetacat.debug("Filename for write is: " + newFile.getAbsolutePath()+" for the data object pid "+pid.getValue());
2094 6241 cjones
2095
    try {
2096
        if (newFile.createNewFile()) {
2097 10288 tao
            String checksumValue = checksum.getValue();
2098
            logMetacat.info("D1NodeService.writeStreamToFile - the checksum value from the system metadata is "+checksumValue+" for the data object "+pid.getValue());
2099
            if(checksumValue == null || checksumValue.trim().equals("")) {
2100
                logMetacat.error("D1NodeService.writeStreamToFile - the checksum value from the system metadata shouldn't be null or blank for the data object "+pid.getValue());
2101
                throw new InvalidSystemMetadata("1180", "The checksum value from the system metadata shouldn't be null or blank.");
2102
            }
2103
            String algorithm = checksum.getAlgorithm();
2104
            logMetacat.info("D1NodeService.writeStreamToFile - the algorithm to calculate the checksum from the system metadata is "+algorithm+" for the data object "+pid.getValue());
2105
            if(algorithm == null || algorithm.trim().equals("")) {
2106
                logMetacat.error("D1NodeService.writeStreamToFile - the algorithm to calculate the checksum from the system metadata shouldn't be null or blank for the data object "+pid.getValue());
2107
                throw new InvalidSystemMetadata("1180", "The algorithm to calculate the checksum from the system metadata shouldn't be null or blank.");
2108
            }
2109 10272 tao
          MessageDigest md = MessageDigest.getInstance(algorithm);
2110 6241 cjones
          // write data stream to desired file
2111 10272 tao
          DigestOutputStream os = new DigestOutputStream( new FileOutputStream(newFile), md);
2112 9783 leinfelder
          long length = IOUtils.copyLarge(dataStream, os);
2113 6241 cjones
          os.flush();
2114
          os.close();
2115 10272 tao
          String localChecksum = DatatypeConverter.printHexBinary(md.digest());
2116
          logMetacat.info("D1NodeService.writeStreamToFile - the check sum calculated from the saved local file is "+localChecksum);
2117
          if(localChecksum == null || localChecksum.trim().equals("") || !localChecksum.equalsIgnoreCase(checksumValue)) {
2118
              logMetacat.error("D1NodeService.writeStreamToFile - the check sum calculated from the saved local file is "+localChecksum+ ". But it doesn't match the value from the system metadata "+checksumValue+" for the object "+pid.getValue());
2119
              boolean success = newFile.delete();
2120
              logMetacat.info("delete the file "+newFile.getAbsolutePath()+" for the object "+pid.getValue()+" sucessfully?"+success);
2121
              throw new InvalidSystemMetadata("1180", "The checksum calculated from the saved local file is "+localChecksum+ ". But it doesn't match the value from the system metadata "+checksumValue+".");
2122
          }
2123
2124 6241 cjones
        } else {
2125
          logMetacat.debug("File creation failed, or file already exists.");
2126
          throw new ServiceFailure("1190", "File already exists: " + fileName);
2127
        }
2128
    } catch (FileNotFoundException e) {
2129 10272 tao
      logMetacat.error("FNF: " + e.getMessage()+" for the data object "+pid.getValue(), e);
2130 6241 cjones
      throw new ServiceFailure("1190", "File not found: " + fileName + " "
2131
                + e.getMessage());
2132
    } catch (IOException e) {
2133 10272 tao
      logMetacat.error("IOE: " + e.getMessage()+" for the data object "+pid.getValue(), e);
2134 6241 cjones
      throw new ServiceFailure("1190", "File was not written: " + fileName
2135
                + " " + e.getMessage());
2136 10272 tao
    } catch (NoSuchAlgorithmException e) {
2137
        logMetacat.error("D1NodeService.writeStreamToFile - no such checksum algorithm exception " + e.getMessage()+" for the data object "+pid.getValue(), e);
2138
        throw new ServiceFailure("1190", "No such checksum algorithm: "
2139
                + " " + e.getMessage());
2140 9783 leinfelder
    } finally {
2141
        IOUtils.closeQuietly(dataStream);
2142 6241 cjones
    }
2143
2144
    return newFile;
2145
  }
2146 7132 cjones
2147
  /*
2148
   * Returns a list of nodes that have been registered with the DataONE infrastructure
2149
   * that match the given session subject
2150
   * @return nodes - List of nodes from the registry with a matching session subject
2151
   *
2152
   * @throws ServiceFailure
2153
   * @throws NotImplemented
2154
   */
2155
  protected List<Node> listNodesBySubject(Subject subject)
2156
      throws ServiceFailure, NotImplemented {
2157 7136 leinfelder
      List<Node> nodeList = new ArrayList<Node>();
2158 7132 cjones
2159
      CNode cn = D1Client.getCN();
2160
      List<Node> nodes = cn.listNodes().getNodeList();
2161
2162
      // find the node in the node list
2163
      for ( Node node : nodes ) {
2164
2165
          List<Subject> nodeSubjects = node.getSubjectList();
2166 7139 leinfelder
          if (nodeSubjects != null) {
2167
	          // check if the session subject is in the node subject list
2168
	          for (Subject nodeSubject : nodeSubjects) {
2169
	              if ( nodeSubject.equals(subject) ) { // subject of session == node subject
2170
	                  nodeList.add(node);
2171
	              }
2172
	          }
2173
          }
2174 7132 cjones
      }
2175
2176
      return nodeList;
2177
2178
  }
2179
2180 7159 leinfelder
  /**
2181 7148 leinfelder
   * Archives an object, where the object is either a
2182
   * data object or a science metadata object.
2183 9353 tao
   * Note: it doesn't check the authorization; it doesn't lock the system metadata;it only accept pid.
2184 7148 leinfelder
   * @param session - the Session object containing the credentials for the Subject
2185
   * @param pid - The object identifier to be archived
2186
   *
2187
   * @return pid - the identifier of the object used for the archiving
2188
   *
2189
   * @throws InvalidToken
2190
   * @throws ServiceFailure
2191
   * @throws NotAuthorized
2192
   * @throws NotFound
2193
   * @throws NotImplemented
2194
   * @throws InvalidRequest
2195
   */
2196 9373 tao
  protected Identifier archiveObject(boolean log, Session session, Identifier pid, SystemMetadata sysMeta, boolean needModifyDate)
2197 7148 leinfelder
      throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
2198 7132 cjones
2199 7148 leinfelder
      String localId = null;
2200
      boolean allowed = false;
2201
      String username = Constants.SUBJECT_PUBLIC;
2202
      if (session == null) {
2203
      	throw new InvalidToken("1330", "No session has been provided");
2204
      } else {
2205
          username = session.getSubject().getValue();
2206
      }
2207
      // do we have a valid pid?
2208
      if (pid == null || pid.getValue().trim().equals("")) {
2209
          throw new ServiceFailure("1350", "The provided identifier was invalid.");
2210
      }
2211 9050 tao
2212 9353 tao
      if(sysMeta == null) {
2213
          throw new NotFound("2911", "There is no system metadata associated with "+pid.getValue());
2214 9050 tao
      }
2215 9353 tao
2216 7148 leinfelder
      // check for the existing identifier
2217
      try {
2218
          localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
2219
      } catch (McdbDocNotFoundException e) {
2220
          throw new NotFound("1340", "The object with the provided " + "identifier was not found.");
2221 9024 tao
      } catch (SQLException e) {
2222
          throw new ServiceFailure("1350", "The object with the provided identifier "+pid.getValue()+" couldn't be identified since "+e.getMessage());
2223 7148 leinfelder
      }
2224
2225
2226
          try {
2227
              // archive the document
2228 7236 cjones
              DocumentImpl.delete(localId, null, null, null, false);
2229 9373 tao
              if(log) {
2230
                   try {
2231
                      EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), username, localId, Event.DELETE.xmlValue());
2232
                   } catch (Exception e) {
2233
                      logMetacat.warn("D1NodeService.archiveObject - can't log the delete event since "+e.getMessage());
2234
                   }
2235 9353 tao
              }
2236 9373 tao
2237 9353 tao
2238 7148 leinfelder
              // archive it
2239
              sysMeta.setArchived(true);
2240 9354 tao
              if(needModifyDate) {
2241
                  sysMeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
2242
                  sysMeta.setSerialVersion(sysMeta.getSerialVersion().add(BigInteger.ONE));
2243
              }
2244 7148 leinfelder
              HazelcastService.getInstance().getSystemMetadataMap().put(pid, sysMeta);
2245 9350 tao
2246 7812 leinfelder
              // submit for indexing
2247 7862 leinfelder
              // DocumentImpl call above should do this.
2248
              // see: https://projects.ecoinformatics.org/ecoinfo/issues/6030
2249
              //HazelcastService.getInstance().getIndexQueue().add(sysMeta);
2250 7148 leinfelder
2251
          } catch (McdbDocNotFoundException e) {
2252 10186 tao
              try {
2253
                  AccessionNumber acc = new AccessionNumber(localId, "NOACTION");
2254
                  String docid = acc.getDocid();
2255
                  int rev = 1;
2256
                  if (acc.getRev() != null) {
2257
                    rev = (new Integer(acc.getRev()).intValue());
2258
                  }
2259
                  if(IdentifierManager.getInstance().existsInXmlLRevisionTable(docid, rev)) {
2260
                      //somehow the document is in the xml_revision table.
2261
                      // archive it
2262
                      sysMeta.setArchived(true);
2263
                      if(needModifyDate) {
2264
                          sysMeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
2265
                          sysMeta.setSerialVersion(sysMeta.getSerialVersion().add(BigInteger.ONE));
2266
                      }
2267
                      HazelcastService.getInstance().getSystemMetadataMap().put(pid, sysMeta);
2268
                  } else {
2269
                      throw new NotFound("1340", "The provided identifier "+ pid.getValue()+" is invalid");
2270
                  }
2271
              } catch (SQLException ee) {
2272
                  ee.printStackTrace();
2273
                  throw new NotFound("1340", "The provided identifier "+ pid.getValue()+" is invalid");
2274
              } catch (AccessionNumberException ee) {
2275
                  ee.printStackTrace();
2276
                  throw new NotFound("1340", "The provided identifier "+ pid.getValue()+" is invalid");
2277
              }
2278 7148 leinfelder
          } catch (SQLException e) {
2279
              throw new ServiceFailure("1350", "There was a problem archiving the object." + "The error message was: " + e.getMessage());
2280
2281
          } catch (InsufficientKarmaException e) {
2282
              throw new NotAuthorized("1320", "The provided identity does not have " + "permission to archive this object.");
2283
2284
          } catch (Exception e) { // for some reason DocumentImpl throws a general Exception
2285
              throw new ServiceFailure("1350", "There was a problem archiving the object." + "The error message was: " + e.getMessage());
2286 9353 tao
          }
2287 7148 leinfelder
2288
2289
      return pid;
2290
  }
2291 9033 tao
2292 9354 tao
  /**
2293
   * Archive a object on cn and notify the replica. This method doesn't lock the system metadata map. The caller should lock it.
2294
   * This method doesn't check the authorization; this method only accept a pid.
2295
   * It wouldn't notify the replca that the system metadata has been changed.
2296
   * @param session
2297
   * @param pid
2298
   * @param sysMeta
2299
   * @param notifyReplica
2300
   * @return
2301
   * @throws InvalidToken
2302
   * @throws ServiceFailure
2303
   * @throws NotAuthorized
2304
   * @throws NotFound
2305
   * @throws NotImplemented
2306
   */
2307 9373 tao
  protected void archiveCNObject(boolean log, Session session, Identifier pid, SystemMetadata sysMeta, boolean needModifyDate)
2308 9354 tao
          throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
2309
2310
          String localId = null; // The corresponding docid for this pid
2311
2312
          // Check for the existing identifier
2313
          try {
2314
              localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
2315 9373 tao
              archiveObject(log, session, pid, sysMeta, needModifyDate);
2316 9354 tao
2317
          } catch (McdbDocNotFoundException e) {
2318
              // This object is not registered in the identifier table. Assume it is of formatType DATA,
2319
              // and set the archive flag. (i.e. the *object* doesn't exist on the CN)
2320
2321
              try {
2322
                  if ( sysMeta != null ) {
2323
                    sysMeta.setArchived(true);
2324
                    if (needModifyDate) {
2325
                        sysMeta.setSerialVersion(sysMeta.getSerialVersion().add(BigInteger.ONE));
2326
                        sysMeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
2327
                    }
2328
                    HazelcastService.getInstance().getSystemMetadataMap().put(pid, sysMeta);
2329
2330
                  } else {
2331
                      throw new ServiceFailure("4972", "Couldn't archive the object " + pid.getValue() +
2332
                          ". Couldn't obtain the system metadata record.");
2333
2334
                  }
2335
2336
              } catch (RuntimeException re) {
2337
                  throw new ServiceFailure("4972", "Couldn't archive " + pid.getValue() +
2338
                      ". The error message was: " + re.getMessage());
2339
2340
              }
2341
2342
          } catch (SQLException e) {
2343
              throw new ServiceFailure("4972", "Couldn't archive the object " + pid.getValue() +
2344
                      ". The local id of the object with the identifier can't be identified since "+e.getMessage());
2345
          }
2346
2347
    }
2348 9033 tao
2349 9354 tao
2350 9033 tao
  /**
2351
   * A utility method for v1 api to check the specified identifier exists as a pid
2352
   * @param identifier  the specified identifier
2353
   * @param serviceFailureCode  the detail error code for the service failure exception
2354
   * @param noFoundCode  the detail error code for the not found exception
2355
   * @throws ServiceFailure
2356
   * @throws NotFound
2357
   */
2358 9036 tao
  public void checkV1SystemMetaPidExist(Identifier identifier, String serviceFailureCode, String serviceFailureMessage,
2359
          String noFoundCode, String notFoundMessage) throws ServiceFailure, NotFound {
2360 9033 tao
      boolean exists = false;
2361
      try {
2362
          exists = IdentifierManager.getInstance().systemMetadataPIDExists(identifier);
2363
      } catch (SQLException e) {
2364 9036 tao
          throw new ServiceFailure(serviceFailureCode, serviceFailureMessage+" since "+e.getMessage());
2365 9033 tao
      }
2366
      if(!exists) {
2367 9041 tao
         //the v1 method only handles a pid. so it should throw a not-found exception.
2368
          // check if the pid was deleted.
2369
          try {
2370
              String localId = IdentifierManager.getInstance().getLocalId(identifier.getValue());
2371
              if(EventLog.getInstance().isDeleted(localId)) {
2372
                  notFoundMessage=notFoundMessage+" "+DELETEDMESSAGE;
2373
              }
2374
            } catch (Exception e) {
2375
              logMetacat.info("Couldn't determine if the not-found identifier "+identifier.getValue()+" was deleted since "+e.getMessage());
2376
            }
2377
            throw new NotFound(noFoundCode, notFoundMessage);
2378 9033 tao
      }
2379
  }
2380
2381
  /**
2382
   * Utility method to get the PID for an SID. If the specified identifier is not an SID
2383
   * , null will be returned.
2384
   * @param sid  the specified sid
2385
   * @param serviceFailureCode  the detail error code for the service failure exception
2386
   * @return the pid for the sid. If the specified identifier is not an SID, null will be returned.
2387
   * @throws ServiceFailure
2388
   */
2389
  protected Identifier getPIDForSID(Identifier sid, String serviceFailureCode) throws ServiceFailure {
2390
      Identifier id = null;
2391 9036 tao
      String serviceFailureMessage = "The PID "+" couldn't be identified for the sid " + sid.getValue();
2392 9033 tao
      try {
2393
          //determine if the given pid is a sid or not.
2394
          if(IdentifierManager.getInstance().systemMetadataSIDExists(sid)) {
2395
              try {
2396
                  //set the header pid for the sid if the identifier is a sid.
2397
                  id = IdentifierManager.getInstance().getHeadPID(sid);
2398
              } catch (SQLException sqle) {
2399 9036 tao
                  throw new ServiceFailure(serviceFailureCode, serviceFailureMessage+" since "+sqle.getMessage());
2400 9033 tao
              }
2401
2402
          }
2403
      } catch (SQLException e) {
2404 9036 tao
          throw new ServiceFailure(serviceFailureCode, serviceFailureMessage + " since "+e.getMessage());
2405 9033 tao
      }
2406
      return id;
2407
  }
2408 7148 leinfelder
2409 9073 tao
  /*
2410
   * Determine if the sid is legitimate in CN.create and CN.registerSystemMetadata methods. It also is used as a part of rules of the updateSystemMetadata method. Here are the rules:
2411
   * A. If the sysmeta doesn't have an SID, nothing needs to be checked for the SID.
2412
   * B. If the sysmeta does have an SID, it may be an identifier which doesn't exist in the system.
2413
   * C. If the sysmeta does have an SID and it exists as an SID in the system, those scenarios are acceptable:
2414
   *    i. The sysmeta has an obsoletes field, the SID has the same value as the SID of the system metadata of the obsoleting pid.
2415
   *    ii. The sysmeta has an obsoletedBy field, the SID has the same value as the SID of the system metadata of the obsoletedBy pid.
2416
   */
2417
  protected boolean checkSidInModifyingSystemMetadata(SystemMetadata sysmeta, String invalidSystemMetadataCode, String serviceFailureCode) throws InvalidSystemMetadata, ServiceFailure{
2418
      boolean pass = false;
2419
      if(sysmeta == null) {
2420
          throw new InvalidSystemMetadata(invalidSystemMetadataCode, "The system metadata is null in the request.");
2421
      }
2422
      Identifier sid = sysmeta.getSeriesId();
2423
      if(sid != null) {
2424
          // the series id exists
2425
          if (!isValidIdentifier(sid)) {
2426
              throw new InvalidSystemMetadata(invalidSystemMetadataCode, "The series id in the system metadata is invalid in the request.");
2427
          }
2428
          Identifier pid = sysmeta.getIdentifier();
2429
          if (!isValidIdentifier(pid)) {
2430
              throw new InvalidSystemMetadata(invalidSystemMetadataCode, "The pid in the system metadata is invalid in the request.");
2431
          }
2432
          //the series id equals the pid (new pid hasn't been registered in the system, so IdentifierManager.getInstance().identifierExists method can't exclude this scenario )
2433
          if(sid.getValue().equals(pid.getValue())) {
2434
              throw new InvalidSystemMetadata(invalidSystemMetadataCode, "The series id "+sid.getValue()+" in the system metadata shouldn't have the same value of the pid.");
2435
          }
2436
          try {
2437
              if (IdentifierManager.getInstance().identifierExists(sid.getValue())) {
2438
                  //the sid exists in system
2439
                  if(sysmeta.getObsoletes() != null) {
2440
                      SystemMetadata obsoletesSysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(sysmeta.getObsoletes());
2441
                      if(obsoletesSysmeta != null) {
2442
                          Identifier obsoletesSid = obsoletesSysmeta.getSeriesId();
2443
                          if(obsoletesSid != null && obsoletesSid.getValue() != null && !obsoletesSid.getValue().trim().equals("")) {
2444
                              if(sid.getValue().equals(obsoletesSid.getValue())) {
2445
                                  pass = true;// the i of rule C
2446
                              }
2447
                          }
2448
                      } else {
2449
                           logMetacat.warn("D1NodeService.checkSidInModifyingSystemMetacat - Can't find the system metadata for the pid "+sysmeta.getObsoletes().getValue()+
2450
                                                                         " which is the value of the obsoletes. So we can't check if the sid " +sid.getValue()+" is legitimate ");
2451
                      }
2452
                  }
2453
                  if(!pass) {
2454
                      // the sid doesn't match the sid of the obsoleting identifier. So we check the obsoletedBy
2455
                      if(sysmeta.getObsoletedBy() != null) {
2456
                          SystemMetadata obsoletedBySysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(sysmeta.getObsoletedBy());
2457
                          if(obsoletedBySysmeta != null) {
2458
                              Identifier obsoletedBySid = obsoletedBySysmeta.getSeriesId();
2459
                              if(obsoletedBySid != null && obsoletedBySid.getValue() != null && !obsoletedBySid.getValue().trim().equals("")) {
2460
                                  if(sid.getValue().equals(obsoletedBySid.getValue())) {
2461
                                      pass = true;// the ii of the rule C
2462
                                  }
2463
                              }
2464
                          } else {
2465
                              logMetacat.warn("D1NodeService.checkSidInModifyingSystemMetacat - Can't find the system metadata for the pid "+sysmeta.getObsoletes().getValue()
2466
                                                                            +" which is the value of the obsoletedBy. So we can't check if the sid "+sid.getValue()+" is legitimate.");
2467
                          }
2468
                      }
2469
                  }
2470
                  if(!pass) {
2471
                      throw new InvalidSystemMetadata(invalidSystemMetadataCode, "The series id "+sid.getValue()+
2472
                              " in the system metadata exists in the system. And it doesn't match either previous object's sid or the next object's sid.");
2473
                  }
2474
              } else {
2475
                  pass = true; //Rule B
2476
              }
2477
          } catch (SQLException e) {
2478
              throw new ServiceFailure(serviceFailureCode, "Can't determine if the sid in the system metadata is unique or not since "+e.getMessage());
2479
          }
2480
2481
      } else {
2482
          //no sid. Rule A.
2483
          pass = true;
2484
      }
2485
      return pass;
2486
2487
  }
2488 9146 tao
2489
  //@Override
2490
  public OptionList listViews(Session arg0) throws InvalidToken,
2491
          ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented {
2492
      OptionList views = new OptionList();
2493 9148 tao
      views.setKey("views");
2494
      views.setDescription("List of views for objects on the node");
2495 9146 tao
      Vector<String> skinNames = null;
2496
      try {
2497
          skinNames = SkinUtil.getSkinNames();
2498
      } catch (PropertyNotFoundException e) {
2499
          throw new ServiceFailure("2841", e.getMessage());
2500
      }
2501
      for (String skinName: skinNames) {
2502
          views.addOption(skinName);
2503
      }
2504
      return views;
2505
  }
2506
2507
  public OptionList listViews() throws InvalidToken,
2508
  ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented {
2509
      return listViews(null);
2510
  }
2511 7148 leinfelder
2512 9146 tao
  //@Override
2513
  public InputStream view(Session session, String format, Identifier pid)
2514
          throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
2515
          NotImplemented, NotFound {
2516
      InputStream resultInputStream = null;
2517
2518
      String serviceFailureCode = "2831";
2519
      Identifier sid = getPIDForSID(pid, serviceFailureCode);
2520
      if(sid != null) {
2521
          pid = sid;
2522
      }
2523
2524
      SystemMetadata sysMeta = this.getSystemMetadata(session, pid);
2525
      InputStream object = this.get(session, pid);
2526
2527
      try {
2528
          // can only transform metadata, really
2529
          ObjectFormat objectFormat = ObjectFormatCache.getInstance().getFormat(sysMeta.getFormatId());
2530
          if (objectFormat.getFormatType().equals("METADATA")) {
2531
              // transform
2532
              DBTransform transformer = new DBTransform();
2533
              String documentContent = IOUtils.toString(object, "UTF-8");
2534
              String sourceType = objectFormat.getFormatId().getValue();
2535
              String targetType = "-//W3C//HTML//EN";
2536
              ByteArrayOutputStream baos = new ByteArrayOutputStream();
2537
              Writer writer = new OutputStreamWriter(baos , "UTF-8");
2538
              // TODO: include more params?
2539
              Hashtable<String, String[]> params = new Hashtable<String, String[]>();
2540
              String localId = null;
2541
              try {
2542
                  localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
2543
              } catch (McdbDocNotFoundException e) {
2544
                  throw new NotFound("1020", e.getMessage());
2545
              }
2546
              params.put("qformat", new String[] {format});
2547
              params.put("docid", new String[] {localId});
2548
              params.put("pid", new String[] {pid.getValue()});
2549
              transformer.transformXMLDocument(
2550
                      documentContent ,
2551
                      sourceType,
2552
                      targetType ,
2553
                      format,
2554
                      writer,
2555
                      params,
2556
                      null //sessionid
2557
                      );
2558
2559
              // finally, get the HTML back
2560
              resultInputStream = new ContentTypeByteArrayInputStream(baos.toByteArray());
2561
              ((ContentTypeByteArrayInputStream) resultInputStream).setContentType("text/html");
2562
2563
          } else {
2564
              // just return the raw bytes
2565
              resultInputStream = object;
2566
          }
2567
      } catch (IOException e) {
2568
          // report as service failure
2569
          ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
2570
          sf.initCause(e);
2571
          throw sf;
2572
      } catch (PropertyNotFoundException e) {
2573
          // report as service failure
2574
          ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
2575
          sf.initCause(e);
2576
          throw sf;
2577
      } catch (SQLException e) {
2578
          // report as service failure
2579
          ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
2580
          sf.initCause(e);
2581
          throw sf;
2582
      } catch (ClassNotFoundException e) {
2583
          // report as service failure
2584
          ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
2585
          sf.initCause(e);
2586
          throw sf;
2587
      }
2588
2589
      return resultInputStream;
2590 9277 tao
  }
2591
2592
  /*
2593
   * Determine if the given identifier exists in the obsoletes field in the system metadata table.
2594
   * If the return value is not null, the given identifier exists in the given cloumn. The return value is
2595
   * the guid of the first row.
2596
   */
2597
  protected String existsInObsoletes(Identifier id) throws InvalidRequest, ServiceFailure{
2598
      String guid = existsInFields("obsoletes", id);
2599
      return guid;
2600
  }
2601
2602
  /*
2603
   * Determine if the given identifier exists in the obsoletes field in the system metadata table.
2604
   * If the return value is not null, the given identifier exists in the given cloumn. The return value is
2605
   * the guid of the first row.
2606
   */
2607
  protected String existsInObsoletedBy(Identifier id) throws InvalidRequest, ServiceFailure{
2608
      String guid = existsInFields("obsoleted_by", id);
2609
      return guid;
2610
  }
2611 9146 tao
2612 9277 tao
  /*
2613
   * Determine if the given identifier exists in the given column in the system metadata table.
2614
   * If the return value is not null, the given identifier exists in the given cloumn. The return value is
2615
   * the guid of the first row.
2616
   */
2617
  private String existsInFields(String column, Identifier id) throws InvalidRequest, ServiceFailure {
2618
      String guid = null;
2619
      if(id == null ) {
2620
          throw new InvalidRequest("4863", "The given identifier is null and we can't determine if the guid exists in the field "+column+" in the systemmetadata table");
2621
      }
2622
      String sql = "SELECT guid FROM systemmetadata WHERE "+column+" = ?";
2623
      int serialNumber = -1;
2624
      DBConnection dbConn = null;
2625
      PreparedStatement stmt = null;
2626
      ResultSet result = null;
2627
      try {
2628
          dbConn =
2629
                  DBConnectionPool.getDBConnection("D1NodeService.existsInFields");
2630
          serialNumber = dbConn.getCheckOutSerialNumber();
2631
          stmt = dbConn.prepareStatement(sql);
2632
          stmt.setString(1, id.getValue());
2633
          result = stmt.executeQuery();
2634
          if(result.next()) {
2635
              guid = result.getString(1);
2636
          }
2637
          stmt.close();
2638
      } catch (SQLException e) {
2639
          e.printStackTrace();
2640
          throw new ServiceFailure("4862","We can't determine if the id "+id.getValue()+" exists in field "+column+" in the systemmetadata table since "+e.getMessage());
2641
      } finally {
2642
          // Return database connection to the pool
2643
          DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2644
          if(stmt != null) {
2645
              try {
2646
                  stmt.close();
2647
              } catch (SQLException e) {
2648
                  logMetacat.warn("We can close the PreparedStatment in D1NodeService.existsInFields since "+e.getMessage());
2649
              }
2650
          }
2651
2652
      }
2653
      return guid;
2654
2655
  }
2656 9781 cjones
}