Skip to content

Commit

Permalink
Merge pull request #154 from gdcc/jakarta
Browse files Browse the repository at this point in the history
Jakarta EE 10 and Java 17
  • Loading branch information
pdurbin authored Aug 21, 2023
2 parents d1a32e0 + 3d98566 commit 9012317
Show file tree
Hide file tree
Showing 19 changed files with 83 additions and 158 deletions.
23 changes: 19 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,26 @@ This server library is an implementation of the SWORD 2.0 standard defined [here
The variant hosted in this repository is being used as the library of choice to implement the compliant interface
for [Dataverse](https://dataverse.org). It also gets pushed to [Maven Central](https://mvnrepository.com/artifact/io.gdcc/sword2-server).

## Important changes

### Removed `multipart/related` support
This library does no longer support [RFC2387 type uploads (multipart/related)](dx.doi.org/10.17487/RFC2387)!
It will present users an error message, telling them to use Atom instead.

Support for this type of uploads was broken for a long time and did not make it into SWORD v3.
[It was tagged for removal for a SWORD v2.1 spec](http://www.mail-archive.com/sword-app-tech@lists.sourceforge.net/msg00327.html),
which never happened.

### Requires Jakarta EE 10+
This library uses Jakarta EE Servlet API 6+ contained in Jakarta EE 10 or newer. Implementing applications should use
these namespace-shifted libs, too. Your mileage may vary, depending on your application server's possibilities.

### Requires Java 17
This library requires Java 17.

## History of this library

1. This library has been develop by @richard-jonnes and @bmckinney at https://github.com/swordapp/JavaServer2.0 first.
2. As development has been staled, DANS-KNAW took as part of project EASY at https://github.com/DANS-KNAW/easy-sword2-lib
2. As development has been stalled, DANS-KNAW took as part of project EASY at https://github.com/DANS-KNAW/easy-sword2-lib
3. MyCoRe project forked and maintain their own version with interesting changes. https://github.com/MyCoRe-Org/easy-sword2-lib
4. IQSS needed a fix in this library, so they currently have their own fork at https://github.com/IQSS/swordv2-java-server-library
This project aims to replace that fork and provide a version with updated dependencies. It has been replaced with
releases from this repo in the meantime.
4. IQSS needed a fix in the original library and has switched from their own fork at https://github.com/IQSS/swordv2-java-server-library to this fork.
18 changes: 10 additions & 8 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
<modelVersion>4.0.0</modelVersion>
<groupId>io.gdcc</groupId>
<artifactId>sword2-server</artifactId>
<version>1.2.3-SNAPSHOT</version>
<!--Leave -SNAPSHOT in the version. It will be removed by Maven Release Plugin: https://maven.apache.org/maven-release/maven-release-plugin/-->
<version>2.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SWORD v2 Common Server Library (forked)</name>
<description>
Expand All @@ -19,10 +20,11 @@
<skipAnalysis>false</skipAnalysis>
<!-- Key 2DFF887456235B4550B857DFBAFC446FC031F36B for Dataverse Bot <dataversebot@gdcc.io> expiring 2024-07-29 -->
<gpg.keyname>0xC031F36B</gpg.keyname>
<jdk.version>11</jdk.version>
<jdk.version>17</jdk.version>

<!-- Dependency versions -->
<servlet-api.version>4.0.4</servlet-api.version>
<servlet-api.version>6.0.0</servlet-api.version>
<jersey.version>3.0.4</jersey.version>
<abdera.version>1.1.3</abdera.version>
<fileupload.version>1.5</fileupload.version>
<jena.version>4.9.0</jena.version>
Expand Down Expand Up @@ -480,6 +482,11 @@
<version>${servlet-api.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.apache.abdera</groupId>
<artifactId>abdera-core</artifactId>
Expand All @@ -496,11 +503,6 @@
<version>${abdera.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${fileupload.version}</version>
</dependency>
<dependency>
<groupId>org.apache.jena</groupId>
<artifactId>jena-core</artifactId>
Expand Down
8 changes: 4 additions & 4 deletions src/main/java/org/swordapp/server/CollectionAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
Expand Down Expand Up @@ -130,7 +130,7 @@ public void post(final HttpServletRequest req, final HttpServletResponse resp) t

// do the different kinds of deposit details extraction
if (isMultipart) {
this.addDepositPropertiesFromMultipart(deposit, req);
throw new SwordError(UriRegistry.ERROR_METHOD_NOT_ALLOWED, "This server does not support RFC2387 Multipart uploads, to be removed in SWORD v2.1");
} else if (isEntryOnly) {
this.addDepositPropertiesFromEntry(deposit, req);
} else if (isBinaryOnly) {
Expand Down
13 changes: 5 additions & 8 deletions src/main/java/org/swordapp/server/ContainerAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
Expand Down Expand Up @@ -163,10 +163,7 @@ public void put(final HttpServletRequest req, final HttpServletResponse resp) th
DepositReceipt receipt;

if (isMultipart) {
this.addDepositPropertiesFromMultipart(deposit, req);

// defer to the implementation layer to update both the metadata and the media resource
receipt = this.cm.replaceMetadataAndMediaResource(iri, deposit, auth, this.config);
throw new SwordError(UriRegistry.ERROR_METHOD_NOT_ALLOWED, "This server does not support RFC2387 Multipart uploads, to be removed in SWORD v2.1");
} else if (isEntryOnly) {
// check that we have the right content type
if (!(contentType.startsWith("application/atom+xml") || contentType.startsWith("application/atom+xml;type=entry"))) {
Expand All @@ -179,7 +176,7 @@ public void put(final HttpServletRequest req, final HttpServletResponse resp) th
receipt = this.cm.replaceMetadata(iri, deposit, auth, this.config);
} else {
// some other sort of deposit which is not supported
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "PUT to Edit-IRI MUST be a multipart request or an Atom Entry");
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "PUT to Edit-IRI MUST be an Atom Entry");
}

// prepare and return the response
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/swordapp/server/ErrorDocument.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
Expand Down
17 changes: 6 additions & 11 deletions src/main/java/org/swordapp/server/MediaResourceAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
Expand Down Expand Up @@ -204,15 +204,10 @@ public void post(final HttpServletRequest req, final HttpServletResponse resp) t

Deposit deposit = null;
try {
// the first thing to do is determine what the deposit type is:
String contentType = this.getContentType(req);
boolean isMultipart = contentType.startsWith("multipart/related");
String uri = this.getFullUrl(req);

deposit = new Deposit();

if (isMultipart) {
this.addDepositPropertiesFromMultipart(deposit, req);
if (this.getContentType(req).startsWith("multipart/related")) {
throw new SwordError(UriRegistry.ERROR_METHOD_NOT_ALLOWED, "This server does not support RFC2387 Multipart uploads, to be removed in SWORD v2.1");
} else {
this.addDepositPropertiesFromBinary(deposit, req);
}
Expand All @@ -222,7 +217,7 @@ public void post(final HttpServletRequest req, final HttpServletResponse resp) t
deposit.setMetadataRelevant(metadataRelevant);

// now send the deposit to the implementation for processing
DepositReceipt receipt = this.mrm.addResource(uri, deposit, auth, this.config);
DepositReceipt receipt = this.mrm.addResource(this.getFullUrl(req), deposit, auth, this.config);

// prepare and return the response
IRI location = receipt.getLocation();
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/org/swordapp/server/ServiceDocumentAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

public class ServiceDocumentAPI extends SwordAPIEndpoint {
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/org/swordapp/server/StatementAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
Expand Down
95 changes: 13 additions & 82 deletions src/main/java/org/swordapp/server/SwordAPIEndpoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,14 @@
import org.apache.abdera.model.Entry;
import org.apache.abdera.parser.ParseException;
import org.apache.abdera.parser.Parser;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ParameterParser;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.glassfish.jersey.media.multipart.ContentDisposition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
Expand All @@ -29,7 +25,6 @@
import java.util.Base64;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

Expand Down Expand Up @@ -191,46 +186,6 @@ protected void storeAndCheckBinary(final Deposit deposit, final SwordConfigurati

log.debug("Package temporarily stored as: " + filename);
}

protected void addDepositPropertiesFromMultipart(final Deposit deposit, final HttpServletRequest req) throws ServletException, IOException, SwordError {
// Parse the request for files (using the fileupload commons library)
List<FileItem> items = this.getPartsFromRequest(req);
for (FileItem item : items) {
// find out which part we are looking at
String contentDisposition = item.getHeaders().getHeader("Content-Disposition");
String name = this.getContentDispositionValue(contentDisposition, "name");

if ("atom".equals(name)) {
parseEntryFromInputStream(deposit, item.getInputStream());
} else if ("payload".equals(name)) {
String md5 = item.getHeaders().getHeader("Content-MD5");
String packaging = item.getHeaders().getHeader("Packaging");
String filename = this.getContentDispositionValue(contentDisposition, "filename");
if (filename == null || "".equals(filename)) {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Filename could not be extracted from Content-Disposition");
}
String ct = item.getContentType();
String mimeType = "application/octet-stream";
if (ct != null) {
String[] bits = ct.split(";");
mimeType = bits[0].trim();
}
InputStream mediaPart = item.getInputStream();

deposit.setFilename(filename);
deposit.setInputStream(mediaPart);
deposit.setMimeType(mimeType);
deposit.setMd5(md5);
deposit.setPackaging(packaging);
}
}

try {
this.storeAndCheckBinary(deposit, this.config);
} catch (SwordServerException e) {
throw new ServletException(e);
}
}

protected void cleanup(final Deposit deposit) {
if (deposit == null) {
Expand Down Expand Up @@ -309,9 +264,14 @@ protected void addDepositPropertiesFromBinary(final Deposit deposit, final HttpS
InputStream file = req.getInputStream();

// now let's interpret and deal with the headers that we have
String filename = this.getContentDispositionValue(contentDisposition, "filename");
if (filename == null || "".equals(filename)) {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Filename could not be extracted from Content-Disposition");
String filename;
try {
filename = new ContentDisposition(contentDisposition).getFileName();
if ("".equals(filename)) {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Filename could not be extracted from Content-Disposition");
}
} catch (java.text.ParseException e) {
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Filename could not be extracted from Content-Disposition: " + e.getMessage());
}

deposit.setFilename(filename);
Expand Down Expand Up @@ -366,35 +326,6 @@ protected void swordError(final HttpServletRequest req, final HttpServletRespons
}
}

protected String getContentDispositionValue(final String contentDisposition, final String key) {
if (contentDisposition == null || key == null) {
return null;
}

ParameterParser parameterParser = new ParameterParser();
char separator = ';';
Map<String, String> parameters = parameterParser.parse(contentDisposition, separator);
return parameters.get(key);
}

protected List<FileItem> getPartsFromRequest(final HttpServletRequest request) throws ServletException {
try {
// Create a factory for disk-based file items
FileItemFactory factory = new DiskFileItemFactory();

// Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setFileCountMax(config.getMaxUploadFiles());

// Parse the request
List<FileItem> items = upload.parseRequest(request);

return items;
} catch (FileUploadException e) {
throw new ServletException(e);
}
}

protected Map<String, String> getAcceptHeaders(final HttpServletRequest req) {
Map<String, String> acceptHeaders = new HashMap<String, String>();
Enumeration headers = req.getHeaderNames();
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/org/swordapp/server/SwordConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ public interface SwordConfiguration {
String getTempDirectory();

int getMaxUploadSize();

int getMaxUploadFiles();

String getAlternateUrl();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,7 @@ public String getTempDirectory() {
public int getMaxUploadSize() {
return -1;
}

@Override
public int getMaxUploadFiles() {
return -1;
}


public String getAlternateUrl() {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
import org.swordapp.server.CollectionDepositManager;
import org.swordapp.server.CollectionListManager;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

public class CollectionServletDefault extends SwordServlet {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
import org.swordapp.server.ContainerManager;
import org.swordapp.server.StatementManager;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

public class ContainerServletDefault extends SwordServlet {
Expand Down
Loading

0 comments on commit 9012317

Please sign in to comment.