de.escidoc.core.common.business.fedora.FedoraUtility.java Source code

Java tutorial

Introduction

Here is the source code for de.escidoc.core.common.business.fedora.FedoraUtility.java

Source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the Common Development and Distribution License, Version 1.0
 * only (the "License"). You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at license/ESCIDOC.LICENSE or http://www.escidoc.de/license. See the License for
 * the specific language governing permissions and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each file and include the License file at
 * license/ESCIDOC.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by
 * brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 * Copyright 2006-2011 Fachinformationszentrum Karlsruhe Gesellschaft fuer wissenschaftlich-technische Information mbH
 * and Max-Planck-Gesellschaft zur Foerderung der Wissenschaft e.V. All rights reserved. Use is subject to license
 * terms.
 */

package de.escidoc.core.common.business.fedora;

import de.escidoc.core.common.business.Constants;
import de.escidoc.core.common.exceptions.system.FedoraSystemException;
import de.escidoc.core.common.exceptions.system.FileSystemException;
import de.escidoc.core.common.exceptions.system.TripleStoreSystemException;
import de.escidoc.core.common.exceptions.system.WebserverSystemException;
import de.escidoc.core.common.util.security.PreemptiveAuthInterceptor;
import de.escidoc.core.common.util.service.BeanLocator;
import de.escidoc.core.common.util.xml.XmlUtility;
import org.apache.axis.types.NonNegativeInteger;
import org.apache.commons.pool.BasePoolableObjectFactory;
import org.apache.commons.pool.PoolUtils;
import org.apache.commons.pool.impl.StackObjectPool;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.params.ConnPerRoute;
import org.apache.http.conn.params.ConnPerRouteBean;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.fcrepo.client.FedoraClient;
import org.fcrepo.client.HttpInputStream;
import org.fcrepo.server.access.FedoraAPIA;
import org.fcrepo.server.management.FedoraAPIM;
import org.fcrepo.server.types.gen.Datastream;
import org.fcrepo.server.types.gen.MIMETypedStream;
import org.fcrepo.server.types.gen.ObjectProfile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.NotWritablePropertyException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;

import javax.servlet.http.HttpServletResponse;
import javax.xml.rpc.ServiceException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * An utility class for Fedora requests.<br />
 * This class uses pools for the fedora interfaces.
 * 
 * @author Rozita Friedman
 */
@ManagedResource(objectName = "eSciDocCore:name=FedoraUtility", description = "The utility class to access the fedora repository.", log = true, logFile = "jmx.log", currencyTimeLimit = 15)
public class FedoraUtility implements InitializingBean {

    public static final String DATASTREAM_STATUS_DELETED = "D";

    public static final int SYNC_RETRIES = 10;

    private static final Logger LOGGER = LoggerFactory.getLogger(FedoraUtility.class);

    private static final int MAX_IDLE = 5;

    private static final int INIT_IDLE_CAPACITY = 2;

    private static final String FOXML_FORMAT = "info:fedora/fedora-system:FOXML-1.1";

    private static final int HTTP_OK = 200;

    private StackObjectPool fedoraClientPool;

    // Nutzerzugriffe
    private StackObjectPool apiaPool;

    // Handler fr managed REsourcen
    private StackObjectPool apimPool;

    /**
     * Query string to trigger a sync via the fedora REST interface.
     */
    private String syncRestQuery;

    // TODO
    // in configurationsdatei auslagern--> escidoc config*

    // escidoc-core.properties // default config
    // escidoc-core.custom.properties --> fhrend
    private static final int HTTP_MAX_CONNECTIONS_PER_HOST = 30;

    private static final int HTTP_MAX_TOTAL_CONNECTIONS = 90;

    private String fedoraUser;

    private String fedoraPassword;

    private String fedoraUrl;

    private String identifierPrefix;

    private DefaultHttpClient httpClient;

    private TripleStoreUtility tripleStoreUtility;

    // The methods exposed via jmx

    /**
     * Gets the FoXML version.
     * 
     * @return The version of FOXML
     */
    @ManagedAttribute(description = "The FoXML version.")
    public String getFoxmlVersion() {

        return FOXML_FORMAT;
    }

    /**
     * Gets the URL to the Fedora repository.
     * 
     * @return Returns the URL to the Fedora repository.
     */
    @ManagedAttribute(description = "The URL to the fedora repository.")
    public String getFedoraUrl() {

        return this.fedoraUrl;
    }

    /**
     * Gets the number of active apia connections.
     * 
     * @return Returns the number of active apia connections.
     */
    @ManagedAttribute(description = "The number of active apia connections.")
    public int getApiaPoolNumActive() {

        return apiaPool.getNumActive();
    }

    /**
     * Gets the number of idle apia connections.
     * 
     * @return Returns the number of idle apia connections.
     */
    @ManagedAttribute(description = "The number of idle apia connections.")
    public int getApiaPoolNumIdle() {

        return apiaPool.getNumIdle();
    }

    /**
     * Clears the pool of apia connections.
     */
    @ManagedOperation(description = "Clear the pool of apia connections.")
    public void clearApiaPool() {

        apiaPool.clear();
    }

    /**
     * Gets the number of active apim connections.
     * 
     * @return Returns the number of active apim connections.
     */
    @ManagedAttribute(description = "The number of active apim connections.")
    public int getApimPoolNumActive() {

        return apimPool.getNumActive();
    }

    /**
     * Gets the number of idle apim connections.
     * 
     * @return Returns the number of idle apim connections.
     */
    @ManagedAttribute(description = "The number of idle apim connections.")
    public int getApimPoolNumIdle() {

        return apimPool.getNumIdle();
    }

    /**
     * Clears the pool of apim connections.
     */
    @ManagedOperation(description = "Clear the pool of apim connections.")
    void clearApimPool() {

        apimPool.clear();
    }

    /**
     * Gets the number of active fedora clients.
     * 
     * @return Returns the number of active fedora clients.
     */
    @ManagedAttribute(description = "The number of active fedora clients.")
    public int getFedoraClientPoolNumActive() {

        return fedoraClientPool.getNumActive();
    }

    /**
     * Gets the number of idle fedora clients.
     * 
     * @return Returns the number of idle fedora clients.
     */
    @ManagedAttribute(description = "The number of idle fedora clients.")
    public int getFedoraClientPoolNumIdle() {

        return fedoraClientPool.getNumIdle();
    }

