org.socialhistoryservices.api.oai.OAIRequestHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.socialhistoryservices.api.oai.OAIRequestHandler.java

Source

/*
 * OAI4Solr exposes your Solr indexes by adding a OAI2 protocol handler.
 *
 *     Copyright (C) 2011  International Institute of Social History
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU 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 Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.socialhistoryservices.api.oai;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.search.DocList;
import org.apache.solr.search.SolrQueryParser;
import org.openarchives.oai2.*;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.*;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

/**
 * OAIRequestHandler
 * <p/>
 * Parse the request as an OAI request; and perform an action.
 * <p/>
 * author: Lucien van Wouw <lwo@iisg.nl>
 */
public class OAIRequestHandler extends RequestHandlerBase {

    private final Log log = LogFactory.getLog(this.getClass());

    @Override
    /**
     * Here we instantiate the oai request and validate the parameters.
     *
     */
    public void handleRequestBody(SolrQueryRequest request, SolrQueryResponse response) throws Exception {

        final NamedList<Object> list = request.getParams().toNamedList();
        list.add("wt", Utils.getParam("wt")); // The request writer
        request.setParams(SolrParams.toSolrParams(list));

        VerbType verb;
        try {
            verb = VerbType.fromValue(request.getParams().get("verb"));
        } catch (Exception e) {
            Utils.error(response, OAIPMHerrorcodeType.BAD_VERB);
            return;
        }
        ResumptionToken oaiRequest = getRequest(request, verb);
        if (oaiRequest == null) {
            Utils.error(response, OAIPMHerrorcodeType.BAD_RESUMPTION_TOKEN);
            return;
        }

        OAIPMHtype oai = initOaiDocument(verb);
        oai.setResponseDate(Utils.getGregorianDate(new Date()));
        oai.setRequest(oaiRequest);
        switch (verb) {
        case GET_RECORD:
        case LIST_IDENTIFIERS:
        case LIST_RECORDS:
            buildQuery(request, response, oaiRequest, verb, oai);
        }
        response.add("oai", oai);
    }

    private ResumptionToken getRequest(SolrQueryRequest request, VerbType verb) {

        final SolrParams params = request.getParams();
        final NamedList<Object> list = params.toNamedList();
        ResumptionToken oaiRequest;
        try {
            oaiRequest = Utils.parseResumptionToken(params.get("resumptionToken"));
        } catch (Exception e) {
            request.setParams(SolrParams.toSolrParams(list));
            return null;
        }
        if (oaiRequest == null) {
            oaiRequest = new ResumptionToken();
            oaiRequest.setVerb(verb);
            oaiRequest.setIdentifier(params.get("identifier"));
            oaiRequest.setMetadataPrefix(params.get("metadataPrefix"));
            oaiRequest.setFrom(params.get("from"));
            oaiRequest.setUntil(params.get("until"));
            oaiRequest.setSet(params.get("set"));
            oaiRequest.setResumptionToken(params.get("resumptionToken"));
            oaiRequest.setValue((String) Utils.getParam("proxyurl"));
        } else {
            oaiRequest.setVerb(verb);
            if (oaiRequest.getFrom() != null)
                list.add("from", oaiRequest.getFrom());
            if (oaiRequest.getUntil() != null)
                list.add("until", oaiRequest.getUntil());
            if (oaiRequest.getSet() != null)
                list.add("set", oaiRequest.getSet());
            if (oaiRequest.getMetadataPrefix() != null)
                list.add("metadataPrefix", oaiRequest.getMetadataPrefix());
        }
        request.setParams(SolrParams.toSolrParams(list));
        return oaiRequest;
    }

