changeset 1613:1d21cb0d697c iiif-presentation-2

first version of Manifester servlet for creating simple IIIF presentation API manifests (does not really work yet).
author Robert Casties <casties@mpiwg-berlin.mpg.de>
date Wed, 24 May 2017 21:02:19 +0200
parents 4018355cb442
children e3c6e4c0d93d
files .hgignore common/src/main/java/digilib/conf/DigilibConfiguration.java common/src/main/java/digilib/io/DocuDirectory.java iiif-presentation/pom.xml iiif-presentation/src/main/java/digilib/conf/ManifestServletConfiguration.java iiif-presentation/src/main/java/digilib/servlet/Manifester.java pom.xml servlet3/src/main/java/digilib/servlet/Scaler.java webapp/pom.xml webapp/src/main/webapp/WEB-INF/web-3.0.xml
diffstat 10 files changed, 554 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Wed May 24 15:58:54 2017 +0200
+++ b/.hgignore	Wed May 24 21:02:19 2017 +0200
@@ -129,4 +129,6 @@
 syntax: regexp
 ^webapp/src/main/webapp/jquery/jquery\.digilib-basic\.js$
 syntax: regexp
-^webapp/src/main/webapp/jquery/jquery\.digilib-basic\.min\.css$
\ No newline at end of file
+^webapp/src/main/webapp/jquery/jquery\.digilib-basic\.min\.css$
+syntax: regexp
+^iiif-presentation/target$
\ No newline at end of file
--- a/common/src/main/java/digilib/conf/DigilibConfiguration.java	Wed May 24 15:58:54 2017 +0200
+++ b/common/src/main/java/digilib/conf/DigilibConfiguration.java	Wed May 24 21:02:19 2017 +0200
@@ -57,7 +57,7 @@
 
     /** digilib version */
     public static String getClassVersion() {
-        return "2.5.2a";
+        return "2.5.3a";
     }
 
     /* non-static getVersion for Java inheritance */
--- a/common/src/main/java/digilib/io/DocuDirectory.java	Wed May 24 15:58:54 2017 +0200
+++ b/common/src/main/java/digilib/io/DocuDirectory.java	Wed May 24 21:02:19 2017 +0200
@@ -30,6 +30,7 @@
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 
 import digilib.conf.DigilibConfiguration;
@@ -47,7 +48,7 @@
  * 
  * @author casties
  */
