org.callimachusproject.xproc.SparqlStep.java Source code

Java tutorial

Introduction

Here is the source code for org.callimachusproject.xproc.SparqlStep.java

Source

/*
 * Copyright (c) 2013 3 Round Stones Inc., Some Rights Reserved
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package org.callimachusproject.xproc;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URI;
import java.util.LinkedHashMap;
import java.util.Map;

import net.sf.saxon.s9api.QName;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.Serializer;
import net.sf.saxon.s9api.XdmNode;

import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.utils.URIUtils;
import org.callimachusproject.engine.ParameterizedQuery;
import org.callimachusproject.engine.ParameterizedQueryParser;
import org.callimachusproject.engine.model.TermFactory;
import org.callimachusproject.fluid.Fluid;
import org.callimachusproject.fluid.FluidBuilder;
import org.callimachusproject.fluid.FluidException;
import org.callimachusproject.fluid.FluidFactory;
import org.callimachusproject.repository.CalliRepository.HttpRepositoryClient;
import org.callimachusproject.xml.XdmNodeFactory;
import org.openrdf.OpenRDFException;
import org.openrdf.query.TupleQueryResult;
import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.sail.SailRepository;
import org.openrdf.repository.sparql.SPARQLRepository;
import org.openrdf.rio.RDFFormat;
import org.openrdf.sail.memory.MemoryStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;

import com.xmlcalabash.core.XProcConstants;
import com.xmlcalabash.core.XProcException;
import com.xmlcalabash.core.XProcRuntime;
import com.xmlcalabash.core.XProcStep;
import com.xmlcalabash.io.ReadablePipe;
import com.xmlcalabash.io.WritablePipe;
import com.xmlcalabash.model.RuntimeValue;
import com.xmlcalabash.runtime.XAtomicStep;
import com.xmlcalabash.util.Base64;
import com.xmlcalabash.util.S9apiUtils;

public class SparqlStep implements XProcStep {
    private static final String RESULTS_XML = "application/sparql-results+xml";
    private static final QName _content_type = new QName("content-type");
    public static final QName _encoding = new QName("", "encoding");
    private static final FluidFactory FF = FluidFactory.getInstance();
    private final Logger logger = LoggerFactory.getLogger(SparqlStep.class);
    private final FluidBuilder fb = FF.builder();
    private final Map<String, String> parameters = new LinkedHashMap<String, String>();
    private final XProcRuntime runtime;
    private final XAtomicStep step;
    private ReadablePipe sourcePipe = null;
    private ReadablePipe queryPipe = null;
    private WritablePipe resultPipe = null;
    private String endpoint;
    private String outputBase;

    public SparqlStep(XProcRuntime runtime, XAtomicStep step) {
        this.runtime = runtime;
        this.step = step;
    }

    @Override
    public void setParameter(QName name, RuntimeValue value) {
        parameters.put(name.getLocalName(), value.getString());
    }

    @Override
    public void setParameter(String port, QName name, RuntimeValue value) {
        setParameter(name, value);
    }

    @Override
    public void setOption(QName name, RuntimeValue value) {
        if ("output-base-uri".equals(name.getClarkName())) {
            outputBase = value.getString();
        } else if ("endpoint".equals(name.getClarkName())) {
            endpoint = value.getString();
        }
    }

    public void setInput(String port, ReadablePipe pipe) {
        if ("source".equals(port)) {
            sourcePipe = pipe;
        } else if ("query".equals(port)) {
            queryPipe = pipe;
        }
    }

    public void setOutput(String port, WritablePipe pipe) {
        if ("result".equals(port)) {
            resultPipe = pipe;
        } else {
            throw new XProcException("No other outputs allowed.");
        }
    }

    public void reset() {
        sourcePipe.resetReader();
        queryPipe.resetReader();
        resultPipe.resetWriter();
    }

    public void run() throws SaxonApiException {
        if (queryPipe == null || !queryPipe.moreDocuments()) {
            throw XProcException.dynamicError(6, step.getNode(), "No query provided.");
        }
        try {

            RepositoryConnection con;
            if (endpoint == null || endpoint.length() == 0) {
                con = createConnection();
            } else {
                con = createConnection(resolve(endpoint));
            }
            try {
                while (sourcePipe != null && sourcePipe.moreDocuments()) {
                    importData(sourcePipe.read(), con);
                }
                while (queryPipe.moreDocuments()) {
                    XdmNode query = queryPipe.read();
                    String queryBaseURI = resolve(query.getBaseURI().toASCIIString());
                    String queryString = getQueryString(query);
                    XdmNode factory = evaluate(queryString, queryBaseURI, con);
                    resultPipe.write(factory);
                }
            } finally {
                Repository repo = con.getRepository();
                con.close();
                if (endpoint == null || endpoint.length() == 0) {
                    repo.shutDown();
                }
            }
        } catch (SAXException e) {
            throw XProcException.dynamicError(30, step.getNode(), e, e.getMessage());
        } catch (SaxonApiException e) {
            throw XProcException.dynamicError(30, step.getNode(), e, e.getMessage());
        } catch (FluidException e) {
            throw XProcException.dynamicError(30, step.getNode(), e, e.getMessage());
        } catch (IOException e) {
            throw XProcException.dynamicError(30, step.getNode(), e, e.getMessage());
        } catch (OpenRDFException e) {
            throw XProcException.dynamicError(30, step.getNode(), e, e.getMessage());
        }
    }

    private RepositoryConnection createConnection() throws RepositoryException {
        Repository repository = new SailRepository(new MemoryStore());
        repository.initialize();
        RepositoryConnection con = repository.getConnection();
        return con;
    }

    private RepositoryConnection createConnection(String endpoint) throws OpenRDFException, IOException {
        final SPARQLRepository repository = new SPARQLRepository(endpoint);
        HttpClient client = runtime.getHttpClient();
        if (client instanceof HttpRepositoryClient) {
            HttpRepositoryClient rclient = (HttpRepositoryClient) client;
            CredentialsProvider provider = rclient.getCredentialsProvider();
            HttpHost authority = URIUtils.extractHost(URI.create(endpoint));
            AuthScope scope = new AuthScope(authority);
            if (provider != null && provider.getCredentials(scope) != null) {
                Credentials cred = provider.getCredentials(scope);
                String username = cred.getUserPrincipal().getName();
                String password = cred.getPassword();
                repository.setUsernameAndPassword(username, password);
            }
        } else {
            logger.warn("Repository credentials could not be read");
        }
        repository.initialize();
        RepositoryConnection con = repository.getConnection();
        return con;
    }

    private void importData(XdmNode document, RepositoryConnection con)
            throws SaxonApiException, OpenRDFException, IOException {
        String sysId = document.getBaseURI().toASCIIString();
        ByteArrayOutputStream s = new ByteArrayOutputStream();
        Serializer serializer = new Serializer();
        serializer.setOutputStream(s);
        S9apiUtils.serialize(runtime, document, serializer);
        con.add(new ByteArrayInputStream(s.toByteArray()), sysId, RDFFormat.RDFXML);
    }

    private String getQueryString(XdmNode document) {
        XdmNode root = S9apiUtils.getDocumentElement(document);

        if ((XProcConstants.c_data.equals(root.getNodeName())
                && "application/octet-stream".equals(root.getAttributeValue(_content_type)))
                || "base64".equals(root.getAttributeValue(_encoding))) {
            byte[] decoded = Base64.decode(root.getStringValue());
            return new String(decoded);
        }
        return root.getStringValue();
    }

    private XdmNode evaluate(String queryString, String queryBaseURI, RepositoryConnection con)
            throws OpenRDFException, IOException, FluidException, SAXException {
        ParameterizedQueryParser parser = ParameterizedQueryParser.newInstance();
        ParameterizedQuery qry = parser.parseQuery(queryString, queryBaseURI);
        XdmNodeFactory factory = new XdmNodeFactory(runtime.getProcessor(), runtime.getHttpClient());
        String baseURI = getBaseURI(queryBaseURI);
        TupleQueryResult results = qry.evaluate(parameters, con);
        try {
            Fluid f = fb.consume(results, baseURI, TupleQueryResult.class, RESULTS_XML);
            return factory.parse(baseURI, f.asStream());
        } finally {
            results.close();
        }
    }

    private String getBaseURI(String queryBaseURI) {
        if (outputBase == null || outputBase.length() == 0)
            return queryBaseURI;
        return resolve(outputBase);
    }

    private String resolve(String href) {
        String base = step.getNode().getBaseURI().toASCIIString();
        if (href == null)
            return base;
        return TermFactory.newInstance(base).resolve(href);
    }

}