    private void buildQuery(SolrQueryRequest request, SolrQueryResponse response, ResumptionToken oaiRequest,
            VerbType verb, OAIPMHtype oai) throws java.text.ParseException, UnsupportedEncodingException {

        List<String> q = new ArrayList<String>();
        Object maxrecords = Utils.getParam("maxrecords_" + oaiRequest.getMetadataPrefix());
        int len = (maxrecords == null) ? (Integer) Utils.getParam("maxrecords_default")
                : Integer.parseInt(String.valueOf(maxrecords));

        DocList docList = null;
        switch (verb) {
        case LIST_IDENTIFIERS:
        case LIST_RECORDS:
            if (!Utils.isValidMetadataPrefix(response, oaiRequest,
                    (OAIPMHtype) Utils.getParam(VerbType.LIST_METADATA_FORMATS.value()))) {
                return;
            }
            try {
                addTimestampToQuery(oaiRequest.getFrom(), oaiRequest.getUntil(), q);
            } catch (Exception e) {
                Utils.error(response, "Bad date format", oai);
                return;
            }
            if (Utils.isValidSet(oaiRequest.getSet(), response, oaiRequest, oai))
                addSetToQuery(oaiRequest.getSet(), q);
            else
                return;

            int cursor = oaiRequest.getCursor();
            int nextCursor = cursor + len;
            docList = runQuery(request, response, oaiRequest, verb, oai, q, cursor, len);
            final ResumptionTokenType rt = (docList.matches() > nextCursor)
                    ? Utils.setResumptionToken(oaiRequest, cursor, nextCursor, docList.matches())
                    : null;
            if (verb == VerbType.LIST_RECORDS)
                oai.setListRecords(listRecords(rt));
            else
                oai.setListIdentifiers(listIdentifiers(rt));
            break;

        case GET_RECORD:
            if (!Utils.isValidIdentifier(response, oaiRequest)) {
                return;
            }
            if (!Utils.isValidMetadataPrefix(response, oaiRequest,
                    (OAIPMHtype) Utils.getParam(VerbType.LIST_METADATA_FORMATS.value()))) {
                return;
            }
            addToQuery(String.format("%s:\"%s\"", Utils.getParam("field_index_identifier"),
                    Utils.stripOaiPrefix(oaiRequest.getIdentifier())), q);
            docList = runQuery(request, response, oaiRequest, verb, oai, q, 0, 1);
            oai.setGetRecord(getRecord(response, docList));
            break;
        }

        response.add("docList", docList);
    }

    private void addToQuery(String query, List<String> q) {
        q.add(query);
    }

    /**
     * Add the datestamp and set to the query.
     * The datestamp range is used to define a default query, in case set, from and until parameters were absent.
     *
     * @param q
     */
    private void addTimestampToQuery(String from, String until, List<String> q) throws java.text.ParseException {

        from = Utils.parseDate(from);
        until = Utils.parseDate(until);
        q.add(String.format("%s:[%s TO %s]", Utils.getParam("field_index_datestamp"), from, until));
    }

    private void addSetToQuery(String setParam, List<String> q) {

        if (setParam != null)
            addToQuery(String.format("%s:\"%s\"", Utils.getParam("field_index_set"), setParam), q);
    }

    /**
     * Return an empty OAI main document.
     * If the request was for ListMetadataFormats, ListSets or Identify, we create a cache for this and add it to the
     * response.
     *
     * @param verb
     * @return
     * @throws FileNotFoundException
     * @throws JAXBException
     */
    private OAIPMHtype initOaiDocument(VerbType verb) throws FileNotFoundException, JAXBException {

        final String v = verb.value();
        Object oaipmHtype = Utils.getParam(verb.value());
        if (oaipmHtype != null)
            return (OAIPMHtype) oaipmHtype;

        final String path = (String) Utils.getParam(v.toLowerCase());
        final File f = new File(Utils.getParam("oai_home") + File.separator + path);
        if (!f.exists())
            return new OAIPMHtype();
        final FileInputStream fis = new FileInputStream(f);
        final Source source = new StreamSource(fis);
        final Unmarshaller marshaller = (Unmarshaller) Utils.getParam("unmarshaller");
        JAXBElement<OAIPMHtype> oai = (JAXBElement<OAIPMHtype>) marshaller.unmarshal(source);
        oaipmHtype = oai.getValue();
        Utils.setParam(v, oaipmHtype);
        return (OAIPMHtype) oaipmHtype;
    }

    private DocList runQuery(SolrQueryRequest request, SolrQueryResponse response, RequestType oaiRequest,
            VerbType verb, OAIPMHtype oai, List<String> q, int cursor, int len) {
        DocList docList;
        try {
            docList = runQuery(request, q, cursor, len);
        } catch (Exception e) {
            Utils.error(response, e.getMessage(), oai);
            return null;
        }

        return docList;
    }

    private DocList runQuery(SolrQueryRequest request, List<String> q, int cursor, int len)
            throws ParseException, IOException {

        Query filter = null; // not implemented
        final SortField sortField = new SortField((String) Utils.getParam("field_sort_datestamp"), SortField.LONG,
                false);
        final Sort sort = new Sort(sortField);
        final SolrQueryParser parser = new SolrQueryParser(request.getSchema(), null);
        String[] queryParts = q.toArray(new String[0]);
        final Query query = parser.parse(Utils.join(queryParts, " AND "));
        return request.getSearcher().getDocList(query, filter, sort, cursor, len);
    }

