All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.gdcc.xoai.dataprovider.DataProvider Maven / Gradle / Ivy

/*
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE and NOTICE files at the root of the source
 * tree and available online at
 *
 * http://www.dspace.org/license/
 */

package io.gdcc.xoai.dataprovider;

import io.gdcc.xoai.dataprovider.handlers.ErrorHandler;
import io.gdcc.xoai.dataprovider.handlers.GetRecordHandler;
import io.gdcc.xoai.dataprovider.handlers.IdentifyHandler;
import io.gdcc.xoai.dataprovider.handlers.ListIdentifiersHandler;
import io.gdcc.xoai.dataprovider.handlers.ListMetadataFormatsHandler;
import io.gdcc.xoai.dataprovider.handlers.ListRecordsHandler;
import io.gdcc.xoai.dataprovider.handlers.ListSetsHandler;
import io.gdcc.xoai.dataprovider.model.Context;
import io.gdcc.xoai.dataprovider.repository.Repository;
import io.gdcc.xoai.dataprovider.repository.RepositoryConfiguration;
import io.gdcc.xoai.dataprovider.request.RequestBuilder;
import io.gdcc.xoai.dataprovider.request.RequestBuilder.RawRequest;
import io.gdcc.xoai.exceptions.BadVerbException;
import io.gdcc.xoai.exceptions.OAIException;
import io.gdcc.xoai.model.oaipmh.OAIPMH;
import io.gdcc.xoai.model.oaipmh.Request;
import io.gdcc.xoai.model.oaipmh.ResumptionToken;
import io.gdcc.xoai.model.oaipmh.verbs.Verb.Type;
import io.gdcc.xoai.services.api.DateProvider;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * When looking to implement an OAI-PMH data provider, you should create a servlet on your own and
 * use an instance of this class (preferably within a stateless bean to make the application server
 * scale instances if necessary) to handle the incoming requests.
 *
 * 

