Coder Social home page Coder Social logo

dbmdz / iiif-server-hymir Goto Github PK

View Code? Open in Web Editor NEW
27.0 10.0 7.0 15.67 MB

Hymir is a Java based IIIF Server. It is based on "IIIF Image API Java Libraries" and "IIIF Presentation API Java Libraries" projects (see https://github.com/dbmdz)

License: MIT License

Java 88.66% HTML 10.67% CSS 0.67%
iiif iiif-server iiif-image iiif-presentation mirador

iiif-server-hymir's Introduction

Hymir IIIF Server

Javadocs License GitHub release Maven Central

Hymir is a Java based IIIF Server. It is based on our IIIF API Java Libraries (Java implementations of the IIIF specifications). It can be used to serve images, presentation manifests, presentation collections and presentation annotation lists.

Features

  • IIIF Image API 2.1 compliant (see http://iiif.io/api/image/2.1/).
  • IIIF Presentation API 2.1 compliant (see http://iiif.io/api/presentation/2.1/).
  • On the fly image processing. No additional pregenerated (pyramid zoom) images are needed. No additional storage consumption.
  • Can simply be run as a standalone IIIF server from the JAR, no application server necessary
  • Spring based modular, extendable, easy to maintain enterprise architecture.
  • Highly customizable image storage and identifier resolving: Access to images over project specific Resolver-plugin mechanism.
  • Support for Filesystem- and HTTP-Image-Repositories (own protocols can be added by providing specific resolver)
  • Pluggable Manifest generation: implement your own mapping from project specific structure metadata to a standard Manifest object.
  • Embedded IIIF Image Viewer (for out of the box viewing of served images): OpenSeadragon 2.4.0 (see "Usage" below)
  • Embedded IIIF Presentation Viewer: Mirador 2.7.0 (see "Usage" below)
  • Direct Manifest access (see "Usage" below)

Supported image formats

Format Reading Writing Dependencies Comment
JPEG [x] [x] libturbojpeg (optional, but recommended)
JPEG2000 [x] [ ] libopenjp2 (>= 2.3 recommended)
TIFF [x] [x]
PNG [x] [x] Due to possible transparency (alpha channel) in PNG it is not possible to use a PNG source file and deliver it as JPG. PNG delivered as PNG is possible.
GIF [x] [x]

Prerequisites

  • Server with minimum 4GB RAM.
  • Java 11

Installation

Download hymir-<version>-exec.jar from the GitHub releases page.

Using the TurboJPEG backend for JPEG files

By default, a Java-based image processing backend is used. If you want better performance, it is recommended to use the native image processing backend that is based on TurboJPEG. For this, you will have to install the TurboJPEG native library, on Ubuntu libturbojpeg.

Debian:

$ sudo apt-get install libturbojpeg

Adding JPEG2000 support

By default, a Java-based image processing backend is used which has no support for JPEG2000. For adding JPEG2000 support, you will have to install the libopenjp2 native library.

Debian:

$ sudo apt-cache search libopenjp2
libopenjp2-7 - Kompressions-/Dekompressions-Bibliothek für das Bildformat JPEG 2000
libopenjp2-tools - Kommandozeilenwerkzeuge zur Verwendung der JPEG 2000-Bibliothek
libopenjp2-7-dev - development files for OpenJPEG, a JPEG 2000 image library
$ sudo apt-get install libopenjp2-7

Creating logging directories

Create directories for

  • hymir application logging (configured in logback-spring.xml), e.g. /var/log/hymir
  • hymir access logs (default: /var/log/digitalcollections)

Example (use more restricted access rights than in this example):

$ sudo mkdir /var/log/hymir
$ sudo chmod 777 /var/log/hymir
$ sudo mkdir /var/log/digitalcollections
$ sudo chmod 777 /var/log/digitalcollections

Usage

Run the downloaded application:

  • locally for testing (default active configuration profile is "local"):
$ java -jar hymir-<version>-exec.jar

Logging: to console

Image, manifest, collection and annotation list file resolving: see here (using directories under /var/local/iiif)

Application configuration: see here (local profile section at beginning of file)

  • in production
$ java -jar hymir-<version>-exec.jar --spring.profiles.active=PROD

Logging: to file ./hymir.log in Logstash-JSON format

Image, manifest, collection and annotation list file resolving: see here (using directories under /var/local/iiif)

Application configuration: see here (PROD profile section overriding some values at bottom of file)

  • in production with custom logging configuration file:
$ java -jar hymir-<version>-exec.jar --logging.config=file:/etc/hymir/logback-spring.xml  --spring.profiles.active=PROD

Read https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-logging.html and https://logback.qos.ch/manual/configuration.html.

  • in production with custom configuration file application.yml:

(Custom application.yml placed beside jar-file. No explicit command line option needed.)

$ java -jar hymir-<version>-exec.jar --spring.profiles.active=PROD
  • in production with a custom server port (e.g. port 8080):
$ java -jar hymir-<version>-exec.jar --server.port=8080 --spring.profiles.active=PROD

Complete parametrized example:

$ java -jar hymir-<version>-exec.jar --logging.config=file:/etc/hymir/logback-spring.xml --server.port=8080 --spring.profiles.active=PROD

(and application.yml beside jar file).

Access Hymir GUI (e.g. http://localhost:9000/).

Configuration

Running Hymir behind a proxy server

If you are running Hymir behind a proxy server, it is important to configure the proxy server to set the X-Forwarded-For and X-Forwarded-Proto headers, because they are used e.g. in rendering absolute URLs in info.json response content.

For NGinx this can be configured like this:

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

Image and presentation manifest resolving

Based on unique resource identifiers the server tries to resolve identifiers to a file: or http: path. The resolving rules (one rule per line) are configurable with regular expressions in YML-files.

You can pass the path to your custom resolving rules with the --spring.config.additional-location=/path/to/rules.yml option.

Example:

$ java -jar hymir-<version>-exec.jar --spring.config.additional-location=file:/etc/hymir/rules.yml

Example file /etc/hymir/rules.yml:

resourceRepository:
  resolved:
    patterns:
      # This configuration file defines a list of patterns with one ore more substitutions.
      # These are used for resolving IDs to a concrete URI, e.g. on the file system, the
      # classpath or even a remote HTTP endpoint.
      # You can specify multiple substitutions, the resolver will try to match them against
      # the desired MIME type and return all that matches
      # The repository will then verify which of these URIs are actually readable and return
      # the first matching substitution.
      # In the example below, we have two MIME types (tiff/jpeg) and for JPEG two resolutions
      # in decreasing order of quality, so that the higher-resolution image will be chosen
      # if it is available.
      # An image pattern resolving example:
      - pattern: ^(\d{8})_(\d{5})$
        substitutions:
          - 'file:/var/local/iiif/images/$1/original/image_$1_$2.tif'
          - 'file:/var/local/iiif/images/$1/300/image_$1_$2.jpg'
          - 'file:/var/local/iiif/images/$1/150/image_$1_$2.jpg'

      # An manifest pattern resolving example:
      - pattern: ^(\d{8})$
        substitutions:
          - 'file:/var/local/iiif/presentation/manifests/manifest_$1.json'

      # For the official IIIF Image API Validator
      - pattern: 67352ccc-d1b0-11e1-89ae-279075081939
        substitutions:
          - 'classpath:validation.jp2'
          - 'classpath:validation.png'

      # Collection manifests ('collection-' pattern-prefix is statically added to requested collection name to disambigued from other patterns)
      - pattern: ^collection-(.*)$
        substitutions:
          - 'file:/var/local/iiif/presentation/collections/$1.json'

Serve images

See https://iiif.io/api/image/2.1/

In the simplest case you just want to serve images of a directory.

Example:

Let's assume you have a bunch of jpg-files residing in the directory "/var/local/iiif/images" for objects with identifiers 00000001, 00000002 and so on. The images-directory contains a directory for each object in which in turn all images of an object reside.

The files are named "image_00000001_00001.jpg", "image_00000001_00002.jpg", ... thus containing the object id and the image number in the filename.

An example for an entry in the rules.yml (including matching just for identifiers and numbered images with digits) then just could look like this:

- pattern: ^(\d{8})_(\d{5})$
  substitutions:
    - 'file:/var/local/iiif/images/$1/image_$1_$2.jpg'

An IIIF Image API url example for this pattern: http://localhost:9000/image/v2/00000005_00012/full/full/0/default.jpg

Change Image API URL prefix

By default the url prefix of the IIIF Image API endpoint is /image/v2/.

You can configure another url prefix on server startup using system property custom.iiif.image.urlPrefix.

Example:

$ java -jar target/hymir-<version>-exec.jar --custom.iiif.image.urlPrefix='/iiifImage/' --spring.config.additional-location=file:/etc/hymir/rules.yml --spring.profiles.active=local

Resulting URL: http://localhost:9000/iiifImage/00113391_00001/full/300,/0/default.jpg

Serve IIIF Presentation manifests

See https://iiif.io/api/presentation/2.1/#manifest

In the simplest case you just want to serve static (pregenerated) IIIF Presentation manifest json-files of a directory.

Example:

Let's assume you have a bunch of json-files residing in the directory "/var/local/iiif/presentation/manifests" for the objects with identifiers 00000001, 00000002 and so on.

The files are named "manifest_00000001.json", "manifest_00000002.json", ... containing the object id in the filename.

An example for an entry in the rules.yml (including matching just for identifiers with digits) then could look like this:

- pattern: ^(\d{8})$
  substitutions:
    - 'file:/var/local/iiif/presentation/manifests/manifest_$1.json'

An IIIF Presentation API url for a manifest example: http://localhost:9000/presentation/v2/00000002/manifest

Change Presentation API URL prefix

By default the url prefix of the IIIF Presentation API endpoint is /presentation/v2/.

You can configure another url prefix on server startup using system property custom.iiif.presentation.urlPrefix.

Example:

$ java -jar hymir-<version>-exec.jar --custom.iiif.presentation.urlPrefix='/iiifPresentation/' --spring.config.additional-location=file:/etc/hymir/rules.yml --spring.profiles.active=local

Resulting URL: http://localhost:9000/iiifPresentation/00113391/manifest

Serve IIIF Presentation collections

See https://iiif.io/api/presentation/2.1/#collection

In the simplest case you just want to serve static (pregenerated) IIIF Presentation collection json-files of a directory.

Example:

Let's assume you have a bunch of json-files residing in the directory "/var/local/iiif/presentation/collections" for collections with a specific name, e.g. newspapers.

The files are named e.g. newspapers.json, medieval_manuscripts.json, ... containing the collection name in the filename.

An example for an entry in the rules.yml then just could look like this:

- pattern: ^collection-(.*)$
  substitutions:
    - 'file:/var/local/iiif/presentation/collections/$1.json'

An IIIF Presentation API url for a collection example: http://localhost:9000/presentation/v2/collection/newspapers

Implementation background: To get a regex resolvable pattern that can be differentiated from patterns for manifest json-files (same mimetype), Hymir adds the static prefix collection- to the given identifier for collections. (This does not appear in the identifier in the url, just in the rules.yml regex)

Logging

Default logging configuration is specified in the file logback-spring.xml packaged in the exectable Hymir JAR-file. The default logging file is configured as ./hymir.log in Logstash-JSON-format.

If you want human readable logging to console use --spring.profiles.active=local on start command line or define a custom logback-spring.xml config location (see "Usage" section above).

Example: Custom config file with human readable logging

$ java -jar hymir-<version>-exec.jar --logging.config=file:/etc/hymir/logback-spring.xml

Example file /etc/hymir/logback-spring.xml:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <!-- see https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-logging.html#_profile_specific_configuration -->
  <springProfile name="PROD">
    <appender name="default" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <file>/var/log/hymir/hymir.log</file>
      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>/var/log/hymir/hymir.%d{yyyy-MM-dd}.log</fileNamePattern>
      </rollingPolicy>
      <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <fileNamePattern>/var/log/hymir/hymir.%d{yyyy-MM}.%i.log.gz</fileNamePattern>
        <maxFileSize>100MB</maxFileSize>
        <maxHistory>90</maxHistory>
        <totalSizeCap>5GB</totalSizeCap>
      </rollingPolicy>
      <encoder>
        <pattern>[%d{ISO8601} %5p] %40.40c:%4L [%-8t] - %m%n</pattern>
      </encoder>
      <!--
      <encoder class="net.logstash.logback.encoder.LogstashEncoder">
        <customFields>{"service":"hymir-server", "group":"rest", "instance":"${instance.name:-default}"}</customFields>
      </encoder>
      -->
    </appender>
  </springProfile>

  <springProfile name="local">
    <appender name="default" class="ch.qos.logback.core.ConsoleAppender">
      <encoder>
        <pattern>[%d{ISO8601} %5p] %40.40c:%4L [%-8t] - %m%n</pattern>
      </encoder>
    </appender>
  </springProfile>

  <logger name="de.digitalcollections.iiif.hymir.presentation.backend" level="error" />
  <logger name="de.digitalcollections.iiif.hymir.presentation.business" level="error" />
  <logger name="de.digitalcollections.commons" level="error" />

  <root level="info">
    <appender-ref ref="default" />
  </root>

</configuration>

Custom configuration file application.yml

The default configuration of the server comes packaged in the executable JAR-file of Hymir. To customize (override) the default configuration parameters, simply put your custom application.yml file beside (in the same directory of) the Hymir JAR-file.

Your custom application.yml does not have to replace all default properties. It can contain only the properties you want to change.

To get the default configuration file, you should download the hymir-<release-version>.jar file (NOT containing -exec in filename) from https://github.com/dbmdz/iiif-server-hymir/releases and unpack the contained application.yml with:

$ jar xfv hymir-<version>.jar application.yml

Now put the file beside the executable Hymir jar and edit it according to your requirements.

Configure custom HTTP-Response-Header

If you already put your custom application.yml file in place (see above), it is possible to set custom HTTP response headers in responses for

  • all requests to Image and Presentation API urls
  • Image API: image requests
  • Image API: info.json requests
  • Presentation API: manifest requests (includes canvas and range requests)
  • Presentation API: collection requests
  • Presentation API: Annotation list requests

Customized response headers are placed in the custom.iiif.headers-section of your application.yml configuration file, e.g.:

custom:
  iiif:
    headers:
      all:
        - name: 'served by'
          value: 'hymir'
      image:
        image:
          - name: 'cache-control'
            value: 'max-age=86400'
        info:
          - name: 'header1'
            value: 'value1'
      presentation:
        manifest:
          - name: 'mani1'
            value: 'mani-value1'
          - name: 'mani2'
            value: 'mani-value2'
        collection: null
        annotationList: null

If you want to override a header that is set by default (e.g. Access-Control-Allow-Origin=*), you just have to configure it with another value, e.g.:

custom:
  iiif:
    headers:
      image:
        info:
          - name: 'Access-Control-Allow-Origin'
            value: 'https://yourdomain.org'

(Given example is a bad practice in the IIIF context, as it contradicts the "interoperability" idea of IIIF...)

Administration

Monitoring

Monitoring endpoints under http://localhost:9001/monitoring, authentication by default: admin/secret (configurable in application.yml)

To change monitoring port, e.g. to 8081 use management.server.port option:

$ java -jar hymir-<version>-exec.jar --management.server.port=8081

Out Of Memory handling

In case the IIIF server runs out of memory it should quit - use java options for this. (To be restarted automatically install it as systemd service, see below.)

$ java -jar hymir-<version>-exec.jar -XX:+ExitOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError

Configure IIIF service as systemd service

In Linux production environments it is a best practice to install the IIIF server as a systemd service / daemon.

Therefore create a user iiif, a service file, add it as service and enable it:

$ sudo nano /etc/systemd/system/iiif-hymir.service
[Unit]
Description=IIIF Hymir Server
After=syslog.target

[Service]
User=iiif
ExecStart=/usr/bin/java -jar /opt/hymir-<version>-exec.jar \
    -XX:+ExitOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError \
    --spring.config.additional-location=file:/etc/hymir/rules.yml \
    --spring.profiles.active=PROD \
    --logging.config=file:/etc/hymir/logback-spring.xml \
    --server.port=8080 \
    --management.server.port=8081
SuccessExitStatus=143

[Install]
WantedBy=multi-user.target

$ sudo systemctl daemon-reload
$ sudo systemctl enable iiif-hymir.service
$ sudo systemctl start iiif-hymir.service

Users

Development

  • Install git client
  • Install Java JDK 11 or above
  • Install Apache Maven buildttool
$ cd ~/development
$ git clone [email protected]:dbmdz/iiif-server-hymir.git
$ cd iiif-server-hymir
$ mvn clean install

Executable JAR-file is in target directory.

On systems without installed libturbojpeg test fail. To build without tests, execute:

$ mvn clean install -DskipTests=true

To install libturbojpeg on Debian based systems:

$ sudo apt install libturbojpeg

iiif-server-hymir's People

Contributors

bitzl avatar clorenz avatar cmahnke avatar datazuul avatar dependabot-preview[bot] avatar dependabot-support avatar dependabot[bot] avatar jbaiter avatar morpheus-87 avatar schmika avatar stefan-it avatar vonunige avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

iiif-server-hymir's Issues

Prometheus endpoint ?

I can't monitor Hymir with prometheus (and grafana) as the endpoint server:port/actuator/prometheus is not available (or activated), even though "micrometer-registry-prometheus" lib is integrated.
How can I monitor hymir with prometheus ?

Freshly added new manifests not found by Hymir

I've a running instance of Hymir 3.5.0, with a custom rules.yml. Everything works fine.

I've generated new manifests, and put them with the same format in the same directory as the already existing ones. Old and new manifests have the same owner and permissions. But when I try to get a new one with /iiif/presentation/v2/1234567890/manifest I obtain a 404. The old ones are working fine. I've restarted Hymir but I have the same result.

Is it possible to add new manifests to a running instance?

Is there some cache somewhere?

Actual behavior : Manifest added at runtime are not found
Desired behavior: Manifest added at runtime are found

Error in manifest file should not lead to a 404

If a manifest file contains an error, the request iiif/presentation/v2/1234567890/manifest return a 404.

Step to reproduce:

  • Edit one of your manifest file
  • Change "@type": "dctypes:Image" to "@type": "dctypes:Illustration"
  • Get the manifest iiif/presentation/v2/1234567890/manifest
  • Observe that a 404 is returned

Desired behavior:
A more meaningful HTTP error should be returned such as 422 (Unprocessable Entity)

Full image sizes differ when using TurboJPEG, sizes and tiles properties are missing

Problem description: full image resolution is not available when using TurboJPEG.
Expected full image size is 15.000x15.000. Actual full image size is 11.250x11.250.

We're running the current version of hymir built from sources cloned yesterday from git. In addition, we are using TurboJPEG 2.0.0 64bit. The OS is Windows 10.
(Interestingly, we needed to rename the library file from libturbojpeg.dll to turbojpeg.dll and also needed to copy it to the hymir directory in order to get it to work. This might be a naming issue in the Windows version of the TurboJPEG SDK).

We're testing hymir with the file https://www.nasa.gov/sites/default/files/thumbnails/image/viirs_9apr2015.jpg copied to C:/dev/CantaloupePix . (As you can guess, we are comparing it to Cantaloupe)

Our rules.yaml is trivial:

- pattern: ^(.*)$
  substitutions:
    - 'file:/C:/dev/CantaloupePix/$1.jpg'

Requesting
http://127.0.0.1:10000/image/v2/viirs_9apr2015/info.json
we get:
{"@context":"http://iiif.io/api/image/2/context.json","@id":"http://127.0.0.1:10000/image/v2/viirs_9apr2015","width":11250,"height":11250,"tiles":[{"width":512,"scaleFactors":[1,2,4,8,16]},{"width":1024,"scaleFactors":[1,2,4,8,16]}],"sizes":[{"width":11250,"height":11250},{"width":1875,"height":1875},{"width":2,"height":2}],"profile":["http://iiif.io/api/image/2/level2.json",{"formats":["gif"],"supports":["profileLinkHeader","canonicalLinkHeader","regionSquare","rotationBy90s","mirroring","sizeAboveFull"]}],"protocol":"http://iiif.io/api/image"}

However, if we disable TurboJPEG by renaming turobjpeg.dll to something else and retry the request we get a different result:
{"@context":"http://iiif.io/api/image/2/context.json","@id":"http://127.0.0.1:10000/image/v2/viirs_9apr2015","width":15000,"height":15000,"profile":["http://iiif.io/api/image/2/level2.json",{"formats":["gif"],"supports":["profileLinkHeader","canonicalLinkHeader","regionSquare","rotationBy90s","mirroring","sizeAboveFull"]}],"protocol":"http://iiif.io/api/image"}

The info.json now contains the correct full image width and height but is now missing the size and tiles attributes altogether.

Are these differences intentional? Is there a way to obtain the native performance of TurboJPEG while still retaining the full image size and sites and tiles support?

Don't rotate during decoding

Currently, Hymir will do rotation during decoding if the ImageIO plugin supports it (e.g. with the Turbojpeg-Plugin).
This is very brittle and has a lot of corner-cases that fail, it would be best to just disable it alltogether and do rotation on the decoded image in all cases.

Requesting default.tiff instead of default.tif creates internal server error

GET localhost:9000/image/v2/bsb00130631_00005/full/full/0/default.tiff

should return either status code 400 (invalid request) or 301 (moved permanently) with a redirect to default.tif, but returns 500 with empty response body instead.

j.l.IllegalArgumentException: No enum constant de.digitalcollections.iiif.model.image.ImageApiProfile.Format.TIFF
	at java.lang.Enum.valueOf(Enum.java:240)
	at d.d.i.m.i.ImageApiProfile$Format.valueOf(ImageApiProfile.java:31)
	at d.d.i.h.i.f.IIIFImageApiController.getImageRepresentation(IIIFImageApiController.java:128)
	... 3 frames excluded
	at o.s.w.m.s.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197)
	at o.s.w.m.s.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141)
	at o.s.w.s.m.m.a.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
	at o.s.w.s.m.m.a.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894)
	at o.s.w.s.m.m.a.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
	... 54 frames truncated

First tile request causes an NPE

Given the setup described in #23 the first GET request for the URL

http://localhost:10000/image/v2/viirs_9apr2015/0,0,8187,8187/512,/0/default.jpg
causes an NPE to be thrown:

java.lang.NullPointerException: null
	at java.util.Objects.requireNonNull(Objects.java:203)
	at java.util.Optional.<init>(Optional.java:96)
	at java.util.Optional.of(Optional.java:108)
	at java.util.stream.FindOps$FindSink$OfRef.get(FindOps.java:193)
	at java.util.stream.FindOps$FindSink$OfRef.get(FindOps.java:190)
	at java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:152)
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:464)
	at de.digitalcollections.iiif.hymir.image.business.ImageServiceImpl.processImage(ImageServiceImpl.java:325)

