View Javadoc

1   /*
2    * Copyright 2015 Data Archiving and Networked Services (an institute of
3    * Koninklijke Nederlandse Akademie van Wetenschappen), King's College London,
4    * Georg-August-Universitaet Goettingen Stiftung Oeffentlichen Rechts
5    *
6    * Licensed under the EUPL, Version 1.1 or – as soon they will be approved by
7    * the European Commission - subsequent versions of the EUPL (the "Licence");
8    * You may not use this work except in compliance with the Licence.
9    * You may obtain a copy of the Licence at:
10   *
11   * https://joinup.ec.europa.eu/software/page/eupl
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the Licence is distributed on an "AS IS" basis,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the Licence for the specific language governing
17   * permissions and limitations under the Licence.
18   */
19  
20  package eu.ehri.extension;
21  
22  import com.typesafe.config.Config;
23  import com.typesafe.config.ConfigFactory;
24  import eu.ehri.extension.base.AbstractResource;
25  import eu.ehri.project.api.Api;
26  import eu.ehri.project.core.Tx;
27  import eu.ehri.project.exporters.xml.IndentingXMLStreamWriter;
28  import eu.ehri.project.oaipmh.OaiPmhData;
29  import eu.ehri.project.oaipmh.OaiPmhExporter;
30  import eu.ehri.project.oaipmh.OaiPmhRenderer;
31  import eu.ehri.project.oaipmh.OaiPmhState;
32  import eu.ehri.project.oaipmh.errors.OaiPmhError;
33  import org.neo4j.graphdb.GraphDatabaseService;
34  
35  import javax.ws.rs.GET;
36  import javax.ws.rs.POST;
37  import javax.ws.rs.Path;
38  import javax.ws.rs.Produces;
39  import javax.ws.rs.core.Context;
40  import javax.ws.rs.core.HttpHeaders;
41  import javax.ws.rs.core.MediaType;
42  import javax.ws.rs.core.Response;
43  import javax.ws.rs.core.StreamingOutput;
44  import javax.xml.stream.XMLOutputFactory;
45  import javax.xml.stream.XMLStreamException;
46  import java.io.BufferedOutputStream;
47  import java.util.List;
48  
49  
50  /**
51   * Open Archives Initiative Protocol for Metadata Harvesting
52   * (OAI-PMH) 2.0 server implementation.
53   */
54  @Path(OaiPmhResource.ENDPOINT)
55  public class OaiPmhResource extends AbstractResource {
56  
57      private static final Config config = ConfigFactory.load();
58      private static final XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newFactory();
59  
60      public static final String ENDPOINT = "oaipmh";
61      public static final String LIMIT_HEADER_NAME = "X-Limit";
62      private static final String DEFAULT_LANG_CODE = "eng";
63  
64  
65  
66      public OaiPmhResource(@Context GraphDatabaseService database) {
67          super(database);
68      }
69  
70      /**
71       * OAI-PMH 2.0 base URL. See specification for usage.
72       * <p>
73       * http://www.openarchives.org/OAI/openarchivesprotocol.html
74       *
75       * @return an OAI-PMH XML payload as a chunked response.
76       */
77      @GET
78      @Produces(MediaType.TEXT_XML)
79      public Response oaiGet() {
80          final int limit = isStreaming() ? -1 : limit(config.getInt("oaipmh.numResponses"));
81          return Response.ok((StreamingOutput) out -> {
82              try (final Tx tx = beginTx();
83                   final BufferedOutputStream bufferedOut = new BufferedOutputStream(out);
84                   final IndentingXMLStreamWriter sw = new IndentingXMLStreamWriter(
85                           xmlOutputFactory.createXMLStreamWriter(bufferedOut))) {
86                  Api api = anonymousApi();
87                  OaiPmhExporter oaiPmh = new OaiPmhExporter(
88                          OaiPmhData.create(api),
89                          OaiPmhRenderer.defaultRenderer(api, DEFAULT_LANG_CODE),
90                          config);
91                  try {
92                      OaiPmhState state = OaiPmhState.parse(uriInfo.getRequestUri().getQuery(), limit);
93                      oaiPmh.performVerb(sw, state);
94                      tx.success();
95                  } catch (OaiPmhError error) {
96                      oaiPmh.error(sw, error.getCode(), error.getMessage(), null);
97                  }
98              } catch (XMLStreamException e) {
99                  throw new RuntimeException(e);
100             }
101         }).header(HttpHeaders.CONTENT_TYPE,
102                 MediaType.TEXT_XML + "; charset=utf-8")
103                 .build();
104     }
105 
106     @POST
107     @Produces(MediaType.TEXT_XML)
108     public Response oaiPost() {
109         return oaiGet();
110     }
111 
112     private int limit(int defaultLimit) {
113         // Allow overriding the limit via a header. This is safe since
114         // we stream requests. It's also very handy for testing.
115         List<String> limit = requestHeaders.getRequestHeader(LIMIT_HEADER_NAME);
116         if (limit == null || limit.size() < 1) {
117             return defaultLimit;
118         } else {
119             try {
120                 return Integer.parseInt(limit.get(0));
121             } catch (NumberFormatException e) {
122                 return defaultLimit;
123             }
124         }
125     }
126 }