source: gs3-extensions/iiif-servlet/trunk/src/src/main/java/edu/illinois/library/cantaloupe/resource/iiif/v2/GSInformationResource.java@ 32891

Last change on this file since 32891 was 32891, checked in by davidb, 5 years ago

Additional error checking

File size: 7.7 KB
Line 
1package edu.illinois.library.cantaloupe.resource.iiif.v2;
2
3import java.io.IOException;
4import java.nio.file.Files;
5import java.nio.file.NoSuchFileException;
6import java.nio.file.Path;
7import java.util.List;
8
9import edu.illinois.library.cantaloupe.RestletApplication;
10import edu.illinois.library.cantaloupe.cache.CacheFacade;
11import edu.illinois.library.cantaloupe.config.Configuration;
12import edu.illinois.library.cantaloupe.config.Key;
13import edu.illinois.library.cantaloupe.image.Format;
14import edu.illinois.library.cantaloupe.image.Identifier;
15import edu.illinois.library.cantaloupe.image.Info;
16import edu.illinois.library.cantaloupe.processor.Processor;
17import edu.illinois.library.cantaloupe.processor.ProcessorFactory;
18import edu.illinois.library.cantaloupe.source.Source;
19import edu.illinois.library.cantaloupe.source.SourceFactory;
20import edu.illinois.library.cantaloupe.resource.JSONRepresentation;
21import edu.illinois.library.cantaloupe.processor.ProcessorConnector;
22import org.restlet.data.MediaType;
23import org.restlet.data.Preference;
24import org.restlet.data.Reference;
25import org.restlet.representation.EmptyRepresentation;
26import org.restlet.representation.Representation;
27import org.restlet.resource.Get;
28
29//import org.greenstone.gsdl3.IIIFServerBridge; // ****
30
31/**
32 * Handles IIIF Image API 2.x information requests.
33 *
34 * @see <a href="http://iiif.io/api/image/2.1/#information-request">Information
35 * Requests</a>
36 */
37public class GSInformationResource extends InformationResource {
38
39 /**
40 * Redirects {@literal /:identifier} to {@literal /:identifier/info.json},
41 * respecting the Servlet context root and
42 * {@link #PUBLIC_IDENTIFIER_HEADER} header.
43 */
44 public static class RedirectingResource extends IIIF2Resource {
45 @Get
46 public Representation doGet() {
47 final Reference newRef = new Reference(
48 getPublicRootReference() +
49 RestletApplication.IIIF_2_PATH + "/" +
50 getPublicIdentifier() +
51 "/info.json");
52 redirectSeeOther(newRef);
53 return new EmptyRepresentation();
54 }
55 }
56
57 /**
58 * Responds to information requests.
59 *
60 * @return {@link ImageInfo} instance serialized as JSON.
61 */
62 @Get
63 public Representation doGet() throws Exception {
64 final Configuration config = Configuration.getInstance();
65 final Identifier identifier = getIdentifier();
66 final CacheFacade cacheFacade = new CacheFacade();
67
68 // If we don't need to resolve first, and are using a cache, and the
69 // cache contains an info matching the request, skip all the setup and
70 // just return the cached info.
71 if (!isResolvingFirst()) {
72 try {
73 Info info = cacheFacade.getInfo(identifier);
74 if (info != null) {
75 // The source format will be null or UNKNOWN if the info was
76 // serialized in version < 3.4.
77 final Format format = info.getSourceFormat();
78 if (format != null && !Format.UNKNOWN.equals(format)) {
79 final Processor processor = new ProcessorFactory().
80 newProcessor(format);
81 commitCustomResponseHeaders();
82 return newRepresentation(info, processor);
83 }
84 }
85 } catch (IOException e) {
86 // Don't rethrow -- it's still possible to service the request.
87 getLogger().severe(e.getMessage());
88 }
89 }
90
91 final Identifier identifier_image = IdentifierToGSAssocfile.createIdentifierImage(identifier);
92 final Source source = new SourceFactory().newSource(identifier_image, getDelegateProxy());
93
94 // If we are resolving first, or if the source image is not present in
95 // the source cache (if enabled), check access to it in preparation for
96 // retrieval.
97 final Path sourceImage = cacheFacade.getSourceCacheFile(identifier);
98 if (sourceImage == null || isResolvingFirst()) {
99 try {
100 source.checkAccess();
101 } catch (NoSuchFileException e) { // this needs to be rethrown!
102 if (config.getBoolean(Key.CACHE_SERVER_PURGE_MISSING, false)) {
103 // If the image was not found, purge it from the cache.
104 cacheFacade.purgeAsync(identifier);
105 }
106 throw e;
107 }
108 }
109
110 // Get the format of the source image.
111 // If we are not resolving first, and there is a hit in the source
112 // cache, read the format from the source-cached-file, as we will
113 // expect source cache access to be more efficient.
114 // Otherwise, read it from the source.
115 Format format = Format.UNKNOWN;
116 if (!isResolvingFirst() && sourceImage != null) {
117 List<edu.illinois.library.cantaloupe.image.MediaType> mediaTypes =
118 edu.illinois.library.cantaloupe.image.MediaType.detectMediaTypes(sourceImage);
119 if (!mediaTypes.isEmpty()) {
120 format = mediaTypes.get(0).toFormat();
121 }
122 } else {
123 format = source.getFormat();
124 }
125
126 // Obtain an instance of the processor assigned to that format.
127 try (Processor processor = new ProcessorFactory().newProcessor(format)) {
128 // Connect it to the source.
129 tempFileFuture = new ProcessorConnector().connect(
130 source, processor, identifier, format);
131
132 final Info info = getOrReadInfo(identifier, processor);
133
134 commitCustomResponseHeaders();
135
136 return newRepresentation(info, processor);
137 }
138 }
139
140 /**
141 * @return Full image URI corresponding to the given identifier, respecting
142 * the {@literal X-Forwarded-*} and
143 * {@link #PUBLIC_IDENTIFIER_HEADER} reverse proxy headers.
144 */
145 private String getImageURI() {
146 return getPublicRootReference() + RestletApplication.IIIF_2_PATH + "/" +
147 getPublicIdentifier();
148 }
149
150 private MediaType getNegotiatedMediaType() {
151 MediaType mediaType;
152 // If the client has requested JSON-LD, set the content type to
153 // that; otherwise set it to JSON.
154 List<Preference<MediaType>> preferences = getRequest().getClientInfo().
155 getAcceptedMediaTypes();
156 if (preferences.get(0) != null && preferences.get(0).toString().
157 startsWith("application/ld+json")) {
158 mediaType = new MediaType("application/ld+json");
159 } else {
160 mediaType = new MediaType("application/json");
161 }
162 return mediaType;
163 }
164
165 private Representation newRepresentation(Info info,
166 Processor processor) {
167 final ImageInfoFactory factory = new ImageInfoFactory(
168 processor.getSupportedFeatures(),
169 processor.getSupportedIIIF2Qualities(),
170 processor.getAvailableOutputFormats());
171 factory.setDelegateProxy(getDelegateProxy());
172
173 final ImageInfo<String, Object> imageInfo = factory.newImageInfo(
174 getImageURI(), info, getPageIndex());
175 final MediaType mediaType = getNegotiatedMediaType();
176
177 return new JSONRepresentation(imageInfo, mediaType, () -> {
178 if (tempFileFuture != null) {
179 Path tempFile = tempFileFuture.get();
180 if (tempFile != null) {
181 Files.deleteIfExists(tempFile);
182 }
183 }
184 return null;
185 });
186 }
187
188 private boolean isResolvingFirst() {
189 return Configuration.getInstance().
190 getBoolean(Key.CACHE_SERVER_RESOLVE_FIRST, true);
191 }
192
193}
Note: See TracBrowser for help on using the repository browser.