Retrying the request right away causes the correct tile to be returned, though.

Question: Documentation or examples for manifest generation?

Sorry, more of a question here. I see that you mention 'highly customizable manifest generation' in the README. Are there any docs or examples I could follow for that?

I have an installation running and have set up a simple file resolver rule for the Image API with a sample image, and that is working fine. However, I don't see how to generate/access a manifest for said item via the Presentation API.

If I've misunderstood and Hymir doesn't actually generate manifests so much as present those already created, please set me straight! Thanks for your time.

No 404 http code when the image doesn't exist ?

When I use a wrong identifier in both endpoints of IIIFImageApiController, I get a 500 error instead of the expected 404.
Looking at IIIFImageApiController implementation I see that both endpoints return either a 200 code or (if anything fails in ImageService) throw an exception that results in a 500 error. (When the identifier is wrong, the exception comes from getInputStream() method in the ResourceRepository implementation that throws a ResourceIOException).

However, the spec (https://iiif.io/api/image/2.1/#server-responses) says it should return a 400 not found when "The image resource specified by identifier does not exist, the value of one or more of the parameters is not supported for this image, or the requested size is greater than the limits specified."

Am I missing something or is there something here that should be fixed ?

Performance issues

I've installed Hymir in a cloud virtual machine with 4Gb of memory. I've installled libturbojpeg. I'm serving 129Gb of images. Some images have a size of 4Mb. These images are scan of books.

I'm using Mirador to visualize books. Some of them are displayed at a reasonable speed but others are really slow, several minutes to load an image.

I don't know, if it's related to a bug or if it's a configuration issue but I think that README.md should give recommendation on:

  • How to correctly choose the -Xmx parameter
  • Any advices to improve speed

Thank you.

Bad signature for hymir package in Maven repo

I get the following error from maven while trying to read hymir 3.5.2 package of maven repo:
/home/marc/.m2/repository/de/digitalcollections/iiif-server-hymir/3.5.2/iiif-server-hymir-3.5.2.jar; invalid CEN header (bad signature)

Manifests and collections

Hi there,

I understand now that one needs to implement them, thank you. Couple of things though

LOGGER.info("Could not retrieve collection {}", collectionName, ex);
  • If I was to create my own class to implement PresentationRepository how would I set the system so that it gets called when a request is made for a collection? Same for collection.

Thank you,
Petros

Compatibility with turbojpeg.dll (renamed libturbojpeg.dll) 2.0.0, 2.0.1, 2.0.2, 2.0.3, and 2.0.4 are ok but 2.0.5 and above ...2.1.1 and 2.1.2 failed

Hi Team,
on windows 10 after the installation of different versions of libjpeg-turbo (https://sourceforge.net/projects/libjpeg-turbo/
https://sourceforge.net/projects/libjpeg-turbo/files/ )
and renaming the libturbojpeg.dll -> turbojpeg.dll and placing it on the path (on the right place);
in IntelliJ with the versions of libjpeg-turbo 2.0.0, 2.0.1, 2.0.2, 2.0.3, and 2.0.4 are the testTurboJpegInstalled() and the tests of IIIFImageApiControllerTest
OK:

turbojpeg_2 0 4_ok

turbojpeg_2 0 4_tests_ok

turbojpeg_2 0 4_tests_ok_2

but
above 2.0.4 from 2.0.5 there are failed tests
and turbojpeg is not loaded well:

turbojpeg_2 0 5_tests_failed

turbojpeg_2 0 5_failed_2

turbojpeg_2 0 5_failed_3

"Could not load libturbojpeg, plugin will be disabled"

What can be the problem with turbojpeg above 2.0.4?

Thanks in advance and all the bests
Balázs Végh

Issues with retrieving IIIF collections

I want to retrieve an IIIF-collection via Hymir and have setup the resolver rule with a "collection"-prefix as described in the documentation.

 - pattern: ^collection-([a-zA-Z0-9-_]+)$
        substitutions:
          - 'file:/collections/$1.json'

I had the assumption that i could request a collection using the Identifier and Hymir would statically add the prefix to match the defined collection rule. But when requesting the endpoint

api/presentation/v2/collection/123456

the log indicates that there is no match and it seems like the prefix is not added.
matching 123456 against ^collection-([a-zA-Z0-9-_]+)$ is false

But if i then change my request by including the prefix in the URL
api/presentation/v2/collection/collection-123456

it seems like the prefix is added to the identifier. The consequence is that now the matching is tried with a duplicated prefix

collection-collection-123456 against ^collection-([a-zA-Z0-9-_]+)$ is true

This is of course not what i want since now i file with a collection prefix is searched which does not exist:

 Could not resolve identifier collection-collection-123456 with MIME type application/json to a readable Resource
 Attempted URIs were [file:/collections/collection-123456.json]

The only way to fix this was to not append the collection-prefix in the implementation. by taking out this line.

The rule ( ^collection-([a-zA-Z0-9-_]+)$ then works as expected (if the collection-prefix is added to the URL) and the files are delivered.

image resolving delegation

We would like to use hymir for our new production system. All our images are on S3 (a few TB) so we need to customize the way hymir gets the image data based on the identifier of the image (and the http headers for authentication). The ideal for us would be to write this mechanism in Java, one possible mechanism could be to have an abstract class in hymir defining a few functions, that we could subclass (in a separate .jar) and have a way to configure hymir so that it uses our class for image resolving and auth.

I don't think hymir currently allows this mechanism, but we would be ready to implement it and propose some pull requests. Would that be reasonable? wdyt?

Activate response compression in application.yml

compression of responses (e.g. json of manifest) reduces traffic. in cases where a webserver is in front of hymir, compression should be done there. nonetheless compression reduces cache storage if webserver caches responses from application (e.g. with nginx in front of hymir)

see https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html

server.compression.enabled=false # Whether response compression is enabled.
server.compression.excluded-user-agents= # Comma-separated list of user agents for which responses should not be compressed.
server.compression.mime-types=text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json,application/xml # Comma-separated list of MIME types that should be compressed.
server.compression.min-response-size=2KB # Minimum "Content-Length" value that is required for compression to be performed.

Add support for caching

Currently Hymir does not do any caching on its own.
This is less than ideal if the images are fetched from a remote source, e.g. via HTTP or S3.
In this case, users should have the option to configure either an on-disk cache or simple LRU RAM caching.
This should be fairly simple to implement using something like EHcache.

Drastically simplify the defaults and the README

So I've been setting up a Docker setup for Hymir for the past few hours.
Some things I noticed:

  • The defaults are way too far-reaching, we're creating files in /var/log and /local, this is really unhygienic
  • The main culprits are the default logging config, Java Melody and the Tomcat Access log.
  • The logging defaults should only log to stdout
  • Is Java Melody even needed with all the Spring Boot Management stuff?
  • The Tomcat Access log should be activated by the user, not the defaults

Also, the README is a bit messy. It would be better to keep it short and simple and move all the advanced stuff into a separate documentation page (e.g. created with mkdocs).

Greyscale image with color profile "distorted"

When requesting an greyscale image with color profile (from GIMP):
"GIMP built-in D65 Grayscale with sRGB TRC"
The image in mirador is shown like "washed out": text can not be read anymore everything "lightened up".

Tested with attached file.
2

WebSecurityConfigurerAdapter is deprecated and not compatible with Spring Boot 3.x

Are there any plans to migrate the existing Spring Security configuration to APIs that are not deprecated and compatible with Spring Boot 3.x?

An example of a deprecated API can be found in SpringConfigSecurity. This class extends WebSecurityConfigurerAdapter which has been deprecated since Spring 5.7.0-M2. Since you cannot mix WebSecurityConfigurerAdapter and SecurityFilterChain, the current approach does not allow us to upgrade to Spring Boot 3.x.

Other Spring Security APIs might be deprecated as well.

File resolving could allow path traversal attack for certain patterns

Problem

For certain scenarios it is desirable to map the identifier directly to file system paths. One example is the planned quick start mode, where Hymir would be started with the image directory as single argument. Path support is important, because subdirectories can be used to group images.

Scenarios:

  1. Identifier/path contains .. as in ../../../etc/password
  2. Identifier/path starts with a / as in /etc/password

Suggested Solution:

Check all identifiers in IIIFImageApiController and IIIFPresentationApiController according to the scenarios above and cancel the request with status 400 - bad request.

Important: Characters like / or . might be URL-encoded and must be decoded before any checks.

NPE when mapping to an HTTPS URI without a file extension

We are trying to have hymir serve images obtained from another server using an REST URL.
The image identifiers used in the IIIF requests consist of decimal numbers separated by dots. They translate directly to the identifiers used in the REST URL.

However, they do NOT end with a file extension. This seems to be expected by hymir, though. The default implementation of getUris() in FileNameResolver expects MimeType.fromURI() not to return null but that method can and does.

The rules.yaml we use looks like this (the actual URL has been anonymized):

Requesting the info.json causes an NPE to be thrown:

[2018-09-14 14:19:22,669 ERROR] ions.iiif.hymir.frontend.ExceptionAdvice:  51 [http-nio-10000-exec-1] - exception stack trace
java.lang.NullPointerException: null
	at de.digitalcollections.core.backend.impl.file.repository.resource.resolver.FileNameResolver.lambda$getUris$1(FileNameResolver.java:56)
	at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:174)
	at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
	at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
	at de.digitalcollections.core.backend.impl.file.repository.resource.resolver.FileNameResolver.getUris(FileNameResolver.java:57)
	at de.digitalcollections.core.backend.impl.file.repository.resource.util.ResolvedResourcePersistenceTypeHandler.getUris(ResolvedResourcePersistenceTypeHandler.java:30)
	at de.digitalcollections.core.backend.impl.file.repository.resource.ResourceRepositoryImpl.getUris(ResourceRepositoryImpl.java:208)
	at de.digitalcollections.core.backend.impl.file.repository.resource.ResourceRepositoryImpl.find(ResourceRepositoryImpl.java:110)
	at de.digitalcollections.core.business.impl.service.ResourceServiceImpl.get(ResourceServiceImpl.java:28)
	at de.digitalcollections.iiif.hymir.image.business.ImageServiceImpl.getImageModificationDate(ImageServiceImpl.java:340)
	at de.digitalcollections.iiif.hymir.image.frontend.IIIFImageApiController.getInfo(IIIFImageApiController.java:153)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:158)
	at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:126)
	at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:111)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter.doFilterInternal(HttpTraceFilter.java:84)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320)
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:158)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:745)