    private ListIdentifiersType listIdentifiers(ResumptionTokenType token) {
        final ListIdentifiersType records = new ListIdentifiersType();
        records.setResumptionToken(token);
        return records;
    }

    private ListRecordsType listRecords(ResumptionTokenType token) {
        final ListRecordsType records = new ListRecordsType();
        records.setResumptionToken(token);
        return records;
    }

    private GetRecordType getRecord(SolrQueryResponse response, DocList docList) {
        if (!Utils.isAvailableIdentifier(response, docList.size()))
            return null;
        if (!Utils.hasMatchingRecords(response, docList.size()))
            return null;
        return new GetRecordType();
    }

    @Override
    public void init(NamedList args) {
        super.init(args);
        Utils.setParam(args, "wt", "oai");
        Utils.setParam(args, "proxyurl", "");
        Utils.setParam(args, "maxrecords", 200);
        Utils.setParam(args, "resumptionTokenExpirationInSeconds", 86400);
        Utils.setParam(args, "separator", ",");
        Utils.setParam(args, "field_index_identifier", "id");
        Utils.setParam(args, "prefix", "");
        Utils.setParam(args, "field_index_datestamp", "datestamp");
        Utils.setParam(args, "field_sort_datestamp", "datestamp");
        Utils.setParam(args, "field_index_set", "set");
        Utils.setParam(args, "identify", "Identify.xml");
        Utils.setParam(args, "listsets", "ListSets.xml");
        Utils.setParam(args, "listmetadataformats", "ListMetadataFormats.xml");
        final List maxrecords = args.getAll("maxrecords");
        if (maxrecords == null)
            Utils.setParam(args, "maxrecords_default", 200);
        else {
            SolrParams p = SolrParams.toSolrParams((NamedList) maxrecords.get(0));
            final Iterator<String> iterator = p.getParameterNamesIterator();
            while (iterator.hasNext()) {
                String key = iterator.next();
                Utils.setParam(args, "maxrecords_" + key, p.getInt(key));
            }
        }

        // Find the application's solr home folder
        String solr_home = SolrResourceLoader.locateSolrHome();
        if (solr_home == null)
            solr_home = System.getProperty("solr.solr.home");
        String oai_home = (String) args.get("oai_home");
        oai_home = (oai_home == null) ? solr_home + File.separatorChar + "oai" : solr_home + oai_home;
        args.remove("oai_home");
        Utils.setParam(args, "oai_home", oai_home);

        try {
            final JAXBContext jc = JAXBContext.newInstance(ObjectFactory.class);
            Utils.setParam("marshaller", jc.createMarshaller());
            Utils.setParam("unmarshaller", jc.createUnmarshaller());
        } catch (JAXBException e) {
            log.error(e);
        }

        try {
            initOaiDocument(VerbType.IDENTIFY);
            initOaiDocument(VerbType.LIST_SETS);
            initOaiDocument(VerbType.LIST_METADATA_FORMATS);
        } catch (FileNotFoundException e) {
            log.error(e);
        } catch (JAXBException e) {
            log.error(e);
        }
        addStylesheets(oai_home, (OAIPMHtype) Utils.getParam("ListMetadataFormats"));
    }

    private void addStylesheets(String oai_home, OAIPMHtype oai) {

        final List<MetadataFormatType> schemas = oai.getListMetadataFormats().getMetadataFormat();
        for (MetadataFormatType schema : schemas) {
            final String metadataPrefix = schema.getMetadataPrefix();
            addStylesheet(oai_home, metadataPrefix);
        }
    }

    private void addStylesheet(String oai_home, String metadataPrefix) {

        final TransformerFactory tf = TransformerFactory.newInstance();
        try {
            final String filename = oai_home + File.separatorChar + metadataPrefix + ".xsl";
            final File file = new File(filename);
            if (!file.exists()) {
                log.warn("Cannot find stylesheet " + filename + " for mapping solr index fields to the "
                        + metadataPrefix + " metadataPrefix.");
                return;
            }
            final Source xslSource = new StreamSource(file);
            xslSource.setSystemId(file.toURI().toURL().toString());
            final Templates templates = tf.newTemplates(xslSource);
            Utils.setParam(metadataPrefix, templates);
        } catch (TransformerConfigurationException e) {
            log.error(e);
        } catch (MalformedURLException e) {
            log.error(e);
        }
    }

    @Override
    public String getDescription() {
        return null;
    }

    @Override
    public String getSourceId() {
        return null;
    }

    @Override
    public String getSource() {
        return null;
    }

    @Override
    public String getVersion() {
        return "1.0";
    }
}