package edu.harvard.iq.dataverse.api.datadeposit;
import edu.harvard.iq.dataverse.Dataset;
import edu.harvard.iq.dataverse.DatasetFieldServiceBean;
import edu.harvard.iq.dataverse.DatasetServiceBean;
import edu.harvard.iq.dataverse.DatasetVersion;
import edu.harvard.iq.dataverse.Dataverse;
import edu.harvard.iq.dataverse.DataverseServiceBean;
import edu.harvard.iq.dataverse.EjbDataverseEngine;
import edu.harvard.iq.dataverse.PermissionServiceBean;
import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
import edu.harvard.iq.dataverse.engine.command.exception.CommandException;
import edu.harvard.iq.dataverse.engine.command.impl.CreateDatasetCommand;
import edu.harvard.iq.dataverse.api.imports.ImportGenericServiceBean;
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
import edu.harvard.iq.dataverse.settings.SettingsServiceBean;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.ejb.EJBException;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import org.apache.abdera.parser.ParseException;
import org.swordapp.server.AuthCredentials;
import org.swordapp.server.CollectionDepositManager;
import org.swordapp.server.Deposit;
import org.swordapp.server.DepositReceipt;
import org.swordapp.server.SwordAuthException;
import org.swordapp.server.SwordConfiguration;
import org.swordapp.server.SwordEntry;
import org.swordapp.server.SwordError;
import org.swordapp.server.SwordServerException;
import org.swordapp.server.UriRegistry;
public class CollectionDepositManagerImpl implements CollectionDepositManager {
private static final Logger logger = Logger.getLogger(CollectionDepositManagerImpl.class.getCanonicalName());
@EJB
DataverseServiceBean dataverseService;
@EJB
DatasetServiceBean datasetService;
@EJB
PermissionServiceBean permissionService;
@Inject
SwordAuth swordAuth;
@Inject
UrlManager urlManager;
@EJB
EjbDataverseEngine engineSvc;
@EJB
DatasetFieldServiceBean datasetFieldService;
@EJB
ImportGenericServiceBean importGenericService;
@EJB
SwordServiceBean swordService;
@EJB
SettingsServiceBean settingsService;
private HttpServletRequest request;
@Override
public DepositReceipt createNew(String collectionUri, Deposit deposit, AuthCredentials authCredentials, SwordConfiguration config)
throws SwordError, SwordServerException, SwordAuthException {
AuthenticatedUser user = swordAuth.auth(authCredentials);
DataverseRequest dvReq = new DataverseRequest(user, request);
urlManager.processUrl(collectionUri);
String dvAlias = urlManager.getTargetIdentifier();
if (urlManager.getTargetType().equals("dataverse") && dvAlias != null) {
logger.log(Level.FINE, "attempting deposit into this dataverse alias: {0}", dvAlias);
Dataverse dvThatWillOwnDataset = dataverseService.findByAlias(dvAlias);
if (dvThatWillOwnDataset != null) {
logger.log(Level.FINE, "multipart: {0}", deposit.isMultipart());
logger.log(Level.FINE, "binary only: {0}", deposit.isBinaryOnly());
logger.log(Level.FINE, "entry only: {0}", deposit.isEntryOnly());
logger.log(Level.FINE, "in progress: {0}", deposit.isInProgress());
logger.log(Level.FINE, "metadata relevant: {0}", deposit.isMetadataRelevant());
if (deposit.isEntryOnly()) {
// do a sanity check on the XML received
try {
SwordEntry swordEntry = deposit.getSwordEntry();
logger.log(Level.FINE, "deposit XML received by createNew():\n{0}", swordEntry.toString());
} catch (ParseException ex) {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Can not create dataset due to malformed Atom entry: " + ex);
}
Dataset dataset = new Dataset();
dataset.setOwner(dvThatWillOwnDataset);
String nonNullDefaultIfKeyNotFound = "";
String protocol = settingsService.getValueForKey(SettingsServiceBean.Key.Protocol, nonNullDefaultIfKeyNotFound);
String authority = settingsService.getValueForKey(SettingsServiceBean.Key.Authority, nonNullDefaultIfKeyNotFound);
String separator = settingsService.getValueForKey(SettingsServiceBean.Key.DoiSeparator, nonNullDefaultIfKeyNotFound);
dataset.setProtocol(protocol);
dataset.setAuthority(authority);
dataset.setDoiSeparator(separator);
dataset.setIdentifier(datasetService.generateIdentifierSequence(protocol, authority, separator));
logger.log(Level.FINE, "DS Deposit identifier: {0}", dataset.getIdentifier());
CreateDatasetCommand createDatasetCommand = new CreateDatasetCommand(dataset, dvReq, false);
if (!permissionService.isUserAllowedOn(user, createDatasetCommand, dataset)) {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "user " + user.getDisplayInfo().getTitle() + " is not authorized to create a dataset in this dataverse.");
}
DatasetVersion newDatasetVersion = dataset.getEditVersion();
String foreignFormat = SwordUtil.DCTERMS;
try {
importGenericService.importXML(deposit.getSwordEntry().toString(), foreignFormat, newDatasetVersion);
} catch (Exception ex) {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "problem calling importXML: " + ex);
}
swordService.addDatasetContact(newDatasetVersion, user);
swordService.addDatasetDepositor(newDatasetVersion, user);
swordService.addDatasetSubjectIfMissing(newDatasetVersion);
swordService.setDatasetLicenseAndTermsOfUse(newDatasetVersion, deposit.getSwordEntry());
Dataset createdDataset = null;
try {
createdDataset = engineSvc.submit(createDatasetCommand);
} catch (EJBException | CommandException ex) {
Throwable cause = ex;
StringBuilder sb = new StringBuilder();
sb.append(ex.getLocalizedMessage());
while (cause.getCause() != null) {
cause = cause.getCause();
/**
* @todo move this ConstraintViolationException
* check to CreateDatasetCommand. Can be triggered
* if you don't call dataset.setIdentifier() or if
* you feed it date format we don't like. Once this
* is done we should be able to drop EJBException
* from the catch above and only catch
* CommandException
*
* See also Have commands catch
* ConstraintViolationException and turn them into
* something that inherits from CommandException ·
* Issue #1009 · IQSS/dataverse -
* https://github.com/IQSS/dataverse/issues/1009
*/
if (cause instanceof ConstraintViolationException) {
ConstraintViolationException constraintViolationException = (ConstraintViolationException) cause;
for (ConstraintViolation<?> violation : constraintViolationException.getConstraintViolations()) {
sb.append(" Invalid value: '").append(violation.getInvalidValue()).append("' for ")
.append(violation.getPropertyPath()).append(" at ")
.append(violation.getLeafBean()).append(" - ")
.append(violation.getMessage());
}
}
}
logger.info(sb.toString());
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Couldn't create dataset: " + sb.toString());
}
if (createdDataset != null) {
ReceiptGenerator receiptGenerator = new ReceiptGenerator();
String baseUrl = urlManager.getHostnamePlusBaseUrlPath(collectionUri);
DepositReceipt depositReceipt = receiptGenerator.createDatasetReceipt(baseUrl, createdDataset);
return depositReceipt;
} else {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Problem creating dataset. Null returned.");
}
} else if (deposit.isBinaryOnly()) {
// get here with this:
// curl --insecure -s --data-binary "@example.zip" -H "Content-Disposition: filename=example.zip" -H "Content-Type: application/zip" https://sword:sword@localhost:8181/dvn/api/data-deposit/v1/swordv2/collection/dataverse/sword/
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Binary deposit to the collection IRI via POST is not supported. Please POST an Atom entry instead.");
} else if (deposit.isMultipart()) {
// get here with this:
// wget https://raw.github.com/swordapp/Simple-Sword-Server/master/tests/resources/multipart.dat
// curl --insecure --data-binary "@multipart.dat" -H 'Content-Type: multipart/related; boundary="===============0670350989=="' -H "MIME-Version: 1.0" https://sword:sword@localhost:8181/dvn/api/data-deposit/v1/swordv2/collection/dataverse/sword/hdl:1902.1/12345
// but...
// "Yeah, multipart is critically broken across all implementations" -- http://www.mail-archive.com/sword-app-tech@lists.sourceforge.net/msg00327.html
throw new UnsupportedOperationException("Not yet implemented");
} else {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "expected deposit types are isEntryOnly, isBinaryOnly, and isMultiPart");
}
} else {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Could not find dataverse: " + dvAlias);
}
} else {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Could not determine target type or identifier from URL: " + collectionUri);
}
}
public void setRequest(HttpServletRequest request) {
this.request = request;
}
}