Package org.fcrepo.client

Source Code of org.fcrepo.client.FedoraClient

/* The contents of this file are subject to the license and copyright terms
* detailed in the license directory at the root of the source tree (also
* available online at http://fedora-commons.org/license/).
*/

package org.fcrepo.client;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.TimeZone;

import javax.xml.rpc.ServiceException;

import org.apache.http.Header;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.util.EntityUtils;
import org.fcrepo.common.Constants;
import org.fcrepo.common.http.HttpInputStream;
import org.fcrepo.common.http.PreemptiveAuth;
import org.fcrepo.server.access.FedoraAPIA;
import org.fcrepo.server.access.FedoraAPIAMTOM;
import org.fcrepo.server.management.FedoraAPIM;
import org.fcrepo.server.management.FedoraAPIMMTOM;
import org.fcrepo.utilities.DateUtility;
import org.jrdf.graph.Literal;
import org.jrdf.graph.Node;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.trippi.RDFFormat;
import org.trippi.TrippiException;
import org.trippi.TupleIterator;

/**
* General-purpose utility class for Fedora clients. Provides methods to get
* SOAP stubs for Fedora APIs. Also serves as one-stop-shopping for issuing HTTP
* requests using Apache's HttpClient. Provides option for client to handle HTTP
* redirects (notably 302 status that occurs with SSL auto-redirects at server.)
*
* @author Chris Wilper
* @author Sandy Payette
*/
public class FedoraClient
        implements Constants {

    public static final String FEDORA_URI_PREFIX = "info:fedora/";

    /** Seconds to wait before a connection is established. */
    private static final int TIMEOUT_SECONDS = 20;

    /** Seconds to wait while waiting for data over the socket (SO_TIMEOUT). */
    private static final int SOCKET_TIMEOUT_SECONDS = 1800; // 30 minutes

    /** Maxiumum http connections per host (for REST calls only). */
    private static final int MAX_CONNECTIONS_PER_HOST = 15;

    /** Maxiumum total http connections (for REST calls only). */
    private static final int MAX_TOTAL_CONNECTIONS = 30;

    /** Whether to automatically follow HTTP redirects. */
    private static final boolean FOLLOW_REDIRECTS = true;

    private static final Logger logger = LoggerFactory
            .getLogger(FedoraClient.class);

    private final SOAPEndpoint m_accessMTOMEndpoint =
            new SOAPEndpoint("accessMTOM", false);

    private final SOAPEndpoint m_managementMTOMEndpoint =
            new SOAPEndpoint("managementMTOM", true);

    private final SOAPEndpoint m_accessEndpoint =
            new SOAPEndpoint("access", false);

    private final SOAPEndpoint m_managementEndpoint =
            new SOAPEndpoint("management", true);

    private String m_baseURL;

    private final String m_user;

    private final String m_pass;

    private final AuthScope m_authScope;

    private final UsernamePasswordCredentials m_creds;

    private PoolingClientConnectionManager m_cManager =
            null;

    private String m_serverVersion;
   
    private FedoraAPIA m_apia;
   
    private FedoraAPIM m_apim;

    private FedoraAPIAMTOM m_apiaMTOM;
   
    private FedoraAPIMMTOM m_apimMTOM;
   

    /**
     * Location of Fedora's upload interface, set on first call to
     * getUploadURL().
     */
    private String m_uploadURL;
   
    private static PoolingClientConnectionManager buildConnectionManager() {
        PoolingClientConnectionManager cManager =
                new PoolingClientConnectionManager();
        // set the maximum connections total and per host
        cManager.setDefaultMaxPerRoute(MAX_CONNECTIONS_PER_HOST);
        cManager.setMaxTotal(MAX_TOTAL_CONNECTIONS);
        return cManager;
    }

    public FedoraClient(String baseURL, String user, String pass)
            throws MalformedURLException {
        m_baseURL = baseURL;
        m_user = user;
        m_pass = pass;
        if (!baseURL.endsWith("/")) {
            m_baseURL += "/";
        }
        URL url = new URL(m_baseURL);
        m_authScope =
                new AuthScope(url.getHost(),
                              AuthScope.ANY_PORT,
                              AuthScope.ANY_REALM);
        m_creds = new UsernamePasswordCredentials(user, pass);
        getConnectionManager();
        if (url.getProtocol().equalsIgnoreCase("https")) {
            Scheme scheme = m_cManager.getSchemeRegistry().getScheme("https");
            if (scheme == null) {
                m_cManager.getSchemeRegistry().register(
                    new Scheme("https", url.getPort(), SSLSocketFactory.getSocketFactory()));
            }
        } else {
            Scheme scheme = m_cManager.getSchemeRegistry().getScheme("http");
            if (scheme == null) {
                m_cManager.getSchemeRegistry().register(
                    new Scheme("http", url.getPort(), PlainSocketFactory.getSocketFactory()));
            }
        }
        try {
            URL rUrl = new URL(getUploadURL());
            if (rUrl.getPort() != url.getPort()) {
                Scheme scheme = m_cManager.getSchemeRegistry().getScheme("https");
                if (scheme == null) {
                    m_cManager.getSchemeRegistry().register(
                        new Scheme("https", rUrl.getPort(), SSLSocketFactory.getSocketFactory()));
                }
            }
        } catch (IOException ioe) {
            logger.warn("Could not get redirect URL (testing for SSL port)");
        }
    }
   
    public void shutdown() {
        if (m_cManager != null) {
            m_cManager.shutdown();
            m_cManager = null;
        }
    }
   
    public ClientConnectionManager getConnectionManager() {
        if (m_cManager == null) {
            m_cManager = buildConnectionManager();
        }
        return m_cManager;
    }
   
    public HttpClient getHttpClient() {
        return getHttpClient(FOLLOW_REDIRECTS,true);
    }
   
    public DefaultHttpClient getHttpClient(boolean followRedirects, boolean preemptiveAuth) {
        // handle pre-emptive AuthN
        getConnectionManager();
        DefaultHttpClient client = preemptiveAuth ? new PreemptiveAuth(m_cManager) :
            new DefaultHttpClient(m_cManager);
        // set the connection timeout properties
        client.getParams().setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, TIMEOUT_SECONDS * 1000);
        client.getParams().setIntParameter(CoreConnectionPNames.SO_TIMEOUT, SOCKET_TIMEOUT_SECONDS * 1000);
        // follow redirects
        client.getParams().setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, followRedirects);
        // set the authentication credentials
        client.getCredentialsProvider().setCredentials(m_authScope, m_creds);
        return client;
    }

    /**
     * Upload the given file to Fedora's upload interface via HTTP POST.
     *
     * @return the temporary id which can then be passed to API-M requests as a
     *         URL. It will look like uploaded://123
     */
    public String uploadFile(File file) throws IOException {
        HttpPost post = null;
        try {
            // prepare the post method
            post = new HttpPost(getUploadURL());
            post.getParams().setParameter("Connection", "Keep-Alive");

            // add the file part
            MultipartEntity entity = new MultipartEntity();
            entity.addPart("file", new FileBody(file));
            post.setEntity(entity);

            // execute and get the response
            HttpResponse response = getHttpClient().execute(post);
            int responseCode = response.getStatusLine().getStatusCode();
            String body = null;
            try {
                if (response.getEntity() != null) {
                    body = EntityUtils.toString(response.getEntity());
                }
            } catch (Exception e) {
                logger.warn("Error reading response body", e);
            }
            if (body == null) {
                body = "[empty response body]";
            }
            body = body.trim();
            if (responseCode != HttpStatus.SC_CREATED) {
                throw new IOException("Upload failed: "
                        + response.getStatusLine().getReasonPhrase()
                        + ": " + replaceNewlines(body, " "));
            } else {
                return replaceNewlines(body, "");
            }
        } finally {
            if (post != null) {
                post.releaseConnection();
            }
        }
    }

    /**
     * Replace newlines with the given string.
     */
    private static String replaceNewlines(String in, String replaceWith) {
        return in.replaceAll("\r", replaceWith).replaceAll("\n", replaceWith);
    }

    /**
     * Get the URL to which API-M upload requests will be sent.
     */
    public synchronized String getUploadURL() throws IOException {
        if (m_uploadURL != null) {
            return m_uploadURL;
        } else {
            m_uploadURL = m_baseURL + "management/upload";
            if (m_uploadURL.startsWith("http:")) {
                URL redirectURL = getRedirectURL(m_uploadURL);
                if (redirectURL != null) {
                    m_uploadURL = redirectURL.toString();
                }
            }
            return m_uploadURL;
        }
    }

    /**
     * Get an HTTP resource with the response as an InputStream, given a
     * resource locator that either begins with 'info:fedora/' , 'http://', or
     * '/'. This method will follow redirects if FOLLOW_REDIRECTS is true. Note
     * that if the HTTP response has no body, the InputStream will be empty. The
     * success of a request can be checked with getResponseCode(). Usually
     * you'll want to see a 200. See
     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html for other codes.
     *
     * @param locator
     *        A URL, relative Fedora URL, or Fedora URI that we want to do an
     *        HTTP GET upon
     * @param failIfNotOK
     *        boolean value indicating if an exception should be thrown if we do
     *        NOT receive an HTTP 200 response (OK)
     * @return HttpInputStream the HTTP response
     * @throws IOException
     */
    public HttpInputStream get(String locator, boolean failIfNotOK)
            throws IOException {
        return get(locator, failIfNotOK, FOLLOW_REDIRECTS);
    }

    /**
     * Get an HTTP resource with the response as an InputStream, given a URL.
     * This method will follow redirects if FOLLOW_REDIRECTS is true. Note that
     * if the HTTP response has no body, the InputStream will be empty. The
     * success of a request can be checked with getResponseCode(). Usually
     * you'll want to see a 200. See
     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html for other codes.
     *
     * @param url
     *        A URL that we want to do an HTTP GET upon
     * @param failIfNotOK
     *        boolean value indicating if an exception should be thrown if we do
     *        NOT receive an HTTP 200 response (OK)
     * @return HttpInputStream the HTTP response
     * @throws IOException
     */
    public HttpInputStream get(URL url, boolean failIfNotOK) throws IOException {
        return get(url, failIfNotOK, FOLLOW_REDIRECTS);
    }

    /**
     * Get an HTTP resource with the response as an InputStream, given a
     * resource locator that either begins with 'info:fedora/' , 'http://', or
     * '/'. Note that if the HTTP response has no body, the InputStream will be
     * empty. The success of a request can be checked with getResponseCode().
     * Usually you'll want to see a 200. See
     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html for other codes.
     *
     * @param locator
     *        A URL, relative Fedora URL, or Fedora URI that we want to do an
     *        HTTP GET upon
     * @param failIfNotOK
     *        boolean value indicating if an exception should be thrown if we do
     *        NOT receive an HTTP 200 response (OK)
     * @param followRedirects
     *        boolean value indicating whether HTTP redirects should be handled
     *        in this method, or be passed along so that they can be handled
     *        later.
     * @return HttpInputStream the HTTP response
     * @throws IOException
     */
    public HttpInputStream get(String locator,
                               boolean failIfNotOK,
                               boolean followRedirects) throws IOException {

        // Convert the locator to a proper Fedora URL and the do a get.
        String url = getLocatorAsURL(locator);
        return get(new URL(url), failIfNotOK, followRedirects);
    }

    /**
     * Get an HTTP resource with the response as an InputStream, given a URL.
     * Note that if the HTTP response has no body, the InputStream will be
     * empty. The success of a request can be checked with getResponseCode().
     * Usually you'll want to see a 200. See
     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html for other codes.
     *
     * @param url
     *        A URL that we want to do an HTTP GET upon
     * @param failIfNotOK
     *        boolean value indicating if an exception should be thrown if we do
     *        NOT receive an HTTP 200 response (OK)
     * @param followRedirects
     *        boolean value indicating whether HTTP redirects should be handled
     *        in this method, or be passed along so that they can be handled
     *        later.
     * @return HttpInputStream the HTTP response
     * @throws IOException
     */
    public HttpInputStream get(URL url,
                               boolean failIfNotOK,
                               boolean followRedirects) throws IOException {

        String urlString = url.toString();
        // presuming that not following redirects means no
        // preemptive authN
        HttpClient client = getHttpClient(followRedirects, followRedirects);
        HttpGet getMethod = new HttpGet(urlString);
        HttpInputStream in = new HttpInputStream(client, getMethod);
        int status = in.getStatusCode();
        logger.debug("GET {} : {}", urlString, status);
        if (failIfNotOK) {
            if (status != 200) {
                if (followRedirects && 300 <= status && status <= 399) {
                    // Handle the redirect here !
                    logger.debug(
                            "FedoraClient is handling redirect for HTTP STATUS={}",
                            status);
                   
                    Header hLoc = in.getResponseHeader(HttpHeaders.LOCATION);
                    if (hLoc != null) {
                        String location = hLoc.getValue();
                        logger.debug("FedoraClient is trying redirect location: {}",
                                location);
                        // Try the redirect location, but don't try to handle another level of redirection.
                        in.close();
                        return get(location, true, false);
                    } else {
                        try {
                            throw new IOException("Request failed [" + status
                                    + " " + in.getStatusText() + "]");
                        } finally {
                            try {
                                in.close();
                            } catch (Exception e) {
                                logger.error("Can't close InputStream: "
                                        + e.getMessage());
                            }
                        }
                    }
                } else {
                    try {
                        throw new IOException("Request failed ["
                                + in.getStatusCode() + " " + in.getStatusText()
                                + "] : " + urlString);
                    } finally {
                        try {
                            in.close();
                        } catch (Exception e) {
                            logger.error("Can't close InputStream: "
                                    + e.getMessage());
                        }
                    }
                }
            }
        }
        return in;
    }

    /**
     * Get an HTTP resource with the response as a String instead of an
     * InputStream, given a resource locator that either begins with
     * 'info:fedora/' , 'http://', or '/'.
     *
     * @param locator
     *        A URL, relative Fedora URL, or Fedora URI that we want to do an
     *        HTTP GET upon
     * @param failIfNotOK
     *        boolean value indicating if an exception should be thrown if we do
     *        NOT receive an HTTP 200 response (OK)
     * @param followRedirects
     *        boolean value indicating whether HTTP redirects should be handled
     *        in this method, or be passed along so that they can be handled
     *        later.
     * @return String the HTTP response as a string
     * @throws IOException
     */
    public String getResponseAsString(String locator,
                                      boolean failIfNotOK,
                                      boolean followRedirects)
            throws IOException {

        InputStream in = get(locator, failIfNotOK, followRedirects);

        // Convert the response into a String.
        try {
            BufferedReader reader =
                    new BufferedReader(new InputStreamReader(in));
            StringBuffer buffer = new StringBuffer();
            String line = reader.readLine();
            while (line != null) {
                buffer.append(line + "\n");
                line = reader.readLine();
            }
            return buffer.toString();
        } finally {
            try {
                in.close();
            } catch (Exception e) {
                logger.error("Can't close InputStream: " + e.getMessage());
            }
        }
    }

    private String getLocatorAsURL(String locator) throws IOException {

        String url;
        if (locator.startsWith(FEDORA_URI_PREFIX)) {
            url =
                    m_baseURL + "get/"
                            + locator.substring(FEDORA_URI_PREFIX.length());
        } else if (locator.startsWith("http://")
                || locator.startsWith("https://")) {
            url = locator;
        } else if (locator.startsWith("/")) {
            // assume it's for something within this Fedora server
            while (locator.startsWith("/")) {
                locator = locator.substring(1);
            }
            url = m_baseURL + locator;
        } else {
            throw new IOException("Bad locator (must start with '"
                    + FEDORA_URI_PREFIX + "', 'http[s]://', or '/'");
        }
        return url;
    }

    /**
     * Get a new SOAP stub for API-A. If the baseURL for this
     * <code>FedoraClient</code> specifies "http", regular HTTP communication
     * will be attempted first. If the server redirects this client to use HTTPS
     * instead, the redirect will be followed and SSL will be used
     * automatically.
     */
    public FedoraAPIA getAPIA() throws ServiceException, IOException {
        if (m_apia == null) m_apia = getSOAPStub(FedoraAPIA.class);
        return m_apia;
    }

    public URL getAPIAEndpointURL() throws IOException {
        return m_accessEndpoint.getURL();
    }

    public FedoraAPIAMTOM getAPIAMTOM() throws ServiceException, IOException {
        if (m_apiaMTOM == null) m_apiaMTOM = getSOAPStub(FedoraAPIAMTOM.class);
        return m_apiaMTOM;
    }

    public URL getAPIAMTOMEndpointURL() throws IOException {
        return m_accessMTOMEndpoint.getURL();
    }

    /**
     * Get a new SOAP stub for API-M. If the baseURL for this
     * <code>FedoraClient</code> specifies "http", regular HTTP communication
     * will be attempted first. If the server redirects this client to use HTTPS
     * instead, the redirect will be followed and SSL will be used
     * automatically.
     */
    public FedoraAPIM getAPIM() throws ServiceException, IOException {
        if (m_apim == null) m_apim = getSOAPStub(FedoraAPIM.class);
        return m_apim;
    }

    public URL getAPIMEndpointURL() throws IOException {
        return m_managementEndpoint.getURL();
    }

    public FedoraAPIMMTOM getAPIMMTOM() throws ServiceException, IOException {
        if (m_apimMTOM == null) m_apimMTOM = getSOAPStub(FedoraAPIMMTOM.class);
        return m_apimMTOM;
    }

    public URL getAPIMMTOMEndpointURL() throws IOException {
        return m_managementMTOMEndpoint.getURL();
    }

    /**
     * Get the appropriate API-A/M stub, given a SOAPEndpoint.
     * @param <T>
     */
    @SuppressWarnings("unchecked")
    private <T> T getSOAPStub(Class<T> type) throws ServiceException,
            IOException {

        if (type == org.fcrepo.server.access.FedoraAPIAMTOM.class) {
            org.fcrepo.client.mtom.APIAStubFactory.SOCKET_TIMEOUT_SECONDS = SOCKET_TIMEOUT_SECONDS;

            URL url = m_accessMTOMEndpoint.getURL();

            String protocol = url.getProtocol();
            String host = url.getHost();
            int port = url.getPort();
            if (port == -1) {
                port = url.getDefaultPort();
            }

            return (T) org.fcrepo.client.mtom.APIAStubFactory
                    .getStub(protocol, host, port, m_user, m_pass);

        } else if (type == FedoraAPIMMTOM.class) {
            org.fcrepo.client.mtom.APIMStubFactory.SOCKET_TIMEOUT_SECONDS = SOCKET_TIMEOUT_SECONDS;

            URL url = m_managementMTOMEndpoint.getURL();

            String protocol = url.getProtocol();
            String host = url.getHost();
            int port = url.getPort();
            if (port == -1) {
                port = url.getDefaultPort();
            }

            return (T) org.fcrepo.client.mtom.APIMStubFactory
                    .getStub(protocol, host, port, m_user, m_pass);

        } else if (type == FedoraAPIM.class) {
            org.fcrepo.client.APIMStubFactory.SOCKET_TIMEOUT_SECONDS = SOCKET_TIMEOUT_SECONDS;

            URL url = m_managementEndpoint.getURL();

            String protocol = url.getProtocol();
            String host = url.getHost();
            int port = url.getPort();
            if (port == -1) {
                port = url.getDefaultPort();
            }

            return (T) org.fcrepo.client.APIMStubFactory
                    .getStub(protocol, host, port, m_user, m_pass);

        } else if (type == FedoraAPIA.class) {
            org.fcrepo.client.APIAStubFactory.SOCKET_TIMEOUT_SECONDS = SOCKET_TIMEOUT_SECONDS;

            URL url = m_accessEndpoint.getURL();

            String protocol = url.getProtocol();
            String host = url.getHost();
            int port = url.getPort();
            if (port == -1) {
                port = url.getDefaultPort();
            }

            return (T) org.fcrepo.client.APIAStubFactory
                    .getStub(protocol, host, port, m_user, m_pass);

        } else {
            throw new IllegalArgumentException("Unrecognized api class: "
                    + type.getName());
        }
    }

    public static String getVersion() {

        ResourceBundle bundle =
                ResourceBundle.getBundle("org.fcrepo.client.resources.Client");
        return bundle.getString("version");
    }

    public static List<String> getCompatibleServerVersions() {

        ResourceBundle bundle =
                ResourceBundle.getBundle("org.fcrepo.client.resources.Client");
        List<String> list = new ArrayList<String>();

        String versions = bundle.getString("compatibleServerVersions");
        if (versions != null && versions.trim().length() > 0) {
            String[] va = versions.trim().split(" ");
            for (String element : va) {
                list.add(element);
            }
        }
        String clientVersion = getVersion();
        if (!list.contains(clientVersion)) {
            list.add(getVersion());
        }

        return list;
    }

    /**
     * Get the version reported by the remote Fedora server.
     */
    public String getServerVersion() throws IOException {
        // only do this once -- future invocations will use the known value
        if (m_serverVersion == null) {
            // Make the APIA call for describe repository
            // and make sure that HTTP 302 status is handled.
            String desc = getResponseAsString("/describe?xml=true", true, true);
            logger.debug("describeRepository response:\n" + desc);
            String[] parts = desc.split("<repositoryVersion>");
            if (parts.length < 2) {
                throw new IOException("Could not find repositoryVersion element in content of /describe?xml=true");
            }
            int i = parts[1].indexOf("<");
            if (i == -1) {
                throw new IOException("Could not find end of repositoryVersion element in content of /describe?xml=true");
            }
            m_serverVersion = parts[1].substring(0, i).trim();
            logger.debug("Server version is " + m_serverVersion);
        }
        return m_serverVersion;
    }

    /**
     * Return the current date as reported by the Fedora server.
     *
     * @throws IOException
     *         if the HTTP Date header is not provided by the server for any
     *         reason, or it is in the wrong format.
     */
    public Date getServerDate() throws IOException {
        HttpInputStream in = get("/describe", false, false);
        String dateString = null;
        try {
            Header header = in.getResponseHeader("Date");
            if (header == null) {
                throw new IOException("Date was not supplied in HTTP response "
                        + "header for " + m_baseURL + "describe");
            }
            dateString = header.getValue();

            // This is the date format recommended by RFC2616
            SimpleDateFormat format =
                    new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z",
                                         Locale.US);
            format.setTimeZone(TimeZone.getTimeZone("UTC"));
            return format.parse(dateString);

        } catch (ParseException e) {
            throw new IOException("Unparsable date (" + dateString
                    + ") in HTTP response header for " + m_baseURL + "describe");
        } finally {
            in.close();
        }
    }

    public Date getLastModifiedDate(String locator) throws IOException {
        if (locator.startsWith(FEDORA_URI_PREFIX)) {
            String query =
                    "select $date " + "from <#ri> " + "where <" + locator
                            + "> <" + VIEW.LAST_MODIFIED_DATE.uri + "> $date";
            Map<String, String> map = new HashMap<String, String>();
            map.put("lang", "itql");
            map.put("query", query);
            TupleIterator tuples = getTuples(map);
            try {
                if (tuples.hasNext()) {
                    Map<String, Node> row = tuples.next();
                    Literal dateLiteral = (Literal) row.get("date");
                    if (dateLiteral == null) {
                        throw new IOException("A row was returned, but it did not contain a 'date' binding");
                    }
                    return DateUtility.parseDateLoose(dateLiteral
                            .getLexicalForm());
                } else {
                    throw new IOException("No rows were returned");
                }
            } catch (TrippiException e) {
                throw new IOException(e.getMessage());
            } finally {
                try {
                    tuples.close();
                } catch (Exception e) {
                }
            }
        } else {
            HttpClient client = getHttpClient();

            HttpHead head = new HttpHead(locator);

            try {
                HttpResponse response = client.execute(head);
                int statusCode = response.getStatusLine().getStatusCode();
                if (statusCode != HttpStatus.SC_OK) {
                    throw new IOException("Method failed: "
                            + response.getStatusLine().getReasonPhrase());
                }
                //Header[] headers = head.getResponseHeaders();

                // Retrieve just the last modified header value.
                Header header = response.getFirstHeader(HttpHeaders.LAST_MODIFIED);
                if (header != null) {
                    String lastModified = header.getValue();
                    return DateUtility.parseDateLoose(lastModified);
                } else {
                    // return current date time
                    return new Date();
                }
            } finally {
                head.releaseConnection();
            }
        }
    }

    public void reloadPolicies() throws IOException {

        InputStream in = null;
        try {
            in = get("/management/control?action=reloadPolicies", true, true);
        } finally {
            try {
                in.close();
            } catch (Exception e) {
                logger.error("Can't close InputStream: " + e.getMessage());
            }
        }
    }

    /**
     * Get tuples from the remote resource index. The map contains
     * <em>String</em> values for parameters that should be passed to the
     * service. Two parameters are required: 1) lang 2) query Two parameters to
     * the risearch service are implied: 1) type = tuples 2) format = sparql See
     * http
     * ://www.fedora.info/download/2.0/userdocs/server/webservices/risearch/#
     * app.tuples
     */
    public TupleIterator getTuples(Map<String, String> params)
            throws IOException {
        params.put("type", "tuples");
        params.put("format", RDFFormat.SPARQL.getName());
        try {
            String url = getRIQueryURL(params);
            return TupleIterator.fromStream(get(url, true, true),
                                            RDFFormat.SPARQL);
        } catch (TrippiException e) {
            throw new IOException("Error getting tuple iterator: "
                    + e.getMessage());
        }
    }

    private String getRIQueryURL(Map<String, String> params) throws IOException {
        if (params.get("type") == null) {
            throw new IOException("'type' parameter is required");
        }
        if (params.get("lang") == null) {
            throw new IOException("'lang' parameter is required");
        }
        if (params.get("query") == null) {
            throw new IOException("'query' parameter is required");
        }
        if (params.get("format") == null) {
            throw new IOException("'format' parameter is required");
        }
        return m_baseURL + "risearch?" + encodeParameters(params);
    }

    private String encodeParameters(Map<String, String> params) {
        StringBuffer encoded = new StringBuffer();
        Iterator<String> iter = params.keySet().iterator();
        int n = 0;
        while (iter.hasNext()) {
            String name = iter.next();
            if (n > 0) {
                encoded.append("&");
            }
            n++;
            encoded.append(name);
            encoded.append('=');
            try {
                encoded.append(URLEncoder.encode(params.get(name), "UTF-8"));
            } catch (UnsupportedEncodingException e) { // UTF-8 won't fail
            }
        }
        return encoded.toString();
    }

    /**
     * Ping the given endpoint to see if an HTTP 302 status code is returned. If
     * so, return the location given in the HTTP response header. If not, return
     * null.
     */
    private URL getRedirectURL(String location) throws IOException {
        HttpInputStream in = get(location, false, false);
        try {
            if (in.getStatusCode() == 302) {
                Header h = in.getResponseHeader("location");
                if (h != null) {
                    return new URL(h.getValue());
                }
            }
            return null;
        } finally {
            try {
                in.close();
            } catch (Exception e) {
            }
        }
    }

    /**
     * Class for storing a Fedora SOAP endpoint, which consists of an endpoint
     * name and a URL. The endpoint name is provided to the constructor. The URL
     * is determined automatically, once, based on:
     * <ul>
     * <li>The baseURL provided to the FedoraClient instance.</li>
     * <li>The server version.</li>
     * <li>Whether the server automatically redirects non-SSL SOAP requests to
     * an SSL endpoint.</li>
     * </ul>
     */
    public class SOAPEndpoint {

        String m_name;

        URL m_url;

        boolean m_apim;

        public SOAPEndpoint(String name, boolean apim) {
            m_name = name;
            m_apim = apim;
        }

        public String getName() {
            return m_name;
        }

        public URL getURL() throws IOException {
            if (m_url == null) {
                String url = getChannelBaseUrl() + "services/" + m_name;
                m_url = new URL(url);
            }
            return m_url;
        }

        public String getChannelBaseUrl() throws IOException {
            if (m_apim) {
                URL redirect = getRedirectURL(m_baseURL + "management/upload");
                if (redirect == null) return m_baseURL;
                else return redirect.toString().replace("management/upload", "");
            } else {
                URL redirect = getRedirectURL(m_baseURL + "wsdl");
                if (redirect == null) return m_baseURL;
                else return redirect.toString().replace("wsdl", "");
            }
        }

    }

}
TOP

Related Classes of org.fcrepo.client.FedoraClient

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.