-public abstract class DocuDirectory extends Directory {
+public abstract class DocuDirectory extends Directory implements Iterable<DocuDirent> {
 
     /** type of files in this DocuDirectory */
     protected FileClass fileClass = FileClass.IMAGE;
@@ -347,6 +348,15 @@
     public DirMeta getMeta() {
         return meta;
     }
+    
+    /**
+     * Returns an Iterator over all DocuDirents in this DocuDirectory in default order.
+     * 
+     * @return
+     */
+    public Iterator<DocuDirent> iterator() {
+    	return files.iterator();
+    }
 
     private boolean isBasenameInList(List<DocuDirent> fileList, int idx, String fn) {
     	String dfn = FileOps.basename((fileList.get(idx)).getName());
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/iiif-presentation/pom.xml	Wed May 24 21:02:19 2017 +0200
@@ -0,0 +1,41 @@
+<?xml version="1.0"?>
+<project
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+	xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>digilib</groupId>
+		<artifactId>digilib</artifactId>
+		<version>2.5-SNAPSHOT</version>
+	</parent>
+	<artifactId>digilib-iiif-presentation</artifactId>
+	<name>digilib-iiif-presentation</name>
+	<description>The Digital Image Library - IIIF presentation API manifest serving servlet</description>
+	<url>http://digilib.sourceforge.net</url>
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>digilib</groupId>
+			<artifactId>digilib-servlet</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.mortbay.jetty</groupId>
+			<artifactId>servlet-api</artifactId>
+			<version>3.0.20100224</version>
+			<type>jar</type>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>javax.json</groupId>
+			<artifactId>javax.json-api</artifactId>
+			<version>1.1</version>
+		</dependency>
+		<dependency>
+			<groupId>org.glassfish</groupId>
+			<artifactId>javax.json</artifactId>
+			<version>1.1</version>
+		</dependency>
+	</dependencies>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/iiif-presentation/src/main/java/digilib/conf/ManifestServletConfiguration.java	Wed May 24 21:02:19 2017 +0200
@@ -0,0 +1,85 @@
+package digilib.conf;
+
+import javax.servlet.ServletContext;
+import javax.servlet.annotation.WebListener;
+
+/**
+ * Class to hold the digilib servlet configuration parameters. The parameters
+ * can be read from the digilib-config file and be passed to other servlets or
+ * beans.
+ * 
+ * @author casties
+ */
+@WebListener
+public class ManifestServletConfiguration extends DigilibServletConfiguration {
+
+    public static final String MANIFEST_SERVLET_CONFIG_KEY = "digilib.manifest.servlet.configuration";
+    
+    public static String getClassVersion() {
+        return DigilibConfiguration.getClassVersion() + " manif";
+    }
+
+    /** non-static getVersion for Java inheritance */
+    @Override
+    public String getVersion() {
+    	return getClassVersion();
+    }
+    
+    /**
+     * Constructs DigilibServletConfiguration and defines all parameters and
+     * their default values.
+     */
+    public ManifestServletConfiguration() {
+        super();
+        // more parameters...
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see digilib.conf.DigilibServletConfiguration#configure(javax.servlet.
+     * ServletContext)
+     */
+    @Override
+    public void configure(ServletContext context) {
+        super.configure(context);
+
+        // set version
+        setValue("servlet.version", getVersion());
+
+    }
+
+    /**
+     * Sets the current DigilibConfiguration in the context. 
+     * @param context
+     */
+    @Override
+    public void setContextConfig(ServletContext context) {
+        context.setAttribute(ManifestServletConfiguration.MANIFEST_SERVLET_CONFIG_KEY, this);
+    }
+    
+    /**
+     * Returns the current TextServletConfiguration from the context.
+     * 
+     * @param context
+     * @return
+     */
+    public static DigilibServletConfiguration getCurrentConfig(ServletContext context) {
+        DigilibServletConfiguration config = (DigilibServletConfiguration) context
+                .getAttribute(ManifestServletConfiguration.MANIFEST_SERVLET_CONFIG_KEY);
+        return config;
+    }
+
+    /**
+     * Returns the current DigilibConfiguration from the context.
+     * (non-static method, for Java inheritance)
+     * 
+     * @param context
+     * @return
+     */
+    @Override
+    protected DigilibServletConfiguration getContextConfig(ServletContext context) {
+        return getCurrentConfig(context);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/iiif-presentation/src/main/java/digilib/servlet/Manifester.java	Wed May 24 21:02:19 2017 +0200
@@ -0,0 +1,369 @@
+package digilib.servlet;
+
+/*
+ * #%L
+ * 
+ * Texter.java -- Servlet for displaying text
+ * 
+ * Digital Image Library servlet components
+ * %%
+ * Copyright (C) 2003 - 2017 MPIWG Berlin
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as 
+ * published by the Free Software Foundation, either version 3 of the 
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Lesser Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Lesser Public 
+ * License along with this program.  If not, see
+ * <http://www.gnu.org/licenses/lgpl-3.0.html>.
+ * #L%
+ * Author: Robert Casties (robcast@sourceforge.net)
+ * Created on 24.5.2017
+ */
+
+import java.io.IOException;
+
+import javax.json.Json;
+import javax.json.stream.JsonGenerator;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.log4j.Logger;
+
+import digilib.auth.AuthzOps;
+import digilib.conf.DigilibServletConfiguration;
+import digilib.conf.DigilibServletRequest;
+import digilib.conf.ManifestServletConfiguration;
+import digilib.io.DocuDirCache;
+import digilib.io.DocuDirectory;
+import digilib.io.DocuDirent;
+import digilib.io.FileOps;
+import digilib.io.ImageFileSet;
+import digilib.io.ImageInput;
+import digilib.util.ImageSize;
+
+/**
+ * Servlet for displaying text
+ * 
+ * 
+ * @author casties
+ * 
+ */
+public class Manifester extends HttpServlet {
+
+	private static final long serialVersionUID = 6678666342141409868L;
+
+	/** Servlet version */
+	public static String tlVersion = ManifestServletConfiguration.getClassVersion();
+
+	/** DigilibConfiguration instance */
+	DigilibServletConfiguration dlConfig = null;
+
+	/** general logger */
+	Logger logger = Logger.getLogger("digilib.texter");
+
+	/** logger for accounting requests */
+	protected static Logger accountlog = Logger.getLogger("account.texter.request");
+
+	/** FileOps instance */
+	FileOps fileOp;
+
+	/** AuthOps instance */
+	AuthzOps authzOp;
+
+	/** ServletOps instance */
+	ServletOps servletOp;
+
+	/** DocuDirCache instance */
+	DocuDirCache dirCache;
+
+	/** use authentication */
+	boolean useAuthorization = false;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
+	 */
+	public void init(ServletConfig config) throws ServletException {
+		super.init(config);
+
+		System.out.println("***** Digital Image Library IIF Manifest Servlet (version " + tlVersion + ") *****");
+
+		// get our ServletContext
+		ServletContext context = config.getServletContext();
+		// see if there is a Configuration instance
+		dlConfig = ManifestServletConfiguration.getCurrentConfig(context);
+		if (dlConfig == null) {
+			// no Configuration
+			throw new ServletException("No Configuration!");
+		}
+		// say hello in the log file
+		logger.info("***** Digital Image Library IIIF Manifest Servlet (version " + tlVersion + ") *****");
+
+		// set our AuthOps
+		useAuthorization = dlConfig.getAsBoolean("use-authorization");
+		authzOp = (AuthzOps) dlConfig.getValue(DigilibServletConfiguration.AUTHZ_OP_KEY);
+		// DocuDirCache instance
+		dirCache = (DocuDirCache) dlConfig.getValue(DigilibServletConfiguration.DIR_CACHE_KEY);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.
+	 * HttpServletRequest, javax.servlet.http.HttpServletResponse)
+	 */
+	protected void doGet(HttpServletRequest request, HttpServletResponse response)
+			throws ServletException, IOException {
+		accountlog.info("GET from " + request.getRemoteAddr());
+		// do the processing
+		processRequest(request, response);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.
+	 * HttpServletRequest, javax.servlet.http.HttpServletResponse)
+	 */
+	protected void doPost(HttpServletRequest request, HttpServletResponse response)
+			throws ServletException, IOException {
+		accountlog.info("POST from " + request.getRemoteAddr());
+		// do the processing
+		processRequest(request, response);
+	}
+
+	protected void processRequest(HttpServletRequest request, HttpServletResponse response) {
+
+		/*
+		 * request parameters
+		 */
+		// create new request with defaults
+		DigilibServletRequest dlRequest = new DigilibServletRequest(request, dlConfig);
+		try {
+			// get directory path
+			String dlFn = dlRequest.getFilePath();
+			// get information about the directory
+			DocuDirectory dlDir = dirCache.getDirectory(dlFn);
+			if (dlDir == null) {
+				logger.error("Directory for manifest not found: " + dlFn);
+				response.sendError(HttpServletResponse.SC_NOT_FOUND);
+				return;
+			}
+			/*
+			 * set CORS header ACAO "*" for info response as per IIIF spec
+			 */
+			if (dlConfig.getAsBoolean("iiif-info-cors")) {
+				String origin = request.getHeader("Origin");
+				if (origin != null) {
+					response.setHeader("Access-Control-Allow-Origin", "*");
+				}
+			}
+
+			// use JSON-LD content type only when asked
+			String accept = request.getHeader("Accept");
+			if (accept != null && accept.contains("application/ld+json")) {
+				response.setContentType("application/ld+json");
+			} else {
+				response.setContentType("application/json");
+			}
+
+			/*
+			 * get manifest URL
+			 */
+			String url = request.getRequestURL().toString();
+			if (url.endsWith("/")) {
+				url = url.substring(0, url.lastIndexOf("/"));
+			}
+
+			/*
+			 * create json representation
+			 */
+			ServletOutputStream out = response.getOutputStream();
+			JsonGenerator manifest = Json.createGenerator(out).writeStartObject();
+			/*
+			 * manifest metadata
+			 */
+			writeManifestMeta(dlFn, url, manifest);
+			/*
+			 * sequences
+			 */
+			manifest.writeStartArray("sequences");
+			/*
+			 * first sequence
+			 */
+			writeSequence(dlDir, url, manifest);
+			
+			manifest.writeEnd(); // sequences
+			
+			manifest.writeEnd(); // manifest
+			manifest.close();
+
+		} catch (IOException e) {
+			logger.error("ERROR sending manifest: ", e);
+		}
+	}
+
+	/**
+	 * @param dlDir
+	 * @param url
+	 * @param manifest
+	 */
+	protected void writeSequence(DocuDirectory dlDir, String url, JsonGenerator manifest) {
+		manifest.writeStartObject()
+			.write("@id", url + "/sequence/default")
+			.write("@type", "sc:Sequence")
+			.write("label", "Scan image order");
+		/*
+		 * canvases
+		 */
+		writeCanvases(dlDir, url, manifest);
+		
+		manifest.writeEnd(); // sequence
+	}
+
+	/**
+	 * @param dlDir
+	 * @param url
+	 * @param manifest
+	 */
+	protected void writeCanvases(DocuDirectory dlDir, String url, JsonGenerator manifest) {
+		/*
+		 * list of canvases
+		 */
+		manifest.writeStartArray("canvases");
+		
+		int idx = 0;
+		for (DocuDirent imgFile : dlDir) {
+			idx += 1;
+			ImageFileSet imgFs = (ImageFileSet) imgFile;
+			ImageInput img = imgFs.getBiggest();
+			ImageSize imgSize = img.getSize();
+			/*
+			 * canvas
+			 */
+			writeCanvas(url, manifest, idx, imgFile, imgSize);
+		}
+		
+		manifest.writeEnd(); // canvases
+	}
+
+	/**
+	 * @param url
+	 * @param manifest
+	 * @param idx
+	 * @param imgFile
+	 * @param imgSize
+	 */
+	protected void writeCanvas(String url, JsonGenerator manifest, int idx, DocuDirent imgFile, ImageSize imgSize) {
+		manifest.writeStartObject()
+			.write("@type", "sc:Canvas")
+			.write("@id", url + "/canvas/p" + idx)
+			.write("label", "image " + imgFile.getName())
+			.write("height", imgSize.getHeight())
+			.write("width", imgSize.getWidth());
+		/*
+		 * images
+		 */
+		writeImages(url, manifest, idx, imgFile, imgSize);
+		
+		manifest.writeEnd(); // canvas
+	}
+
+	/**
+	 * @param url
+	 * @param manifest
+	 * @param idx
+	 * @param imgFile
+	 * @param imgSize
+	 */
+	protected void writeImages(String url, JsonGenerator manifest, int idx, DocuDirent imgFile, ImageSize imgSize) {
+		/*
+		 * list of images (just one)
+		 */
+		manifest.writeStartArray("images");
+		/*
+		 * image
+		 */
+		writeImage(url, manifest, idx, imgFile, imgSize);
+		
+		manifest.writeEnd(); // images
+	}
+
+	/**
+	 * @param url
+	 * @param manifest
+	 * @param idx
+	 * @param imgFile
+	 * @param imgSize
+	 */
+	protected void writeImage(String url, JsonGenerator manifest, int idx, DocuDirent imgFile, ImageSize imgSize) {
+		manifest.writeStartObject()
+			.write("@type", "oa:Annotation")
+			.write("@id", url + "/annotation/p" + idx + "-image")
+			.write("motivation", "sc:painting");
+		/*
+		 * resource
+		 */
+		writeResource(url, manifest, imgFile, imgSize);
+		
+		manifest.write("on", url + "/canvas/p" + idx)
+			.writeEnd(); // image
+	}
+
+	/**
+	 * @param url
+	 * @param manifest
+	 * @param imgFile
+	 * @param imgSize
+	 */
+	protected void writeResource(String url, JsonGenerator manifest, DocuDirent imgFile, ImageSize imgSize) {
+		manifest.writeStartObject("resource")
+			.write("@id", url + "/" + imgFile.getName())
+			.write("@type", "dctypes:Image");
+		/*
+		 * service
+		 */
+		writeService(manifest);
+		
+		manifest.write("height", imgSize.getHeight())
+			.write("width", imgSize.getWidth())
+			.writeEnd(); // resource
+	}
+
+	/**
+	 * @param manifest
+	 */
+	protected void writeService(JsonGenerator manifest) {
+		manifest.writeStartObject("service")
+			.write("@context", "http://iiif.io/api/image/2/context.json")
+			.write("@id", "digilib-iiif???")
+			.write("profile", "http://iiif.io/api/image/2/profiles/level2.json")
+			.writeEnd(); // service
+	}
+
+	/**
+	 * @param dlFn
+	 * @param url
+	 * @param manifest
+	 */
+	protected void writeManifestMeta(String dlFn, String url, JsonGenerator manifest) {
+		manifest.write("@context", "http://iiif.io/api/presentation/2/context.json")
+			.write("@type", "sc:Manifest")
+			.write("@id", url + "/manifest")
+			.write("label", "(Scanned work " + dlFn + ")");
+	}
+
+}
\ No newline at end of file
--- a/pom.xml	Wed May 24 15:58:54 2017 +0200
+++ b/pom.xml	Wed May 24 21:02:19 2017 +0200
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
 
   <properties>
@@ -158,6 +157,12 @@
       </modules>
     </profile>
     <profile>
+      <id>iiif-presentation</id>
+      <modules>
+        <module>iiif-presentation</module>
+      </modules>
+    </profile>
+    <profile>
       <id>pdf</id>
       <modules>
         <module>pdf</module>
@@ -230,6 +235,11 @@
       </dependency>
       <dependency>
         <groupId>digilib</groupId>
+        <artifactId>digilib-iiif-presentation</artifactId>
+        <version>2.5-SNAPSHOT</version>
+      </dependency>
+      <dependency>
+        <groupId>digilib</groupId>
         <artifactId>digilib-servlet</artifactId>
         <version>2.5-SNAPSHOT</version>
       </dependency>
--- a/servlet3/src/main/java/digilib/servlet/Scaler.java	Wed May 24 15:58:54 2017 +0200
+++ b/servlet3/src/main/java/digilib/servlet/Scaler.java	Wed May 24 21:02:19 2017 +0200
@@ -142,7 +142,7 @@
         authzOp = (AuthzOps) dlConfig.getValue(DigilibServletConfiguration.AUTHZ_OP_KEY);
 
         // DocuDirCache instance
-        dirCache = (DocuDirCache) dlConfig.getValue("servlet.dir.cache");
+        dirCache = (DocuDirCache) dlConfig.getValue(DigilibServletConfiguration.DIR_CACHE_KEY);
 
         // Executor
         imageJobCenter = (DigilibJobCenter<DocuImage>) dlConfig.getValue("servlet.worker.imageexecutor");
--- a/webapp/pom.xml	Wed May 24 15:58:54 2017 +0200
+++ b/webapp/pom.xml	Wed May 24 21:02:19 2017 +0200
@@ -276,6 +276,17 @@
 				</dependency>
 			</dependencies>
 		</profile>
+        <profile>
+            <id>iiif-presentation</id>
+            <dependencies>
+                <dependency>
+                    <groupId>digilib</groupId>
+                    <artifactId>digilib-iiif-presentation</artifactId>
+                    <type>jar</type>
+                    <scope>compile</scope>
+                </dependency>
+            </dependencies>
+        </profile>
 		<profile>
 			<id>codec-jai</id>
 			<dependencies>
--- a/webapp/src/main/webapp/WEB-INF/web-3.0.xml	Wed May 24 15:58:54 2017 +0200
+++ b/webapp/src/main/webapp/WEB-INF/web-3.0.xml	Wed May 24 21:02:19 2017 +0200
@@ -50,4 +50,24 @@
             /Scaler/*
         </url-pattern>
   </servlet-mapping>
+  
+    <!-- The Manifest servlet -->
+    <servlet>
+        <servlet-name>Manifester</servlet-name>
+        <servlet-class>digilib.servlet.Manifester</servlet-class>
+    </servlet>
+    <!-- The Intialisation Listener -->
+    <listener>
+        <listener-class>
+            digilib.conf.ManifestServletConfiguration
+        </listener-class>
+    </listener>
+    <!-- The mapping for the Manifest servlet -->
+    <servlet-mapping>
+        <servlet-name>Manifester</servlet-name>
+        <url-pattern>/servlet/Manifester/*</url-pattern>
+    </servlet-mapping>
+
+
+  
 </web-app>