Help building please

Hi there, this isn't really an issue as such. Could you please help me by pointing at the command I need to run to build from source? I've installed maven etc.. please tolerate my ignorance and feel free to close this bit I would appreciate your help!

Error in manifest files should appear in log file

With the default log levels, if a manifest file contains an error nothing appears in the log.

Step to reproduce:

  1. Edit one of your manifest file
  2. Change "@type": "dctypes:Image" to "@type": "dctypes:Illustration"
  3. Get the manifest iiif/presentation/v2/1234567890/manifest
  4. Observe that no error message appears in the log and no manifest is returned.

Desired behavior:
An error message should appear in the log

Tests in error: JpegImageTest.testDownScale:104

Observed behavior:

Running mvn clean install produces:

Tests in error:
  JpegImageTest.testDownScale:104 » UnsatisfiedLink de.digitalcollections.iiif.h...

Desired behavior

No errors in test

Steps to reproduce

On Debian 8 (Jessie), install libturbojpeg with:

wget https://github.com/dbmdz/iiif-image-api/releases/download/2.2.6/libturbojpeg-jni_1.5.1_amd64.deb
sudo dpkg -i libturbojpeg-jni_1.5.1_amd64.deb

Clone the repository with:

git clone https://github.com/dbmdz/iiif-server-hymir