    /**
     * Clears the pool of fedora clients.
     */
    @ManagedOperation(description = "Clear the pool of fedora clients.")
    public void clearFedoraClientPool() {

        fedoraClientPool.clear();
    }

    /**
     * The method returns the foxml of the Fedora object with provided id via Fedora APIM-Webservice export().
     * 
     * @param pid
     *            Fedora object pid.
     * @return content of the fedora object foxml as byte []
     * @throws FedoraSystemException
     *             Thrown if retrieving FOXML of object failed.
     */
    public byte[] getObjectFoxml(final String pid) throws FedoraSystemException {

        final FedoraAPIM apim = borrowApim();
        try {
            return apim.export(pid, FOXML_FORMAT, "public");
        } catch (final RemoteException e) {
            throw new FedoraSystemException("APIM export failure: " + e.getMessage(), e);
        } finally {
            returnApim(apim);
        }
    }

    /**
     * Get next object IDs from Fedora (Fedora use the name PID for there identifier).
     * 
     * @param noOfPids
     *            Number of IDs which are to request.
     * @return Array with object IDs.
     * @throws FedoraSystemException
     *             Thrown if collection values from Fedora failed.
     */
    public String[] getNextPID(final int noOfPids) throws FedoraSystemException {

        String[] pids = null;
        final NonNegativeInteger number = new NonNegativeInteger(String.valueOf(noOfPids));

        final FedoraAPIM apim = borrowApim();
        try {
            pids = apim.getNextPID(number, this.identifierPrefix);
        } catch (final RemoteException e) {
            throw new FedoraSystemException("Unable to get Obids from Fedora: " + e.getMessage(), e);
        } finally {
            returnApim(apim);
        }
        return pids;
    }

    /**
     * The method sets the data stream status to a provided value.
     * 
     * @param pid
     *            The Fedora object id.
     * @param dsName
     *            The name of the datastream
     * @param dsState
     *            The status of the datastream.
     * @return Timestamp of the datastream.
     * @throws FedoraSystemException
     *             Thrown if set datastream at Fedora failed.
     */
    public String setDatastreamState(final String pid, final String dsName, final String dsState)
            throws FedoraSystemException {

        String timestamp = null;
        final FedoraAPIM apim = borrowApim();
        try {
            timestamp = apim.setDatastreamState(pid, dsName, dsState, "ds state is changed.");
        } catch (final RemoteException e) {
            throw new FedoraSystemException("APIM setDatastreamState failure: " + e.getMessage(), e);
        } finally {
            returnApim(apim);
        }
        return timestamp;
    }

