Java tutorial
/* * 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; } }