Run:

mvn clean install

Remarks

  • This happens in master but not with version 3.0.1 (git checkout 3.0.1). Simply because there is no JpegImageTest in 3.0.1.
  • java version "1.8.0_121"
  • Travis build uses -DskipTests=true

How to load images from disk

Hi, sorry if this is a dumb question, but I cannot seem to work out how to display any images. I have tried using "file:/path/to/image.jp2" under "View image with OpenSeadragon", but that doesn't seem to work.

Apologies again if this is really obvious.

Access-Control-Allow-Origin header

Hi,
we have both
headers.add("Access-Control-Allow-Origin", "*")
and
response.setHeader("Access-Control-Allow-Origin", "*");
in IIIFImageApiController

However, we might want a different setting for that header or we might want to have it also for "{identifier}/{region}/{size}/{rotation}/{quality}.{format}" urls (this header is only set for redirect and info.json request types in the current implementation).
I implemented a filter in order to solve this issue (that gets even more CORS headers values from application.yml file for setting them), but It resulted in a double "Access-Control-Allow-Origin" in the response since Filters are applied before the response is built.

I therefore ended up with a custom version of IIIFImageApiController in which the lines above are commented out. I think there should be a way to externalize CORS headers settings and other headers - for instance cache related headers - (eventually by using a filter as described above).