The only state to keep track is the configuration. The repositories need to interface with the * rest of your application anyway - they probably should be instances within the same service bean, * alongside a provider. */ public class DataProvider { private static final Logger log = LoggerFactory.getLogger(DataProvider.class); public static DataProvider dataProvider(final Context context, final Repository repository) { return new DataProvider(context, repository); } private final RepositoryConfiguration configuration; protected final IdentifyHandler identifyHandler; protected final GetRecordHandler getRecordHandler; protected final ListSetsHandler listSetsHandler; protected final ListRecordsHandler listRecordsHandler; protected final ListIdentifiersHandler listIdentifiersHandler; protected final ListMetadataFormatsHandler listMetadataFormatsHandler; protected final ErrorHandler errorsHandler; /** * Create a new data provider with a context and a repository, linked to your item and set * repository implementations. In case you need to get any custom handlers in place, extend the * class and either override this constructor or add some method to manipulate the instance * fields. * * @param context Your context * @param repository Your repository */ public DataProvider(final Context context, final Repository repository) { Objects.requireNonNull(context, "Context for data provider may not be null"); Objects.requireNonNull(repository, "Repository for data provider may not be null"); Objects.requireNonNull( repository.getItemRepository(), "Repository must contain a non-null item repository"); Objects.requireNonNull( repository.getSetRepository(), "Repository must contain a non-null sets repository"); this.configuration = repository.getConfiguration(); Objects.requireNonNull(this.configuration, "Repository configuration may not be null"); this.identifyHandler = new IdentifyHandler(context, repository); this.listSetsHandler = new ListSetsHandler(context, repository); this.listMetadataFormatsHandler = new ListMetadataFormatsHandler(context, repository); this.listRecordsHandler = new ListRecordsHandler(context, repository); this.listIdentifiersHandler = new ListIdentifiersHandler(context, repository); this.getRecordHandler = new GetRecordHandler(context, repository); this.errorsHandler = new ErrorHandler(repository.getConfiguration()); } /** * Handle a request by passing along a {@see jakarta.servlet.ServletRequest#getParameters} map. * * @param queryParameters The requests parameter map * @return The response to send to the user (you need an {@link io.gdcc.xoai.xml.XmlWriter}). * Might contain errors! * @throws io.gdcc.xoai.dataprovider.exceptions.InternalOAIException in case of serverside * errors. */ public OAIPMH handle(Map queryParameters) { OAIPMH oaipmh = new OAIPMH(); try { RawRequest rawRequest = RequestBuilder.buildRawRequest(queryParameters); // process the raw request further, even if it has errors - we might discover more // errors in // the next step return handle(rawRequest); } catch (OAIException e) { log.debug(e.getMessage(), e); return this.errorsHandler.handle(oaipmh, e); } } /** * Second entrypoint. You can also build the {@link RawRequest} yourself and start here. Please * look at {@link RequestBuilder#buildRawRequest(Map)} or {@link RequestBuilder.RawRequest} to * learn about creating these. * * @param rawRequest The minimal parsed and validated request * @return The response to send to the user (you need an {@link io.gdcc.xoai.xml.XmlWriter}). * Might contain errors! * @throws io.gdcc.xoai.dataprovider.exceptions.InternalOAIException in case of serverside * errors. */ public OAIPMH handle(final RawRequest rawRequest) { OAIPMH oaipmh = new OAIPMH(); // build the request first final Request request = RequestBuilder.buildRequest(rawRequest, this.configuration); // if there are errors, stop here, build errors and return. if (rawRequest.hasErrors()) { return this.errorsHandler.handle(oaipmh, rawRequest); } // hand down the request, now validated for the most basic things, to the real verb handlers return handle(request); } /** * Third entry point. You provide a full-fledged {@link Request}, compiled, validated etc to * work with. Please look at {@link RequestBuilder#buildRequest(RawRequest, * RepositoryConfiguration)} or {@link Request} to learn about creating these. * * @param request The validated full-fledged request to be worked on. * @return The response to send to the user (you need an {@link io.gdcc.xoai.xml.XmlWriter}). * Might contain errors! * @throws io.gdcc.xoai.dataprovider.exceptions.InternalOAIException in case of serverside * errors. */ public OAIPMH handle(Request request) { // Build a response model instance OAIPMH oaipmh = new OAIPMH().withRequest(request).withResponseDate(DateProvider.now()); try { // Try to retrieve a token sent by the client Optional clientSentToken = configuration.getResumptionTokenFormat().parse(request); // TODO: validate the token when present! // When not present, create a new token value for this initial request // Remember: an initial token never be sent back to the client. (Empty when result is // small, // additional offset otherwise) ResumptionToken.Value token = clientSentToken.orElse(ResumptionToken.ValueBuilder.build(request)); // Execute the request with a matching handler Type verb = request.getType().orElseThrow(BadVerbException::new); switch (verb) { case Identify: return oaipmh.withVerb(identifyHandler.handle(request)); case ListSets: return oaipmh.withVerb(listSetsHandler.handle(token)); case ListMetadataFormats: return oaipmh.withVerb(listMetadataFormatsHandler.handle(request)); case GetRecord: return oaipmh.withVerb(getRecordHandler.handle(request)); case ListIdentifiers: return oaipmh.withVerb(listIdentifiersHandler.handle(token)); case ListRecords: return oaipmh.withVerb(listRecordsHandler.handle(token)); default: throw new BadVerbException("Illegal verb " + verb); } } catch (OAIException e) { log.debug(e.getMessage(), e); return this.errorsHandler.handle(oaipmh, e); } } /** * Let an implementing application retrieve the OAI XSLT for nicer UI of results. The app needs * to create some endpoint to serve the content and reference that endpoint in {@link * io.gdcc.xoai.xml.XmlWriter#writeStylesheet(String)}. The app might also choose to cache the * String instead of re-reading it. * * @return The XSLT document as complete String */ public static final String getOaiXSLT() { // get resource from classpath ClassLoader classLoader = DataProvider.class.getClassLoader(); URL oaiXsltResource = classLoader.getResource("oai2.xsl"); // Prevent errors if file not found or could not be loaded if (oaiXsltResource == null) { log.warn("Could not find or load OAI XSLT file, class loader returned null"); return ""; } try (InputStream inStream = oaiXsltResource.openStream()) { return new String(inStream.readAllBytes(), StandardCharsets.UTF_8); } catch (IOException e) { log.warn("Could not read OAI XSLT file", e); return ""; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy