de.qucosa.servlet.MetsDisseminatorServlet.java Source code

Java tutorial

Introduction

Here is the source code for de.qucosa.servlet.MetsDisseminatorServlet.java

Source

/*
 * Copyright 2016 Saxon State and University Library Dresden (SLUB)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package de.qucosa.servlet;

import com.yourmediashelf.fedora.client.FedoraClient;
import com.yourmediashelf.fedora.client.FedoraCredentials;
import de.qucosa.disseminator.IdentifierCannotBeResolved;
import de.qucosa.disseminator.MetsDisseminator;
import de.qucosa.repository.AuthenticationException;
import de.qucosa.repository.Credentials;
import de.qucosa.repository.FedoraRepository;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import org.apache.commons.pool2.ObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.apache.xml.security.exceptions.Base64DecodingException;
import org.apache.xml.security.utils.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.LinkedHashSet;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;

public class MetsDisseminatorServlet extends HttpServlet {
    private static final String PROP_FEDORA_CREDENTIALS = "fedora.credentials";
    private static final String PROP_FEDORA_HOST_URL = "fedora.host.url";
    private static final String PROP_FEDORA_CONTENT_URL = "fedora.content.url";
    private static final String PROP_FEDORA_CONNECTIONPOOL_MAXSIZE = "fedora.connectionPool.maxSize";

    private final Logger log = LoggerFactory.getLogger(MetsDisseminatorServlet.class);
    private Cache cache;
    private CacheManager cacheManager;
    private ObjectPool<FedoraClient> fedoraClientPool = null;
    private Properties startupProperties;

    @Override
    public void init() throws ServletException {
        super.init();

        startupProperties = new PropertyCollector().source(getServletContext()).source(System.getProperties())
                .collect();

        final FedoraClientFactory fedoraClientFactory = attemptToCreateFedoraClientFactoryFrom(startupProperties);

        if (fedoraClientFactory == null) {
            // we need a client factory for startup connections
            log.warn("Fedora connection credentials not configured. No connection pooling possible.");
        } else {
            final GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
            poolConfig.setMaxTotal(
                    Integer.parseInt(startupProperties.getProperty(PROP_FEDORA_CONNECTIONPOOL_MAXSIZE, "20")));
            poolConfig.setMinIdle(5);
            poolConfig.setMinEvictableIdleTimeMillis(TimeUnit.MINUTES.toMillis(1));

            fedoraClientPool = new GenericObjectPool<>(fedoraClientFactory, poolConfig);

            log.info("Initialized Fedora connection pool.");
        }

        cacheManager = CacheManager.newInstance();
        cache = cacheManager.getCache("dscache");
    }

    @Override
    public void destroy() {
        super.destroy();

        try {
            fedoraClientPool.close();
        } catch (Exception e) {
            log.warn("Error while closing connection pool on shutdown", e);
        }

        cacheManager.clearAll();
    }

    /**
     * Writes a METS representation to the response object if a proper PID was given as query parameter.
     *
     * @param request  The HTTP request
     * @param response The HTTP response
     * @throws IOException if something failes when writing the response
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        try {
            final Set<String> allowedRequestParameters = new LinkedHashSet<>();
            allowedRequestParameters.add("pid");
            allowedRequestParameters.add("supplement");

            final Properties requestProperties = new PropertyCollector().source(startupProperties)
                    .source(request, allowedRequestParameters).collect();

            if (!requestProperties.containsKey("pid")) {
                throw new MissingRequiredParameter("pid");
            }

            if (!requestProperties.containsKey(PROP_FEDORA_HOST_URL)) {
                String targetUrlFromRequest = extractTargetUrlFromRequest(request);
                requestProperties.setProperty(PROP_FEDORA_HOST_URL, targetUrlFromRequest);
            }

            if (!requestProperties.containsKey(PROP_FEDORA_CONTENT_URL)) {
                requestProperties.setProperty(PROP_FEDORA_CONTENT_URL,
                        requestProperties.getProperty(PROP_FEDORA_HOST_URL));
            }

            final String basicAuthCredentials = extractBasicAuthCredentials(request);
            if (!basicAuthCredentials.isEmpty()) {
                requestProperties.setProperty(PROP_FEDORA_CREDENTIALS, basicAuthCredentials);
            }

            final FedoraClient client;
            if (basicAuthCredentials.isEmpty() && fedoraClientPool != null) {
                client = fedoraClientPool.borrowObject();
            } else {
                client = attemptToCreateFedoraClientFrom(requestProperties);
            }

            if (client == null) {
                throw new AuthenticationException("No connection obtainable. Credentials missing?");
            }

            final FedoraRepository fedoraRepository = new FedoraRepository(client, cache);

            if (fedoraClientPool != null) {
                fedoraClientPool.returnObject(client);
            }

            final MetsDisseminator metsDisseminator = new MetsDisseminator(fedoraRepository,
                    requestProperties.getProperty(PROP_FEDORA_CONTENT_URL), implementationVersion());

            metsDisseminator.disseminate(requestProperties.getProperty("pid"),
                    "yes".equals(requestProperties.getProperty("supplement")), response.getOutputStream());

            response.setStatus(HttpServletResponse.SC_OK);
            response.setContentType("application/mets+xml");
            response.setCharacterEncoding("UTF-8");

        } catch (AuthenticationException e) {
            final String msg = "Authentication failed";
            log.warn(msg, e.getMessage());
            response.sendError(SC_UNAUTHORIZED, msg);
        } catch (IdentifierCannotBeResolved e) {
            response.sendError(SC_NOT_FOUND, e.getMessage());
        } catch (MissingRequiredParameter e) {
            response.sendError(SC_BAD_REQUEST, e.getMessage());
        } catch (Exception e) {
            log.error("Unexpected error", e);
            response.sendError(SC_INTERNAL_SERVER_ERROR, "Internal Server Error");
        }
    }

    private FedoraClient attemptToCreateFedoraClientFrom(Properties properties) {
        if (properties.containsKey(PROP_FEDORA_CREDENTIALS) && properties.containsKey(PROP_FEDORA_HOST_URL)) {

            final Credentials credentials = Credentials
                    .fromColonSeparatedString(properties.getProperty(PROP_FEDORA_CREDENTIALS));

            try {
                return new FedoraClient(new FedoraCredentials(properties.getProperty(PROP_FEDORA_HOST_URL),
                        credentials.getUsername(), credentials.getPassword()));
            } catch (MalformedURLException ignored) {
                // noop
            }
        }
        return null;
    }

    private String implementationVersion() {
        String version = getClass().getPackage().getImplementationVersion();
        if (version == null) {
            Properties prop = new Properties();
            try {
                prop.load(getServletContext().getResourceAsStream("/META-INF/MANIFEST.MF"));
                version = prop.getProperty("Implementation-Version");
            } catch (Exception ignored) {
            }
        }
        return version;
    }

    private static String extractBasicAuthCredentials(HttpServletRequest request) throws AuthenticationException {
        String credentials = "";
        final String authHeader = request.getHeader("Authorization");
        if (authHeader != null && authHeader.startsWith("Basic")) {
            try {
                final String base64credentials = authHeader.substring("Basic".length()).trim();
                credentials = new String(Base64.decode(base64credentials));
            } catch (Base64DecodingException e) {
                throw new AuthenticationException("Provided credentials are invalid.");
            }
        }
        return credentials;
    }

    private static String extractTargetUrlFromRequest(HttpServletRequest request) {
        return String.format("%s://%s:%s/fedora", request.getScheme(), request.getLocalName(),
                request.getLocalPort());
    }

    private FedoraClientFactory attemptToCreateFedoraClientFactoryFrom(Properties properties) {
        if (properties.containsKey(PROP_FEDORA_CREDENTIALS) && properties.containsKey(PROP_FEDORA_HOST_URL)) {

            final Credentials credentials = Credentials
                    .fromColonSeparatedString(properties.getProperty(PROP_FEDORA_CREDENTIALS));

            return new FedoraClientFactory(properties.getProperty(PROP_FEDORA_HOST_URL), credentials.getUsername(),
                    credentials.getPassword());
        }
        return null;
    }
}