Run Hymir faild

[2018-03-27 19:52:52,799 ERROR] org.apache.catalina.core.StandardService: 181 [main    ] - Failed to start connector [Connector[HTTP/1.1-9001]]
org.apache.catalina.LifecycleException: Failed to start component [Connector[HTTP/1.1-9001]]
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:167)
        at org.apache.catalina.core.StandardService.addConnector(StandardService.java:225)
        at org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer.addPreviouslyRemovedConnectors(TomcatEmbeddedServletContainer.java:250)
        at org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer.start(TomcatEmbeddedServletContainer.java:193)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.startEmbeddedServletContainer(EmbeddedWebApplicationContext.java:297)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.finishRefresh(EmbeddedWebApplicationContext.java:145)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
        at org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration.createChildManagementContext(EndpointWebMvcAutoConfiguration.java:193)
        at org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration.afterSingletonsInstantiated(EndpointWebMvcAutoConfiguration.java:156)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:781)
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693)
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
        at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:134)
        at de.digitalcollections.iiif.hymir.Application.main(Application.java:26)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:87)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:50)
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51)
Caused by: org.apache.catalina.LifecycleException: service.getName(): "Tomcat";  Protocol handler start failed
        at org.apache.catalina.connector.Connector.startInternal(Connector.java:1031)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
        ... 26 common frames omitted