    /**
     * The method fetches the {@link MIMETypedStream} of the datastream with provided data stream id from Fedora-object
     * with provided pid via Fedora APIA-Webservice getDatastreamDissemination().
     * 
     * @param dataStreamId
     *            The id of the datastream.
     * @param pid
     *            The Fedora object id.
     * @param timestamp
     *            Timestamp related to datastream version to retrieve. May be null.
     * @return Returns the {@link MIMETypedStream} representing the addressed datastream.
     * @throws FedoraSystemException
     *             Thrown in case of Fedora exceptions.
     */
    public MIMETypedStream getDatastreamWithMimeType(final String dataStreamId, final String pid,
            final String timestamp) throws FedoraSystemException {

        FedoraAPIA apia = borrowApia();
        try {
            return apia.getDatastreamDissemination(pid, dataStreamId, timestamp);
        } catch (final RemoteException e) {
            // Workaround
            LOGGER.warn("APIA getDatastreamWithMimeType(..) " + e);
            invalidateApiaObject(apia);
            clearApimPool();
            apia = borrowApia();
            try {
                return apia.getDatastreamDissemination(pid, dataStreamId, timestamp);
            } catch (final RemoteException e1) {
                final String message = "Error on retrieve datastream (pid='" + pid + "', dataStreamId='"
                        + dataStreamId + "', timestamp='" + timestamp + "') ";
                LOGGER.warn(message);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug(message, e1);
                }
                throw new FedoraSystemException(message, e);
            }
        } finally {
            returnApia(apia);
        }
    }

    /**
     * The method modifies the named xml datastream of the Object with the given Pid. New Datastream-content is the
     * given byte[] datastream
     * 
     * @param pid
     *            id of the Object
     * @param datastreamName
     *            datastreamName
     * @param datastreamLabel
     *            datastreamLabel
     * @param datastream
     *            datastream
     * @param syncTripleStore
     *            whether the triples should be flushed
     * @return The timestamp of the modified datastream.
     * @throws FedoraSystemException
     *             Thrown if Fedora access failed.
     * @throws WebserverSystemException
     *             Thrown in case of an internal error.
     */
    public String modifyDatastream(final String pid, final String datastreamName, final String datastreamLabel,
            final byte[] datastream, final boolean syncTripleStore)
            throws FedoraSystemException, WebserverSystemException {

        String timestamp = null;
        final FedoraAPIM apim = borrowApim();
        try {
            timestamp = apim.modifyDatastreamByValue(pid, datastreamName, new String[0], datastreamLabel,
                    de.escidoc.core.common.business.fedora.datastream.Datastream.MIME_TYPE_TEXT_XML, null,
                    datastream, null, null, null, true);
        } catch (final Exception e) {
            preventWrongLogging(e);
            throw new FedoraSystemException(e.toString(), e);
        } finally {
            returnApim(apim);
            if (syncTripleStore) {
                sync();
            }
        }
        return timestamp;
    }

    /**
     * The method modifies the named datastream of the Object with the given Pid. New Datastream-content is the given
     * byte[] datastream
     * 
     * @param pid
     *            id of the Object
     * @param datastreamName
     *            datastreamName
     * @param datastreamLabel
     *            datastreamLabel
     * @param mimeType
     *            mimeType
     * @param altIDs
     *            Vector with alternate Ids.
     * @param url
     *            url
     * @param syncTripleStore
     *            whether the triples should be flushed
     * @return The timestamp of the modified datastream.
     * @throws FedoraSystemException
     *             Thrown if request to Fedora failed.
     * @throws WebserverSystemException
     *             Thrown in case of an internal error.
     */
    public String modifyDatastream(final String pid, final String datastreamName, final String datastreamLabel,
            final String mimeType, final String[] altIDs, final String url, final boolean syncTripleStore)
            throws FedoraSystemException, WebserverSystemException {

        String timestamp = null;
        final FedoraAPIM apim = borrowApim();
        try {
            timestamp = apim.modifyDatastreamByReference(pid, datastreamName, altIDs, datastreamLabel, mimeType,
                    null, url, null, null, "Modified by reference.", true);
        } catch (final Exception e) {
            throw new FedoraSystemException("Failed to modify Fedora datastream by reference: " + url, e);
        } finally {
            returnApim(apim);
            if (syncTripleStore) {
                sync();
            }
        }
        return timestamp;
    }

    /**
     * The method modifies the named xml datastream of the Object with the given Pid. New Datastream-content is the
     * given byte[] datastream
     * 
     * @param pid
     *            id of the Object
     * @param datastreamName
     *            datastreamName
     * @param datastreamLabel
     *            datastreamLabel
     * @param mimeType
     *            The MIME Type of the datastream.
     * @param alternateIDs
     *            String array of alternateIDs of the datastream
     * @param datastream
     *            datastream
     * @param syncTripleStore
     *            whether the triples should be flushed
     * @return The new timestamp of the modified datastream.
     * @throws FedoraSystemException
     *             Thrown if request to Fedora failed.
     * @throws WebserverSystemException
     *             Thrown in case of an internal error.
     */
    public String modifyDatastream(final String pid, final String datastreamName, final String datastreamLabel,
            final String mimeType, final String[] alternateIDs, final byte[] datastream,
            final boolean syncTripleStore) throws FedoraSystemException, WebserverSystemException {

        String timestamp = null;
        final FedoraAPIM apim = borrowApim();
        try {
            timestamp = apim.modifyDatastreamByValue(pid, datastreamName, alternateIDs, datastreamLabel, mimeType,
                    null, datastream, null, null, null, true);
        } catch (final Exception e) {
            throw new FedoraSystemException("Failed to modify Fedora datastream.", e);
        } finally {
            returnApim(apim);
            if (syncTripleStore) {
                sync();
            }
        }
        return timestamp;
    }

    /**
     * Purge all versions of datastreams between timestamps.
     * 
     * @param pid
     *            Fedora pid
     * @param datastreamName
     *            Fedora datastream name
     * @param startDT
     *            Start timestamp. Use null for newest.
     * @param endDT
     *            End timestamp. Use null for oldest.
     * @throws FedoraSystemException
     *             Thrown if purging failed by Fedora
     */
    public void purgeDatastream(final String pid, final String datastreamName, final String startDT,
            final String endDT) throws FedoraSystemException {

        final FedoraAPIM apim = borrowApim();
        try {
            apim.purgeDatastream(pid, datastreamName, startDT, endDT, "datastream purged", false);
        } catch (final Exception e) {
            throw new FedoraSystemException("Failed to purge Fedora datastream.", e);
        } finally {
            returnApim(apim);
        }
    }

    /**
     * The method modifies the named xml datastream of the Object with the given Pid. New Datastream-content is the
     * given byte[] datastream
     * 
     * @param pid
     *            id of the Object
     * @param datastreamName
     *            datastreamName
     * @param datastreamLabel
     *            datastreamLabel
     * @param alternateIDs
     *            String array of alternateIDs of the datastream
     * @param datastream
     *            datastream
     * @param syncTripleStore
     *            whether the triples should be flushed
     * @return The timestamp of the modified datastream.
     * @throws FedoraSystemException
     *             Thrown if request to Fedora failed.
     * @throws WebserverSystemException
     *             Thrown in case of an internal error.
     */
    public String modifyDatastream(final String pid, final String datastreamName, final String datastreamLabel,
            final String[] alternateIDs, final byte[] datastream, final boolean syncTripleStore)
            throws FedoraSystemException, WebserverSystemException {

        return modifyDatastream(pid, datastreamName, datastreamLabel,
                de.escidoc.core.common.business.fedora.datastream.Datastream.MIME_TYPE_TEXT_XML, alternateIDs,
                datastream, syncTripleStore);
    }

    /**
     * Get the names of data streams selected by alternateId. Only the first value of the altIds is compared.
     * 
     * @param pid
     *            The id of the Fedora object.
     * @param altId
     *            The alternate ID filter.
     * @return Vector of data stream names where the first altId is equal.
     * @throws FedoraSystemException
     *             Thrown if instantiation of Fedora connection fail.
     */
    public Collection<String> getDatastreamNamesByAltId(final String pid, final String altId)
            throws FedoraSystemException {
        final Collection<String> names = new ArrayList<String>();

        final Datastream[] ds = getDatastreamsInformation(pid);

        for (final Datastream d : ds) {
            final String[] altIDs = d.getAltIDs();
            if (altIDs.length > 0 && altIDs[0].equals(altId)) {
                names.add(d.getID());
            }
        }
        return names;
    }

    /**
     * Store an object.
     * 
     * @param foxml
     *            The foxml representation of the object.
     * @param syncTripleStore
     *            whether the triples should be flushed
     * @return The fedora pid of the stored object.
     * @throws WebserverSystemException
     *             Thrown if synchronization of Fedora fails.
     * @throws FedoraSystemException
     *             Thrown in case of a Fedora error.
     */
    public String storeObjectInFedora(final String foxml, final boolean syncTripleStore)
            throws FedoraSystemException, WebserverSystemException {

        String pid;

        FedoraAPIM apim = borrowApim();
        try {
            pid = apim.ingest(foxml.getBytes(XmlUtility.CHARACTER_ENCODING), FOXML_FORMAT,
                    "eSciDoc object created");
        } catch (final Exception e) {

            // Workaround - try a secound connection
            if (e.getMessage().contains("Address already in use: connect")) {
                LOGGER.warn("APIM storeObjectInFedora(..) " + e);
                invalidateApimObject(apim);
                apim = borrowApim();

                try {
                    pid = apim.ingest(foxml.getBytes(XmlUtility.CHARACTER_ENCODING), FOXML_FORMAT,
                            "eSciDoc object created");
                } catch (final Exception e1) {
                    preventWrongLogging(e1);
                    throw new FedoraSystemException("Ingest to Fedora failed. ", e);
                }
            }

            preventWrongLogging(e);
            throw new FedoraSystemException("Ingest to Fedora failed. ", e);
        } finally {
            returnApim(apim);
            if (syncTripleStore) {
                sync();
            }
        }
        return pid;
    }

    /**
     * The method retrieves metadata for all datastreams of the fedora object with provided id as Array.
     * 
     * @param pid
     *            provided id
     * @return Fedora information set about all datastreams of the requested object.
     * @throws FedoraSystemException
     *             Thrown if request to Fedora failed.
     */
    Datastream[] getDatastreamsInformation(final String pid) throws FedoraSystemException {
        return getDatastreamsInformation(pid, null);
    }

    /**
     * The method retrieves metadata for all datastreams of the fedora object with provided id as Array.
     * 
     * @param pid
     *            provided id
     * @param timestamp
     *            Timestamp related to datastream version to retrieve. May be null.
     * @return metadata of datastreams.
     * @throws FedoraSystemException
     *             Thrown if Fedora request failed.
     */
    public Datastream[] getDatastreamsInformation(final String pid, final String timestamp)
            throws FedoraSystemException {
        Datastream[] datastreams = null;
        FedoraAPIM apim = borrowApim();
        try {
            // work around to prevent null returns
            datastreams = apim.getDatastreams(pid, timestamp, null);
            if (datastreams == null) {
                LOGGER.warn("APIM getDatastreams(" + pid + ", ..) returns null.");
                returnApim(apim);
                apim = borrowApim();
                datastreams = apim.getDatastreams(pid, timestamp, null);
            }
        } catch (final RemoteException e) {
            if (LOGGER.isWarnEnabled()) {
                LOGGER.warn("Error on APIM getDatastreams(..)");
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Error on APIM getDatastreams(..)", e);
            }
            invalidateApimObject(apim);
            clearApimPool();
            apim = borrowApim();
            try {
                datastreams = apim.getDatastreams(pid, timestamp, null);
            } catch (final RemoteException e1) {
                final String message = "Error on retrieve datastream (pid='" + pid + "', timestamp='" + timestamp
                        + "') ";
                LOGGER.warn(message);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug(message, e1);
                }
                throw new FedoraSystemException(message, e);
            }
        } finally {
            returnApim(apim);
        }
        return datastreams;
    }

    /**
     * The method retrieves metadata for the datastream with a provided name of the fedora object with provided id as
     * Array.
     * 
     * @param pid
     *            provided object id
     * @param name
     *            provided data stream name
     * @param timestamp
     *            Timestamp related to datastream version to retrieve. May be null.
     * @return datastream information
     * @throws FedoraSystemException
     *             Thrown if request to Fedora failed.
     */
    public Datastream getDatastreamInformation(final String pid, final String name, final String timestamp)
            throws FedoraSystemException {

        final FedoraAPIM apim = borrowApim();
        try {
            return apim.getDatastream(pid, name, timestamp);
        } catch (final Exception e) {
            throw new FedoraSystemException(e.toString(), e);
        } finally {
            returnApim(apim);
        }
    }

    public byte[] getDissemination(final String pid, final String contentModelPid, final String name)
            throws FedoraSystemException {

        // TODO check if APIA.listMethods is sufficient for retrieving dynamic
        // resource names.
        final FedoraAPIA apia = borrowApia();
        try {
            return apia.getDissemination(pid,
                    "sdef:" + contentModelPid.replace(":", Constants.COLON_REPLACEMENT_PID) + '-' + name, name,
                    null, null).getStream();
        } catch (final Exception e) {
            throw new FedoraSystemException("Failed to get result of dynamic resource.", e);
        } finally {
            returnApia(apia);
        }
    }

    /**
     * The method retrieves history of a datastream.
     * 
     * @param pid
     *            Fedora object id
     * @param dsID
     *            ID of the datastream
     * @return history of datastream.
     * @throws FedoraSystemException
     *             Thrown if Fedora request failed.
     */
    public Datastream[] getDatastreamHistory(final String pid, final String dsID) throws FedoraSystemException {
        Datastream[] datastreams = null;
        final FedoraAPIM apim = borrowApim();
        try {
            datastreams = apim.getDatastreamHistory(pid, dsID);
        } catch (final RemoteException e) {
            throw new FedoraSystemException(e.toString(), e);
        } finally {
            returnApim(apim);
        }
        return datastreams;
    }

    public String addDatastream(final String pid, final String name, final String[] altIDs, final String label,
            final boolean versionable, final byte[] stream, final String controlGroup,
            final boolean syncTripleStore) throws FedoraSystemException, WebserverSystemException {
        final String tempURI;
        try {
            tempURI = Utility.getInstance().upload(stream, pid + name,
                    de.escidoc.core.common.business.fedora.datastream.Datastream.MIME_TYPE_TEXT_XML);
        } catch (final FileSystemException e) {
            throw new WebserverSystemException("Error while uploading of content of datastream '" + name
                    + "' of the fedora object with id '" + pid + "' to the staging area. ", e);
        }
        final String datastreamID = addDatastream(pid, name, altIDs, label, versionable,
                de.escidoc.core.common.business.fedora.datastream.Datastream.MIME_TYPE_TEXT_XML, null, tempURI,
                controlGroup, "A", "created");

        if (syncTripleStore) {
            sync();
        }
        return datastreamID;
    }

    /**
     * Add datastream to Fedora object.
     * 
     * @param pid
     *            Fedora object id.
     * @param name
     *            Datastream ID
     * @param altIDs
     *            Alt IDs
     * @param label
     *            Label
     * @param versionable
     *            Set true if Fedora has to keep old version. Set false if update overrides existing versions.
     * @param stream
     *            byte[] information dataset
     * @param syncTripleStore
     *            whether the triples should be flushed
     * @return Fedora Identifier of added Datastream.
     * @throws FedoraSystemException
     *             Thrown if request to Fedora failed.
     * @throws WebserverSystemException
     *             Thrown in case of an internal error.
     */
    public String addDatastream(final String pid, final String name, final String[] altIDs, final String label,
            final boolean versionable, final byte[] stream, final boolean syncTripleStore)
            throws FedoraSystemException, WebserverSystemException {

        final String tempURI;
        try {
            tempURI = Utility.getInstance().upload(stream, pid + name,
                    de.escidoc.core.common.business.fedora.datastream.Datastream.MIME_TYPE_TEXT_XML);
        } catch (final FileSystemException e) {
            throw new WebserverSystemException("Error while uploading of content of datastream '" + name
                    + "' of the fedora object with id '" + pid + "' to the staging area. ", e);
        }
        final String datastreamID = addDatastream(pid, name, altIDs, label, versionable,
                de.escidoc.core.common.business.fedora.datastream.Datastream.MIME_TYPE_TEXT_XML, null, tempURI, "X",
                "A", "created");

        if (syncTripleStore) {
            sync();
        }
        return datastreamID;
    }

    /**
     * Add datastream to Fedora object. Datastream is versionated by default.
     * 
     * @param pid
     *            Fedora object id.
     * @param name
     *            Datastream ID
     * @param altIDs
     *            Alt IDs
     * @param label
     *            Label
     * @param stream
     *            byte[] information dataset
     * @param syncTripleStore
     * @return Fedora Identifier of added Datastream.
     * @throws FedoraSystemException
     *             Thrown if add of datastream failed during Fedora communication.
     * @throws WebserverSystemException
     *             Thrown in case of an internal error.
     */
    @Deprecated
    // use addDatastream method where versionable is set explicitly
    public String addDatastream(final String pid, final String name, final String[] altIDs, final String label,
            final byte[] stream, final boolean syncTripleStore)
            throws FedoraSystemException, WebserverSystemException {

        return addDatastream(pid, name, altIDs, label, true, stream, syncTripleStore);
    }

    /**
     * Add datastream to Fedora object.
     * 
     * @param pid
     *            Fedora object id.
     * @param name
     *            Datastream ID
     * @param altIDs
     *            Alt IDs
     * @param label
     *            Label
     * @param url
     *            URL of binary data.
     * @param mimeType
     *            MIME Type
     * @param controlGroup
     *            Defines the datastream storage type. (See Fedora ControlGroups)
     * @param syncTripleStore
     *            whether the triples should be flushed
     * @return Fedora Identifier of added Datastream.
     * @throws FedoraSystemException
     *             Thrown if adding datastream to Fedora failed.
     * @throws WebserverSystemException
     *             Thrown if syncing TripleStore failed.
     */
    public String addDatastream(final String pid, final String name, final String[] altIDs, final String label,
            final String url, final String mimeType, final String controlGroup, final boolean syncTripleStore)
            throws FedoraSystemException, WebserverSystemException {

        final String datastreamID = addDatastream(pid, name, altIDs, label, true, mimeType, null, url, controlGroup,
                "A", "created");

        if (syncTripleStore) {
            sync();
        }
        return datastreamID;
    }

    /**
     * Add datastream to the object.
     * 
     * @param pid
     *            The Fedora object Id or PID.
     * @param dsID
     *            The id of the datastream.
     * @param altIDs
     *            The alternate IDs.
     * @param dsLabel
     *            The label of the datastream.
     * @param versionable
     *            Set true if datastream is verionable. False if not.
     * @param mimeType
     *            The MIME type of the datastream.
     * @param formatURI
     *            TODO
     * @param dsLocation
     *            TODO
     * @param controlGroup
     *            The Fedora Control Group Type.
     * @param dsState
     *            TODO
     * @param logMessage
     *            The Log Message.
     * @return Id of datastream.
     * @throws FedoraSystemException
     *             Thrown in case of Fedora exceptions.
     */
    private String addDatastream(final String pid, final String dsID, final String[] altIDs, final String dsLabel,
            final boolean versionable, final String mimeType, final String formatURI, final String dsLocation,
            final String controlGroup, final String dsState, final String logMessage) throws FedoraSystemException {

        final FedoraAPIM apim = borrowApim();
        try {
            return apim.addDatastream(pid, dsID, altIDs, dsLabel, versionable, mimeType, formatURI, dsLocation,
                    controlGroup, dsState, null, null, logMessage);
        } catch (final Exception e) {
            throw new FedoraSystemException(e.toString(), e);
        } finally {
            returnApim(apim);
        }
    }

    /**
     * Touch the object in Fedora. The object is only modified to new version with 'touched' comment. No further update
     * is done. As last process is the TripleStore synced.
     * 
     * @param pid
     *            Fedora object id.
     * @param syncTripleStore
     *            Set true if the TripleStore is to sync after object modifiing. Set false otherwise.
     * @return modified timestamp of the Fedora Objects
     * @throws FedoraSystemException
     *             Thrown if modifiyObject in Fedora fails.
     * @throws WebserverSystemException
     *             Thrown if sync TripleStore failed.
     */
    public String touchObject(final String pid, final boolean syncTripleStore)
            throws FedoraSystemException, WebserverSystemException {

        String timestamp = null;
        final FedoraAPIM apim = borrowApim();
        try {
            timestamp = apim.modifyObject(pid, null, null, null, "touched");
        } catch (final RemoteException e) {
            throw new FedoraSystemException(e.toString(), e);
        } finally {
            returnApim(apim);
            if (syncTripleStore) {
                sync();
            }
        }
        return timestamp;
    }

    /**
     * Delete the Object with the given pid.
     * 
     * @param pid
     *            id of the Object
     * @param syncTripleStore
     *            whether the triples should be flushed
     * @throws FedoraSystemException
     *             Thrown if Fedora request failed.
     * @throws WebserverSystemException
     *             Thrown in case of an internal error.
     */
    public void deleteObject(final String pid, final boolean syncTripleStore)
            throws FedoraSystemException, WebserverSystemException {
        final String msg = "Deleted object " + pid + '.';

        final FedoraAPIM apim = borrowApim();
        try {
            apim.purgeObject(pid, msg, false);
        } catch (final RemoteException e) {
            throw new FedoraSystemException("While deleting: ", e);
        } finally {
            returnApim(apim);
            if (syncTripleStore) {
                sync();
            }
        }
    }

    /**
     * Send a risearch request to fedora repository with flag flush set to true.
     * Call reinialize() in order to reset a Table Manager for the Triple Store.
     * 
     * @throws FedoraSystemException
     *             Thrown if TripleStore synchronization failed.
     * @throws WebserverSystemException
     *             Thrown if TripleStore initialization failed.
     */
    public void sync() throws FedoraSystemException, WebserverSystemException {
        /*
         * TODO The call to Fedora sync is handled multiple time to get a successful result. A single request should
         * help, but the return value of the sync method is not always HTTP.200.
         */
        int i = 0;
        while (i < SYNC_RETRIES) {
            try {
                callSync();
                break;
            } catch (final IOException e) {
                logExcetionAndWait(e, i);
            } catch (final TripleStoreSystemException e) {
                logExcetionAndWait(e, i);
            }
            i++;
            if (i >= SYNC_RETRIES) {
                throw new FedoraSystemException("TripleStore sync failed.");
            }
        }
    }

    private static void logExcetionAndWait(final Exception e, final int i) throws FedoraSystemException {
        if (LOGGER.isWarnEnabled()) {
            LOGGER.warn("Error syncing with TripleStore.");
        }
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Error syncing with TripleStore.", e);
        }
        try {
            Thread.sleep((long) (i + 1000));
        } catch (final InterruptedException e1) {
            if (LOGGER.isWarnEnabled()) {
                LOGGER.warn("Error on waiting for Fedora.");
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Error on waiting for Fedora.", e);
            }
        }
    }

    /**
     * Send a risearch request to fedora repository with flag flush set to true. Call reinialize() in order to reset a
     * Table Manager for the Triple Store.
     * 
     * @throws TripleStoreSystemException
     *             Thrown resetting of a Table Manager failed.
     * @throws FedoraSystemException
     *             Thrown if TripleStore synchronization failed.
     * @throws WebserverSystemException
     *             Thrown if TripleStore initialization failed.
     * @throws IOException
     *             Thrown from FedoraClient
     */
    private void callSync()
            throws FedoraSystemException, IOException, TripleStoreSystemException, WebserverSystemException {

        FedoraClient fc = null;
        try {
            fc = borrowFedoraClient();
            final HttpInputStream httpInStr = fc.get(this.syncRestQuery, true);
            if (httpInStr.getStatusCode() != HTTP_OK) {
                throw new FedoraSystemException("Triplestore sync failed.");
            }
            tripleStoreUtility.reinitialize();
        } finally {
            returnFedoraClient(fc);
        }
    }

    /**
     * Get the last-modification-date for the Fedora object.
     * 
     * @param pid
     *            Fedora objectId.
     * @return last-modification-date
     * @throws FedoraSystemException
     *             Thrown if connection to and retrieving data from Fedora fails.
     */
    public String getLastModificationDate(final String pid) throws FedoraSystemException {

        ObjectProfile op;
        FedoraAPIA apia = borrowApia();

        try {
            op = apia.getObjectProfile(pid, null);
        } catch (final RemoteException e) {
            // Workaround
            invalidateApiaObject(apia);
            apia = borrowApia();

            try {
                op = apia.getObjectProfile(pid, null);
            } catch (final RemoteException e1) {
                final String message = "Error on retrieve object profile (pid='" + pid + "') ";
                LOGGER.warn(message);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug(message, e1);
                }
                throw new FedoraSystemException(message, e);
            }

            throw new FedoraSystemException(e);
        } finally {
            returnApia(apia);
        }

        return op.getObjLastModDate();
    }

    /**
     * Send a GET request to Fedora.
     * 
     * @param queryString
     *            query string
     * @return response from Fedora as input stream
     * @throws FedoraSystemException
     *             Thrown in case of an internal error.
     * @throws IOException
     *             Thrown if the GET request to Fedora failed.
     */
    public HttpInputStream query(final String queryString) throws FedoraSystemException, IOException {

        HttpInputStream result = null;
        FedoraClient fc = null;

        try {
            fc = borrowFedoraClient();
            result = fc.get(queryString, true);
            if (result.getStatusCode() != HTTP_OK) {
                throw new FedoraSystemException(
                        "GET request to Fedora failed with error " + result.getStatusCode());
            }
        } finally {
            returnFedoraClient(fc);
        }
        return result;
    }

    /**
     * Borrows a {@link FedoraClient} from the pool.
     * 
     * @return Returns a {@link FedoraClient}.
     * @throws FedoraSystemException
     *             Thrown in case of an internal error.
     */
    private FedoraClient borrowFedoraClient() throws FedoraSystemException {
        try {
            return (FedoraClient) fedoraClientPool.borrowObject();
        } catch (final Exception e) {
            throw convertPoolException(e);
        }
    }

    /**
     * Returns a {@link FedoraClient} to the pool.
     * 
     * @param fedoraClient
     *            The {@link FedoraClient} to be returned to the pool.
     * @throws FedoraSystemException
     *             Thrown in case of an internal error.
     */
    private void returnFedoraClient(final FedoraClient fedoraClient) throws FedoraSystemException {
        try {
            fedoraClientPool.returnObject(fedoraClient);
        } catch (final Exception e) {
            throw convertPoolException(e);
        }
    }

    /**
     * Borrows a {@link FedoraAPIA} from the pool.
     * 
     * @return Returns a {@link FedoraAPIA}.
     * @throws FedoraSystemException
     *             Thrown in case of an internal error.
     */
    private FedoraAPIA borrowApia() throws FedoraSystemException {
        try {
            return (FedoraAPIA) apiaPool.borrowObject();
        } catch (final Exception e) {
            throw convertPoolException(e);
        }
    }

    /**
     * Returns a {@link FedoraAPIA} to the pool.
     * 
     * @param fedoraApia
     *            The {@link FedoraAPIA} to be returned to the pool.
     * @throws FedoraSystemException
     *             Thrown in case of an internal error.
     */
    private void returnApia(final FedoraAPIA fedoraApia) throws FedoraSystemException {
        try {
            apiaPool.returnObject(fedoraApia);
        } catch (final Exception e) {
            throw convertPoolException(e);
        }
    }

    /**
     * Borrows a {@link FedoraAPIM} from the pool.
     * 
     * @return Returns a {@link FedoraAPIM}.
     * @throws FedoraSystemException
     *             Thrown in case of an internal error.
     */
    private FedoraAPIM borrowApim() throws FedoraSystemException {
        try {
            return (FedoraAPIM) apimPool.borrowObject();
        } catch (final Exception e) {
            throw convertPoolException(e);
        }
    }

    /**
     * Returns a {@link FedoraAPIM} to the pool.
     * 
     * @param fedoraApim
     *            The {@link FedoraAPIM} to be returned to the pool.
     * @throws FedoraSystemException
     *             Thrown in case of an internal error.
     */
    private void returnApim(final FedoraAPIM fedoraApim) throws FedoraSystemException {
        try {
            apimPool.returnObject(fedoraApim);
        } catch (final Exception e) {
            throw convertPoolException(e);
        }
    }

    /**
     * Invalidate a {@link FedoraAPIA} to the pool.
     * 
     * @param fedoraApia
     *            The {@link FedoraAPIA} to be returned to the pool.
     * @throws FedoraSystemException
     *             Thrown in case of an internal error.
     */
    private void invalidateApiaObject(final FedoraAPIA fedoraApia) throws FedoraSystemException {

        try {
            apiaPool.invalidateObject(fedoraApia);
        } catch (final Exception e) {
            throw convertPoolException(e);
        }
    }

    /**
     * Invalidate a {@link FedoraAPIM} to the pool.
     * 
     * @param fedoraApim
     *            The {@link FedoraAPIM} to be returned to the pool.
     * @throws FedoraSystemException
     *             Thrown in case of an internal error.
     */
    private void invalidateApimObject(final FedoraAPIM fedoraApim) throws FedoraSystemException {
        try {
            apimPool.invalidateObject(fedoraApim);
        } catch (final Exception e) {
            throw convertPoolException(e);
        }
    }

    /**
     * Converts an exception thrown during a pool operation to a {@link FedoraSystemException}.
     * 
     * @param e
     *            The {@link Exception} to be converted.
     * @return Returns the {@link FedoraSystemException}
     */
    private static FedoraSystemException convertPoolException(final Exception e) {
        return e instanceof FedoraSystemException ? (FedoraSystemException) e
                : new FedoraSystemException(e.getMessage(), e);
    }

    /**
     * See Interface for functional description.
     * 
     * @see InitializingBean #afterPropertiesSet()
     */
    @Override
    public void afterPropertiesSet() throws IOException, MalformedURLException, ServiceException {

        this.fedoraClientPool = new StackObjectPool(
                PoolUtils.synchronizedPoolableFactory(new BasePoolableObjectFactory() {
                    /**
                     * See Interface for functional description.
                     * 
                     * @return
                     * @see BasePoolableObjectFactory #makeObject()
                     */
                    @Override
                    public Object makeObject() throws MalformedURLException {
                        return new FedoraClient(FedoraUtility.this.fedoraUrl, FedoraUtility.this.fedoraUser,
                                FedoraUtility.this.fedoraPassword);
                    }
                }), MAX_IDLE, INIT_IDLE_CAPACITY);

        this.apiaPool = new StackObjectPool(PoolUtils.synchronizedPoolableFactory(new BasePoolableObjectFactory() {
            /**
             * See Interface for functional description.
             * 
             * @return
             * @see BasePoolableObjectFactory #makeObject()
             */
            @Override
            public Object makeObject() throws IOException, MalformedURLException, ServiceException {
                return new FedoraClient(FedoraUtility.this.fedoraUrl, FedoraUtility.this.fedoraUser,
                        FedoraUtility.this.fedoraPassword).getAPIA();
            }
        }), MAX_IDLE, INIT_IDLE_CAPACITY);

        this.apimPool = new StackObjectPool(new BasePoolableObjectFactory() {
            /**
             * See Interface for functional description.
             * 
             * @return
             * @see BasePoolableObjectFactory #makeObject()
             */
            @Override
            public Object makeObject() throws IOException, MalformedURLException, ServiceException {
                return new FedoraClient(FedoraUtility.this.fedoraUrl, FedoraUtility.this.fedoraUser,
                        FedoraUtility.this.fedoraPassword).getAPIM();
            }
        }, MAX_IDLE, INIT_IDLE_CAPACITY);

        this.syncRestQuery = this.fedoraUrl + "/risearch?flush=true";
    }

    /**
     * Returns a HttpClient object configured with credentials to access Fedora URLs.
     * 
     * @return A HttpClient object configured with credentials to access Fedora URLs.
     * @throws de.escidoc.core.common.exceptions.system.WebserverSystemException
     */
    DefaultHttpClient getHttpClient() throws WebserverSystemException {
        try {
            if (this.httpClient == null) {
                final HttpParams params = new BasicHttpParams();
                ConnManagerParams.setMaxTotalConnections(params, HTTP_MAX_TOTAL_CONNECTIONS);

                final ConnPerRoute connPerRoute = new ConnPerRouteBean(HTTP_MAX_CONNECTIONS_PER_HOST);
                ConnManagerParams.setMaxConnectionsPerRoute(params, connPerRoute);

                final Scheme http = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80);
                final SchemeRegistry sr = new SchemeRegistry();
                sr.register(http);
                final ClientConnectionManager cm = new ThreadSafeClientConnManager(params, sr);

                this.httpClient = new DefaultHttpClient(cm, params);
                final URL url = new URL(this.fedoraUrl);
                final CredentialsProvider credsProvider = new BasicCredentialsProvider();

                final AuthScope authScope = new AuthScope(url.getHost(), AuthScope.ANY_PORT, AuthScope.ANY_REALM);
                final Credentials creds = new UsernamePasswordCredentials(this.fedoraUser, this.fedoraPassword);
                credsProvider.setCredentials(authScope, creds);

                httpClient.setCredentialsProvider(credsProvider);
            }

            // don't wait for auth request
            final HttpRequestInterceptor preemptiveAuth = new HttpRequestInterceptor() {

                @Override
                public void process(final HttpRequest request, final HttpContext context)
                        throws HttpException, IOException {

                    final AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);
                    final CredentialsProvider credsProvider = (CredentialsProvider) context
                            .getAttribute(ClientContext.CREDS_PROVIDER);
                    final HttpHost targetHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);

                    // If not auth scheme has been initialized yet
                    if (authState.getAuthScheme() == null) {
                        final AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort());
                        // Obtain credentials matching the target host
                        final Credentials creds = credsProvider.getCredentials(authScope);
                        // If found, generate BasicScheme preemptively
                        if (creds != null) {
                            authState.setAuthScheme(new BasicScheme());
                            authState.setCredentials(creds);
                        }
                    }
                }

            };

            httpClient.addRequestInterceptor(preemptiveAuth, 0);

            // try only BASIC auth; skip to test NTLM and DIGEST

            return this.httpClient;
        } catch (final MalformedURLException e) {
            throw new WebserverSystemException("Fedora URL from configuration malformed.", e);
        }
    }

    /**
     * Makes a HTTP GET request to Fedora URL expanded by given local URL.
     * 
     * @param localUrl
     *            The Fedora local URL. Should start with the '/' after the webcontext path (usually "fedora"). E.g. if
     *            http://localhost:8080/fedora/get/... then localUrl is /get/...
     * @return MimeInputStream for content of the URL Request.
     * @throws WebserverSystemException
     *             If an error occurs.
     */
    public MimeInputStream requestMimeFedoraURL(final String localUrl) throws WebserverSystemException {
        final MimeInputStream fedoraResponseStream = new MimeInputStream();
        try {
            final DefaultHttpClient httpClient = getHttpClient();
            final HttpContext localcontext = new BasicHttpContext();
            final BasicScheme basicAuth = new BasicScheme();
            localcontext.setAttribute("preemptive-auth", basicAuth);
            httpClient.addRequestInterceptor(new PreemptiveAuthInterceptor(), 0);
            final HttpGet httpGet = new HttpGet(this.fedoraUrl + localUrl);
            final HttpResponse httpResponse = httpClient.execute(httpGet);
            final int responseCode = httpResponse.getStatusLine().getStatusCode();

            if (httpResponse.getFirstHeader("Content-Type") != null) {
                fedoraResponseStream.setMimeType(httpResponse.getFirstHeader("Content-Type").getValue());
            }

            if (responseCode != HttpServletResponse.SC_OK) {
                EntityUtils.consume(httpResponse.getEntity());
                throw new WebserverSystemException(
                        "Bad response code '" + responseCode + "' requesting '" + this.fedoraUrl + localUrl + "'.",
                        new FedoraSystemException(httpResponse.getStatusLine().getReasonPhrase()));
            }
            fedoraResponseStream.setInputStream(httpResponse.getEntity().getContent());

        } catch (final IOException e) {
            throw new WebserverSystemException(e);
        }

        return fedoraResponseStream;
    }

    /**
     * Makes a HTTP GET request to Fedora URL expanded by given local URL.
     * 
     * @param localUrl
     *            The Fedora local URL. Should start with the '/' after the webcontext path (usually "fedora"). E.g. if
     *            http://localhost:8080/fedora/get/... then localUrl is /get/...
     * @return the content of the URL Request.
     * @throws WebserverSystemException
     *             If an error occurs.
     */
    public InputStream requestFedoraURL(final String localUrl) throws WebserverSystemException {
        try {
            return requestMimeFedoraURL(localUrl).getInputStream();
        } catch (Exception e) {
            throw new WebserverSystemException(e);
        }
    }

    /**
     * @param fedoraUrl
     *            the fedoraUrl to inject
     */
    public void setFedoraUrl(final String fedoraUrl) {

        if (this.fedoraUrl != null) {
            throw new NotWritablePropertyException(this.getClass(), "fedoraUrl", "Property must not be set twice.");
        }
        this.fedoraUrl = fedoraUrl;
    }

    /**
     * @param fedoraUser
     *            the fedoraUser to inject
     */
    public void setFedoraUser(final String fedoraUser) {

        if (this.fedoraUser != null) {
            throw new NotWritablePropertyException(this.getClass(), "fedoraUser",
                    "Property must not be set twice.");
        }
        this.fedoraUser = fedoraUser;
    }

    /**
     * @param fedoraPassword
     *            the fedoraPassword to inject
     */
    public void setFedoraPassword(final String fedoraPassword) {

        if (this.fedoraPassword != null) {
            throw new NotWritablePropertyException(this.getClass(), "fedoraPassword",
                    "Property must not be set twice.");
        }
        this.fedoraPassword = fedoraPassword;
    }

    /**
     * @param identifierPrefix
     *            the identifierPrefix to inject
     */
    public void setIdentifierPrefix(final String identifierPrefix) {

        this.identifierPrefix = identifierPrefix;
    }

    /**
     * Injects the TripleStore utility.
     * 
     * @param tripleStoreUtility
     *            TripleStoreUtility from Spring
     */
    public void setTripleStoreUtility(final TripleStoreUtility tripleStoreUtility) {
        this.tripleStoreUtility = tripleStoreUtility;
    }

    /**
     * Get FedoraUtility instance.
     * 
     * @return Instance of FedoraUtilitiy.
     * @throws FedoraSystemException
     *             Thrown if instantiation failed.
     */
    public static FedoraUtility getInstance() throws FedoraSystemException {

        try {
            return (FedoraUtility) BeanLocator.getBean(BeanLocator.COMMON_FACTORY_ID,
                    "escidoc.core.business.FedoraUtility");
        } catch (final WebserverSystemException e) {
            throw new FedoraSystemException("FedoraUtility creation failed", e);
        }
    }

    /**
     * FIXME (SWA) crud, crud, crud!
     * <p/>
     * This should only prevent to write the FoXML into log if the resource URL is wrong or resource in in accessible.
     * If the exception is required as Fedora Exception or not, like in the case of Components, is decided in the upper
     * methods.
     * 
     * @param e
     *            Exception
     */
    private void preventWrongLogging(final Exception e) {

        final Pattern patternErrorGetting = Pattern.compile("fedora.server.errors.GeneralException: Error getting",
                Pattern.CASE_INSENSITIVE);
        final Pattern patternMalformedUrl = Pattern.compile(
                "fedora.server.errors.ObjectIntegrityException: " + "FOXML IO stream was bad : Malformed URL");

        final Matcher matcherErrorGetting = patternErrorGetting.matcher(e.getMessage());
        final Matcher matcherMalformedUrl = patternMalformedUrl.matcher(e.getMessage());

        if (matcherErrorGetting.find() || matcherMalformedUrl.find()) {
            if (LOGGER.isWarnEnabled()) {
                LOGGER.warn("Failed to load content. ");
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Failed to load content. ", e);
            }
        } else {
            if (LOGGER.isWarnEnabled()) {
                LOGGER.warn("Failed to modify Fedora datastream.");
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Failed to modify Fedora datastream.", e);
            }
        }

    }

}