use of org.swordapp.server.SwordError in project dataverse by IQSS.
the class StatementManagerImpl method getStatement.
@Override
public Statement getStatement(String editUri, Map<String, String> map, AuthCredentials authCredentials, SwordConfiguration swordConfiguration) throws SwordServerException, SwordError, SwordAuthException {
AuthenticatedUser user = swordAuth.auth(authCredentials);
DataverseRequest dvReq = new DataverseRequest(user, httpRequest);
urlManager.processUrl(editUri);
String globalId = urlManager.getTargetIdentifier();
if (urlManager.getTargetType().equals("study") && globalId != null) {
logger.fine("request for sword statement by user " + user.getDisplayInfo().getTitle());
Dataset dataset = datasetService.findByGlobalId(globalId);
if (dataset == null) {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "couldn't find dataset with global ID of " + globalId);
}
Dataverse dvThatOwnsDataset = dataset.getOwner();
if (!permissionService.isUserAllowedOn(user, new GetDraftDatasetVersionCommand(dvReq, dataset), dataset)) {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "user " + user.getDisplayInfo().getTitle() + " is not authorized to view dataset with global ID " + globalId);
}
String feedUri = urlManager.getHostnamePlusBaseUrlPath(editUri) + "/edit/study/" + dataset.getGlobalId();
String author = dataset.getLatestVersion().getAuthorsStr();
String title = dataset.getLatestVersion().getTitle();
// in the statement, the element is called "updated"
Date lastUpdatedFinal = new Date();
Date lastUpdateTime = dataset.getLatestVersion().getLastUpdateTime();
if (lastUpdateTime != null) {
lastUpdatedFinal = lastUpdateTime;
} else {
logger.info("lastUpdateTime was null, trying createtime");
Date createtime = dataset.getLatestVersion().getCreateTime();
if (createtime != null) {
lastUpdatedFinal = createtime;
} else {
logger.info("creatime was null, using \"now\"");
lastUpdatedFinal = new Date();
}
}
AtomDate atomDate = new AtomDate(lastUpdatedFinal);
String datedUpdated = atomDate.toString();
Statement statement = new AtomStatement(feedUri, author, title, datedUpdated);
Map<String, String> states = new HashMap<>();
states.put("latestVersionState", dataset.getLatestVersion().getVersionState().toString());
Boolean isMinorUpdate = dataset.getLatestVersion().isMinorUpdate();
states.put("isMinorUpdate", isMinorUpdate.toString());
if (dataset.isLocked()) {
states.put("locked", "true");
states.put("lockedDetail", dataset.getLocks().stream().map(l -> l.getInfo()).collect(joining(",")));
Optional<DatasetLock> earliestLock = dataset.getLocks().stream().min((l1, l2) -> (int) Math.signum(l1.getStartTime().getTime() - l2.getStartTime().getTime()));
states.put("lockedStartTime", earliestLock.get().getStartTime().toString());
} else {
states.put("locked", "false");
}
statement.setStates(states);
List<FileMetadata> fileMetadatas = dataset.getLatestVersion().getFileMetadatas();
for (FileMetadata fileMetadata : fileMetadatas) {
DataFile dataFile = fileMetadata.getDataFile();
// We are exposing the filename for informational purposes. The file id is what you
// actually operate on to delete a file, etc.
//
// Replace spaces to avoid IRISyntaxException
String fileNameFinal = fileMetadata.getLabel().replace(' ', '_');
String fileUrlString = urlManager.getHostnamePlusBaseUrlPath(editUri) + "/edit-media/file/" + dataFile.getId() + "/" + fileNameFinal;
IRI fileUrl;
try {
fileUrl = new IRI(fileUrlString);
} catch (IRISyntaxException ex) {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Invalid URL for file ( " + fileUrlString + " ) resulted in " + ex.getMessage());
}
ResourcePart resourcePart = new ResourcePart(fileUrl.toString());
// default to something that doesn't throw a org.apache.abdera.util.MimeTypeParseException
String finalFileFormat = "application/octet-stream";
String contentType = dataFile.getContentType();
if (contentType != null) {
finalFileFormat = contentType;
}
resourcePart.setMediaType(finalFileFormat);
/**
* @todo: Why are properties set on a ResourcePart not exposed
* when you GET a Statement? Asked about this at
* http://www.mail-archive.com/sword-app-tech@lists.sourceforge.net/msg00394.html
*/
// Map<String, String> properties = new HashMap<String, String>();
// properties.put("filename", studyFile.getFileName());
// properties.put("category", studyFile.getLatestCategory());
// properties.put("originalFileType", studyFile.getOriginalFileType());
// properties.put("id", studyFile.getId().toString());
// properties.put("UNF", studyFile.getUnf());
// resourcePart.setProperties(properties);
statement.addResource(resourcePart);
/**
* @todo it's been noted at
* https://github.com/IQSS/dataverse/issues/892#issuecomment-54159284
* that at the file level the "updated" date is always "now",
* which seems to be set here:
* https://github.com/swordapp/JavaServer2.0/blob/sword2-server-1.0/src/main/java/org/swordapp/server/AtomStatement.java#L70
*/
}
return statement;
} else {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Could not determine target type or identifier from URL: " + editUri);
}
}
use of org.swordapp.server.SwordError in project dataverse by IQSS.
the class SwordUtil method throwSpecialSwordErrorWithoutStackTrace.
/*
* @todo get rid of this method
*/
public static SwordError throwSpecialSwordErrorWithoutStackTrace(String SwordUriRegistryError, String error) {
if (SwordUriRegistryError == null) {
SwordUriRegistryError = UriRegistry.ERROR_BAD_REQUEST;
}
if (error == null) {
error = "UNKNOWN";
}
SwordError swordError = new SwordError(SwordUriRegistryError, error);
StackTraceElement[] emptyStackTrace = new StackTraceElement[0];
swordError.setStackTrace(emptyStackTrace);
return swordError;
}
use of org.swordapp.server.SwordError in project dataverse by IQSS.
the class CollectionDepositManagerImpl method createNew.
@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);
// Wait until the create command before actually getting an identifier
// dataset.setIdentifier(datasetService.generateDatasetIdentifier(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()) {
// 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()) {
// "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);
}
}
use of org.swordapp.server.SwordError in project dataverse by IQSS.
the class ContainerManagerImpl method replaceMetadata.
@Override
public DepositReceipt replaceMetadata(String uri, Deposit deposit, AuthCredentials authCredentials, SwordConfiguration swordConfiguration) throws SwordError, SwordServerException, SwordAuthException {
AuthenticatedUser user = swordAuth.auth(authCredentials);
DataverseRequest dvReq = new DataverseRequest(user, httpRequest);
logger.fine("replaceMetadata called with url: " + uri);
urlManager.processUrl(uri);
String targetType = urlManager.getTargetType();
if (!targetType.isEmpty()) {
logger.fine("operating on target type: " + urlManager.getTargetType());
if ("dataverse".equals(targetType)) {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Metadata replace of dataverse is not supported.");
} else if ("study".equals(targetType)) {
logger.fine("replacing metadata for dataset");
// do a sanity check on the XML received
try {
SwordEntry swordEntry = deposit.getSwordEntry();
logger.fine("deposit XML received by replaceMetadata():\n" + swordEntry);
} catch (ParseException ex) {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Can not replace dataset metadata due to malformed Atom entry: " + ex);
}
String globalId = urlManager.getTargetIdentifier();
Dataset dataset = datasetService.findByGlobalId(globalId);
if (dataset != null) {
Dataverse dvThatOwnsDataset = dataset.getOwner();
UpdateDatasetCommand updateDatasetCommand = new UpdateDatasetCommand(dataset, dvReq);
if (!permissionService.isUserAllowedOn(user, updateDatasetCommand, dataset)) {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "User " + user.getDisplayInfo().getTitle() + " is not authorized to modify dataverse " + dvThatOwnsDataset.getAlias());
}
DatasetVersion datasetVersion = dataset.getEditVersion();
// erase all metadata before creating populating dataset version
List<DatasetField> emptyDatasetFields = new ArrayList<>();
datasetVersion.setDatasetFields(emptyDatasetFields);
String foreignFormat = SwordUtil.DCTERMS;
try {
importGenericService.importXML(deposit.getSwordEntry().toString(), foreignFormat, datasetVersion);
} catch (Exception ex) {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "problem calling importXML: " + ex);
}
swordService.addDatasetContact(datasetVersion, user);
swordService.addDatasetDepositor(datasetVersion, user);
swordService.addDatasetSubjectIfMissing(datasetVersion);
swordService.setDatasetLicenseAndTermsOfUse(datasetVersion, deposit.getSwordEntry());
try {
engineSvc.submit(updateDatasetCommand);
} catch (CommandException ex) {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "problem updating dataset: " + ex);
}
ReceiptGenerator receiptGenerator = new ReceiptGenerator();
String baseUrl = urlManager.getHostnamePlusBaseUrlPath(uri);
DepositReceipt depositReceipt = receiptGenerator.createDatasetReceipt(baseUrl, dataset);
return depositReceipt;
} else {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Could not find dataset based on global id (" + globalId + ") in URL: " + uri);
}
} else {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Unknown target type specified on which to replace metadata: " + uri);
}
} else {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "No target specified on which to replace metadata: " + uri);
}
}
use of org.swordapp.server.SwordError in project dataverse by IQSS.
the class UrlManager method processUrl.
String processUrl(String url) throws SwordError {
logger.fine("URL was: " + url);
String warning = null;
this.originalUrl = url;
URI javaNetUri;
try {
javaNetUri = new URI(url);
} catch (URISyntaxException ex) {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Invalid URL syntax: " + url);
}
/**
* @todo: figure out another way to check for http. We used to use
* javaNetUri.getScheme() but now that we are using "ProxyPass /
* ajp://localhost:8009/" in Apache it's always http rather than https.
*
* http://serverfault.com/questions/6128/how-do-i-force-apache-to-use-https-in-conjunction-with-ajp
* http://stackoverflow.com/questions/1685563/apache-webserver-jboss-ajp-connectivity-with-https
* http://stackoverflow.com/questions/12460422/how-do-ensure-that-apache-ajp-to-tomcat-connection-is-secure-encrypted
*/
if (!"https".equals(javaNetUri.getScheme())) {
/**
* @todo figure out how to prevent this stackstrace from showing up
* in Glassfish logs:
*
* Unable to populate SSL attributes
* java.lang.IllegalStateException: SSLEngine is null at
* org.glassfish.grizzly.ssl.SSLSupportImpl
*
* https://github.com/IQSS/dataverse/issues/643
*
* SSLOptions +StdEnvVars +ExportCertData ?
*
* [#GLASSFISH-20694] Glassfish 4.0 and jk Unable to populate SSL
* attributes - Java.net JIRA -
* https://java.net/jira/browse/GLASSFISH-20694
*/
logger.fine("https is required but protocol was " + javaNetUri.getScheme());
// throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "https is required but protocol was " + javaNetUri.getScheme());
}
this.port = javaNetUri.getPort();
String[] urlPartsArray = javaNetUri.getPath().split("/");
List<String> urlParts = Arrays.asList(urlPartsArray);
String dataDepositApiBasePath;
try {
List<String> dataDepositApiBasePathParts;
// 1 2 3 4 5 6 7 8 9
// for example: /dvn/api/data-deposit/v1/swordv2/collection/dataverse/sword
dataDepositApiBasePathParts = urlParts.subList(0, 6);
dataDepositApiBasePath = StringUtils.join(dataDepositApiBasePathParts, "/");
} catch (IndexOutOfBoundsException ex) {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Error processing URL: " + url);
}
if (!swordConfiguration.getBaseUrlPathsValid().contains(dataDepositApiBasePath)) {
throw new SwordError(dataDepositApiBasePath + " found but one of these required: " + swordConfiguration.getBaseUrlPathsValid() + ". Current version is " + swordConfiguration.getBaseUrlPathCurrent());
} else {
if (swordConfiguration.getBaseUrlPathsDeprecated().contains(dataDepositApiBasePath)) {
String msg = "Deprecated version used for Data Deposit API. The current version expects '" + swordConfiguration.getBaseUrlPathCurrent() + "'. URL passed in: " + url;
warning = msg;
}
}
try {
this.servlet = urlParts.get(6);
} catch (ArrayIndexOutOfBoundsException ex) {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Unable to determine servlet path from URL: " + url);
}
if (!servlet.equals("service-document")) {
List<String> targetTypeAndIdentifier;
try {
// 6 7 8
// for example: /collection/dataverse/sword
targetTypeAndIdentifier = urlParts.subList(7, urlParts.size());
} catch (IndexOutOfBoundsException ex) {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "No target components specified in URL: " + url);
}
this.targetType = targetTypeAndIdentifier.get(0);
if (targetType != null) {
if (targetType.equals("dataverse")) {
String dvAlias;
try {
dvAlias = targetTypeAndIdentifier.get(1);
} catch (IndexOutOfBoundsException ex) {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "No dataverse alias provided in URL: " + url);
}
this.targetIdentifier = dvAlias;
/**
* @todo it would be nice to support "dataset" as an alias
* for "study" since that's what we call them now in
* Dataverse 4.0. We should continue to support "study" in
* the URL however because some API users have these URLs
* stored in databases:
* http://irclog.iq.harvard.edu/dvn/2014-05-14#i_9404
*
* Also, to support "dataset" in URLs properly, we'd need to
* examine all the places where we return the string "study"
* such as in the Deposit Receipt.
*/
} else if (targetType.equals("study")) {
String globalId;
try {
List<String> globalIdParts = targetTypeAndIdentifier.subList(1, targetTypeAndIdentifier.size());
globalId = StringUtils.join(globalIdParts, "/");
} catch (IndexOutOfBoundsException ex) {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Invalid study global id provided in URL: " + url);
}
this.targetIdentifier = globalId;
} else if (targetType.equals("file")) {
String fileIdString;
try {
// a user might reasonably pass in a filename as well [.get(2)] since
// we expose it in the statement of a study but we ignore it here
fileIdString = targetTypeAndIdentifier.get(1);
} catch (IndexOutOfBoundsException ex) {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "No file id provided in URL: " + url);
}
this.targetIdentifier = fileIdString;
} else {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "unsupported target type: " + targetType);
}
} else {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Unable to determine target type from URL: " + url);
}
logger.fine("target type: " + targetType);
logger.fine("target identifier: " + targetIdentifier);
}
if (warning != null) {
logger.info(warning);
}
return warning;
}
Aggregations