Caused by: java.net.BindException: Address already in use
        at sun.nio.ch.Net.bind0(Native Method)
        at sun.nio.ch.Net.bind(Net.java:433)
        at sun.nio.ch.Net.bind(Net.java:425)
        at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:223)
        at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:74)
        at org.apache.tomcat.util.net.NioEndpoint.bind(NioEndpoint.java:210)
        at org.apache.tomcat.util.net.AbstractEndpoint.start(AbstractEndpoint.java:990)
        at org.apache.coyote.AbstractProtocol.start(AbstractProtocol.java:635)
        at org.apache.catalina.connector.Connector.startInternal(Connector.java:1022)
        ... 27 common frames omitted
[2018-03-27 19:52:52,820  INFO] g.apache.coyote.http11.Http11NioProtocol: 179 [main    ] - Pausing ProtocolHandler ["http-nio-9001"]
[2018-03-27 19:52:52,820  INFO] org.apache.catalina.core.StandardService: 179 [main    ] - Stopping service [Tomcat]
[2018-03-27 19:52:53,137  INFO]   org.apache.catalina.util.LifecycleBase: 179 [main    ] - The stop() method was called on component [StandardServer[-1]] after stop() had already been called. The second call will be ignored.
[2018-03-27 19:52:53,138  INFO] g.apache.coyote.http11.Http11NioProtocol: 179 [main    ] - Stopping ProtocolHandler ["http-nio-9001"]
[2018-03-27 19:52:53,139  INFO] g.apache.coyote.http11.Http11NioProtocol: 179 [main    ] - Destroying ProtocolHandler ["http-nio-9001"]
[2018-03-27 19:52:53,143  INFO] org.apache.catalina.core.StandardService: 179 [main    ] - Stopping service [Tomcat]
[2018-03-27 19:52:53,167  INFO] utoConfigurationReportLoggingInitializer: 101 [main    ] -

Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
[2018-03-27 19:52:53,168  INFO] ationConfigEmbeddedWebApplicationContext: 984 [main    ] - Closing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@134b6cb: startup date [Tue Mar 27 19:52:51 CST 2018]; parent: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@73f712
[2018-03-27 19:52:53,179 ERROR] agnostics.LoggingFailureAnalysisReporter:  42 [main    ] -

