Skip to content

Commit

Permalink
Change the package format to JAR (#339)
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex Dolski committed Jul 7, 2020
1 parent 7c437ca commit aab2b90
Show file tree
Hide file tree
Showing 27 changed files with 173 additions and 281 deletions.
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@

### Other

* The application is now packaged as a JAR file and can no longer work in a
Servlet container. (See
[#339](https://github.com/cantaloupe-project/cantaloupe/issues/339) for
background.)
* Java 11 is required.
* Corrected the structure of the `cookie` key in the delegate script context.
See the upgrade guide for more information.
Expand Down
24 changes: 9 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,20 @@

### Command line

* `mvn clean compile exec:java -Dcantaloupe.config=...` will build the project
and run in standalone mode, using the embedded Servlet container listening on
the port(s) specified in `cantaloupe.properties`.
* `mvn clean package -DskipTests` will build a release WAR in the `target`
folder, which can be run like any other.
* `mvn clean compile exec:java -Dcantaloupe.config=...` will build and run the
project using the embedded web server listening on the port(s) specified in
`cantaloupe.properties`.
* `mvn clean package -DskipTests` will build a release JAR in the `target`
folder, which can be run using either of the following invocations:
* `java -cp cantaloupe-{version}.jar -Dcantaloupe.config=... edu.illinois.library.cantaloupe.StandaloneEntry`
* `java -Dcantaloupe.config=... -jar cantaloupe-{version}.jar`

### IDE

There are a few ways to do this. The simplest is probably:

1. Add a new run configuration using the "Java Application" template or its
equivalent.
1. Add a new run configuration using the "Java Application" template or
similar.
2. Set the main class to `edu.illinois.library.cantaloupe.StandaloneEntry` and
add the `-Dcantaloupe.config=...` VM option.
3. Set the run configuration to include dependencies with `provided` scope.
(IntelliJ has a checkbox for this.) Alternatively, download
[servlet-api-3.1.jar](http://central.maven.org/maven2/javax/servlet/javax.servlet-api/3.1.0/javax.servlet-api-3.1.0.jar)
and add it to your classpath: `--class-path=/path/to/servlet-api-3.1.jar`

Or, in Java 9+, your module path: `--module-path=/path/to/containing/dir`

## Test

Expand Down
28 changes: 15 additions & 13 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ current version.

## 4.1.x → 5.0

1. Note that Java 11 is now required.
2. Rename the following configuration keys:
* `endpoint.iiif.2.restrict_to_sizes` to `endpoint.iiif.restrict_to_sizes`
1. Note that the application is now packaged as a JAR file and can no longer
be used in a Servlet container.
2. Note that Java 11 is now required.
3. Add the following keys from the sample configuration:
* `http.min_threads`
* `http.max_threads`
Expand All @@ -28,28 +28,30 @@ current version.
* `GraphicsMagickProcessor.path_to_binaries`
* `KakaduDemoProcessor.path_to_binaries`
* `ImageMagickProcessor.path_to_binaries`
5. Rename the `extra_iiif2_information_response_keys()` delegate method to
5. Rename the following configuration keys:
* `endpoint.iiif.2.restrict_to_sizes` to `endpoint.iiif.restrict_to_sizes`
6. Rename the `extra_iiif2_information_response_keys()` delegate method to
`extra_iiif_information_response_keys()`.
6. The `X-IIIF-ID` reverse proxy header is no longer supported. Use
7. The `X-IIIF-ID` reverse proxy header is no longer supported. Use
`X-Forwarded-ID` instead.
7. Purge your derivative cache.
8. If you were using the `processor.metadata.preserve` key, you will need to
8. Purge your derivative cache.
9. If you were using the `processor.metadata.preserve` key, you will need to
use the new `metadata()` delegate method instead.
9. If you were using the `cookie` key in the delegate script context hash,
10. If you were using the `cookie` key in the delegate script context hash,
note that its structure has changed to a hash of cookie name-value pairs.
This is how it was documented to work, and how it was supposed to work, in
prior versions.
10. Note that the IIIF Image API 1.x endpoint no longer supports content
11. Note that the IIIF Image API 1.x endpoint no longer supports content
negotiation. If a format extension is not supplied in the URI, JPEG will be
returned regardless of the value of the `Accept` header.
11. If you are using HttpSource, note that the client implementation has
12. If you are using HttpSource, note that the client implementation has
changed. If you encounter errors like, "PKIX path building failed," consult
the HttpSource section of the user manual.
12. If you are using KakaduNativeProcessor, you must install the updated Kakadu
13. If you are using KakaduNativeProcessor, you must install the updated Kakadu
shared library, contained in the `deps` folder.
13. KakaduDemoProcessor is no longer available. If you were using it, you must
14. KakaduDemoProcessor is no longer available. If you were using it, you must
switch to either OpenJpegProcessor or KakaduNativeProcessor.
14. Note that the `false` value supplied to the `cache` URL query argument
15. Note that the `false` value supplied to the `cache` URL query argument
(e.g. `?cache=false`) has been replaced by `nocache`. `false` can still be
used, but it is deprecated and may be removed in a future version.

Expand Down
2 changes: 1 addition & 1 deletion assembly.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ http://maven.apache.org/plugins/maven-assembly-plugin/assembly.html
<fileSet>
<directory>target</directory>
<includes>
<include>cantaloupe-${project.version}.war</include>
<include>cantaloupe-${project.version}.jar</include>
</includes>
<outputDirectory>.</outputDirectory>
</fileSet>
Expand Down
146 changes: 35 additions & 111 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
<modelVersion>4.0.0</modelVersion>
<groupId>edu.illinois.library.cantaloupe</groupId>
<artifactId>cantaloupe</artifactId>
<packaging>war</packaging>
<packaging>jar</packaging>
<version>5.0-SNAPSHOT</version>
<name>Cantaloupe</name>
<url>https://cantaloupe-project.github.io/</url>
<inceptionYear>2015</inceptionYear>

<properties>
<maven.compile.source>11</maven.compile.source>
<maven.compile.target>11</maven.compile.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<aws-sdk.version>1.11.538</aws-sdk.version>
Expand Down Expand Up @@ -187,7 +189,6 @@
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
Expand Down Expand Up @@ -441,131 +442,54 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>11</source>
<target>11</target>
<source>${maven.compile.source}</source>
<target>${maven.compile.target}</target>
<useIncrementalCompilation>false</useIncrementalCompilation>
</configuration>
</plugin>

<!-- About the build: it's easy to build a JAR using the Maven Shade
plugin, and it's easy to build a WAR using the Maven WAR plugin. However,
this application is packaged as a special WAR that can also be run like a
JAR, and there's no all-in-one Maven plugin for that. So, from here, we
have to go through a sequence of steps to build the custom WAR. -->

<!-- The first step is to unpack dependencies (direct and transient)
needed in standalone mode into the root, for compatibility with
`java -jar` invocation. Note that this plugin is not smart about handling
dependencies with the same filename, which mainly is a problem with
same-named META-INF/services files from e.g. Jetty, so we will have to
work around that in a later step. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<id>unpack-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<excludes>**/META-INF/*.DSA, **/META-INF/*.MF,
**/META-INF/*.RSA, **/META-INF/*.SF,
**/META-INF/*.txt
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>edu.illinois.library.cantaloupe.StandaloneEntry</Main-Class>
<Implementation-Vendor-Id>${project.groupId}</Implementation-Vendor-Id>
<Implementation-Version>${project.version}</Implementation-Version>
<Implementation-URL>${project.url}</Implementation-URL>
<X-Compile-Source-JDK>${maven.compile.source}</X-Compile-Source-JDK>
<X-Compile-Target-JDK>${maven.compile.target}</X-Compile-Target-JDK>
</manifestEntries>
</transformer>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>META-INF/services/javax.imageio.*</exclude>
</excludes>
<includeGroupIds>
javax.servlet,
org.eclipse.jetty
</includeGroupIds>
<outputDirectory>
${project.build.directory}/${project.artifactId}-${project.version}
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>

<!-- The previous step copied only dependencies. Now we copy the
application classes needed for standalone mode into the root. -->
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>copy-standalone-classes-to-root</id>
<phase>prepare-package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<copy todir="${project.build.directory}/${project.artifactId}-${project.version}/">
<fileset dir="${project.build.directory}/classes/">
<include name="edu/illinois/library/cantaloupe/config/*"/>
<include name="edu/illinois/library/cantaloupe/util/FilesystemWatcher.class"/>
<include name="edu/illinois/library/cantaloupe/util/FilesystemWatcher$Callback.class"/>
<include name="edu/illinois/library/cantaloupe/util/StringUtils.class"/>
<include name="edu/illinois/library/cantaloupe/util/SystemUtils.class"/>
<include name="edu/illinois/library/cantaloupe/Application.class"/>
<include name="edu/illinois/library/cantaloupe/ApplicationServer.class"/>
<include name="edu/illinois/library/cantaloupe/StandaloneEntry.class"/>
</fileset>
</copy>
</tasks>
</configuration>
</execution>

<!-- And also add back a missing Jetty service that didn't get
copied in the unpack-dependencies execution. -->
<execution>
<id>finalize-services</id>
<phase>prepare-package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<echo file="${project.build.directory}/${project.artifactId}-${project.version}/META-INF/services/org.eclipse.jetty.http.HttpFieldPreEncoder"
append="true">
${line.separator}org.eclipse.jetty.http2.hpack.HpackFieldPreEncoder
</echo>
</tasks>
</configuration>
</execution>
</executions>
</plugin>

<!-- Create the war file from the stuff we just unpacked. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.0</version>
</filter>
</filters>
</configuration>
<executions>
<execution>
<id>create-war</id>
<phase>package</phase>
<goals>
<goal>war</goal>
<goal>shade</goal>
</goals>
<configuration>
<archive>
<manifest>
<mainClass>
edu.illinois.library.cantaloupe.StandaloneEntry
</mainClass>
<addDefaultImplementationEntries>true
</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true
</addDefaultSpecificationEntries>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>

<!-- Assemble the created war into a release package. -->
<!-- Assemble the JAR into a release package. -->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.1</version>
Expand Down
32 changes: 14 additions & 18 deletions src/main/java/edu/illinois/library/cantaloupe/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import edu.illinois.library.cantaloupe.config.Configuration;
import edu.illinois.library.cantaloupe.config.Key;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
Expand All @@ -19,14 +21,14 @@
*/
public final class Application {

// N.B.: Due to the way the application is packaged, this class does not
// have access to a logger.

/**
* Reads information from the manifest.
*/
private static class ManifestReader {

private static final Logger LOGGER =
LoggerFactory.getLogger(ManifestReader.class);

private static String name, version;

static {
Expand Down Expand Up @@ -60,31 +62,34 @@ private static void readManifest() {
name = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
version = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
} catch (IOException e) {
System.err.println(e.getMessage());
LOGGER.error("readManifest(): {}", e.getMessage(), e);
}
}
}
}
}

private static final Logger LOGGER =
LoggerFactory.getLogger(Application.class);

/**
* Set to {@literal true} during testing.
* Set to {@code true} during testing.
*
* @see #isTesting()
*/
public static final String TEST_VM_ARGUMENT = "cantaloupe.test";

/**
* @return The application title from {@literal MANIFEST.MF}, or some other
* string if not running from a JAR/WAR.
* string if not running from a JAR.
*/
public static String getName() {
return ManifestReader.name;
}

/**
* @return The application version from {@literal MANIFEST.MF}, or some
* other string if not running from a JAR/WAR.
* other string if not running from a JAR.
*/
public static String getVersion() {
return ManifestReader.version;
Expand All @@ -93,7 +98,6 @@ public static String getVersion() {
/**
* @return Path to the temp directory used by the application. If it does
* not exist, it will be created.
* @see #isUsingSystemTempPath()
*/
public static Path getTempPath() {
final Configuration config = Configuration.getInstance();
Expand All @@ -107,9 +111,8 @@ public static Path getTempPath() {
} catch (FileAlreadyExistsException ignore) {
// This is fine.
} catch (IOException e) {
System.err.println("Application.getTempPath(): " + e.getMessage());
System.err.println("Application.getTempPath(): " +
"falling back to java.io.tmpdir");
LOGGER.error("getTempPath(): falling back to java.io.tmpdir: {}",
e.getMessage(), e);
}
}
return Paths.get(System.getProperty("java.io.tmpdir"));
Expand All @@ -123,13 +126,6 @@ public static boolean isTesting() {
return "true".equals(System.getProperty(TEST_VM_ARGUMENT));
}

/**
* @see #getTempPath()
*/
public static boolean isUsingSystemTempPath() {
return Paths.get(System.getProperty("java.io.tmpdir")).equals(getTempPath());
}

private Application() {}

}
Loading

0 comments on commit aab2b90

Please sign in to comment.