***************************
APPLICATION FAILED TO START
***************************

Description:

The Tomcat connector configured to listen on port 9001 failed to start. The port may already be in use or the connector may be misconfigured.

Action:

Verify the connector's configuration, identify and stop any process that's listening on port 9001, or configure this application to listen on another port.

[2018-03-27 19:52:53,184  INFO] ationConfigEmbeddedWebApplicationContext: 984 [main    ] - Closing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@73f712: startup date [Tue Mar 27 19:52:36 CST 2018]; root of context hierarchy
[2018-03-27 19:52:53,184  WARN] ationConfigEmbeddedWebApplicationContext:1002 [main    ] - Exception thrown from LifecycleProcessor on context close
java.lang.IllegalStateException: LifecycleProcessor not initialized - call 'refresh' before invoking lifecycle methods via the context: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@73f712: startup date [Tue Mar 27 19:52:36 CST 2018]; root of context hierarchy
        at org.springframework.context.support.AbstractApplicationContext.getLifecycleProcessor(AbstractApplicationContext.java:427)
        at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:999)
        at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:958)
        at org.springframework.boot.SpringApplication.handleRunFailure(SpringApplication.java:750)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:314)
        at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:134)
        at de.digitalcollections.iiif.hymir.Application.main(Application.java:26)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:87)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:50)
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51)
[2018-03-27 19:52:53,191  INFO] tuate.endpoint.jmx.EndpointMBeanExporter: 449 [main    ] - Unregistering JMX-exposed beans on shutdown
[2018-03-27 19:52:53,192  INFO] xport.annotation.AnnotationMBeanExporter: 449 [main    ] - Unregistering JMX-exposed beans on shutdown

I cannot figure why. The port 9001 showed {"timestamp":"2018-03-27T11:56:40.580+0000","status":404,"error":"Not Found","message":"No message available","path":"/"}
I can still open port 9000 but cannot view the pictures. The problem happened suddenly.
Please help me !!!!! Tomorrow I have to show it to my tutor.

There is a need to update api versions

iiif-apis version should be changed in pom.xml from 0.3.1 to 0.3.2 (that fixes an issue related to tiff format) and iiif-server-hymir should be upgraded in maven repo.
Thx

Extend README

The README should point out, that it's not possible to read a png file and deliver it as jpg.

Return status 400 if scaling results in zero height

A query like /iiif/image/v2/bsb00035009_00004/0,2048,1500,1/750,/0/default.jpg may result in a target image size with a zero height. This currently leads to an internal server error, but should return a response with status code 400 according to IIIF Image API 2.1.1 specification:

If the resulting height or width is zero, then the server should return a 400 (bad request) status code.

Determing the target size happens in ImageServiceImpl:253.

Request for testing:

GET localhost:9000/image/v2/bsb00035009_00004/0,2048,1500,1/750,/0/default.jpg

release?

It's been almost 9 months since the latest release and I'm wondering if there is a plan to have a new release soon? Or is there a simple way to use the git version in a spring boot release such as ours? Thanks!

Use standard semantic versioning

In the IIIF specification we can read:

Starting with the 2.0.0 releases of the IIIF Image API and Presentation API, all future specifications will follow Semantic Versioning with version numbers of the form “MAJOR.MINOR.PATCH”

Now Hymir takes url such as:
image/v2/1072035/2048,4096,1869,1078/468,/0/default.jpg
it should be
image/2/1072035/2048,4096,1869,1078/468,/0/default.jpg

Error in coordinate calculations

GET localhost:9000/image/v2/bsb00041016_00002/full/142,/90/default.jpg
 j.l.IllegalArgumentException: Selected region (3952x2808+0+0) exceeds the image boundaries (351x494).
	at d.d.t.i.TurboJpegImageReader.read(TurboJpegImageReader.java:287)
	at d.d.i.h.i.b.ImageServiceImpl.readImage(ImageServiceImpl.java:291)
	at d.d.i.h.i.b.ImageServiceImpl.processImage(ImageServiceImpl.java:370)
	at d.d.i.h.i.f.IIIFImageApiController.getImageRepresentation(IIIFImageApiController.java:164)
	... 3 frames excluded
	at o.s.w.m.s.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197)
	at o.s.w.m.s.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141)
	at o.s.w.s.m.m.a.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
	at o.s.w.s.m.m.a.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894)
	... 55 frames truncated

(stack_hash: aa1f0afb)

Image IDs containing urlencoded characters or '/' fail to be served

  • Using ID patterns in a call to the image/<id>/view.html endpoint that include a subpath (like images/set01/01.tif) result in a 404 error
  • Using ID patterns to the same endpoint that include urlencoded characters (like images%2Fset01%2F01.tif) result in a 500 error

The same behaviour occurs with other urlencoded characters like '(' (%28) or ')' (%29).
I'm aware that directly including the character '/' in the ID is forbidden per IIIF Image API; some other IIIF-Servers support it though.

Is there currently a way in Hymir to serve images in arbitrarily deep directory structures without explicitly enumerating each subdirectory?

Manifest generation?

Hi,

I am sorry if this sounds like an idiotic question - I am struggling to get my head round this whole IIIF.

I've installed the jar with TurboJPEG etc and I get nice images with zoom as below

screen shot 2018-07-20 at 12 15 40

The JPEGs are on the file system and my config is like this:

- pattern: ^(.*)$
  substitutions:
    - 'file:/home/pete/17n/$1.jpg'

- pattern: ^manifest/(.*)$
  substitutions:
    - 'file:/home/pete/17n/$1.json
  • How do I get manifests generated?
  • How do I get collections to work?

At the moment both throw a 404!

My intended use is to expose OCRed images and provide search (with SOLR I guess) and highligting (so expect more questions!)

Changing prefix in URL ?

Is it possible to configure a different prefix for the Image API server instead of the (seemingly) hard-coded "/image/v2/" ?

Custom log file path

Hello,

Hymir server is using this path /var/log/iiif/iiifServer.log to store its logs but I have restricted rights on the server and I cannot access to /var/log.

Could it be possible to configure the log file path ?

Note : As a workaround I've changed the logback config from <file>/var/log/iiif/iiifServer.log</file> to <file>${IIIF_SERVER_LOG_DIR}/iiifServer.log</file> and it works. Do you want me to provide a PR ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.