/* 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.server.access;
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PipedReader;
import java.io.PipedWriter;
import java.net.URLDecoder;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.fcrepo.common.Constants;
import org.fcrepo.server.Context;
import org.fcrepo.server.ReadOnlyContext;
import org.fcrepo.server.errors.GeneralException;
import org.fcrepo.server.errors.ServerException;
import org.fcrepo.server.errors.StreamIOException;
import org.fcrepo.server.errors.authorization.AuthzException;
import org.fcrepo.server.errors.servletExceptionExtensions.InternalError500Exception;
import org.fcrepo.server.errors.servletExceptionExtensions.RootException;
import org.fcrepo.utilities.XmlTransformUtility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implements the "describeRepository" functionality of the Fedora Access LITE
* (API-A-LITE) interface using a java servlet front end. The syntax defined by
* API-A-LITE has for getting a description of the repository has the following
* binding:
* <p>describeRepository URL syntax:
* protocol://hostname:port/fedora/describe[?xml=BOOLEAN] This syntax requests
* information about the repository. The xml parameter determines the type of
* output returned. If the parameter is omitted or has a value of "false", a
* MIME-typed stream consisting of an html table is returned providing a
* browser-savvy means of viewing the object profile. If the value specified is
* "true", then a MIME-typed stream consisting of XML is returned.</p>
* <ul>
* <li>protocol - either http or https.</li>
* <li>hostname - required hostname of the Fedora server.</li>
* <li>port - required port number on which the Fedora server is running.</li>
* <li>fedora - required name of the Fedora access service.</li>
* <li>describe - required verb of the Fedora service.</li>
* <li>xml - an optional parameter indicating the requested output format. A
* value of "true" indicates a return type of text/xml; the absence of the xml
* parameter or a value of "false" indicates format is to be text/html.</li>
* </ul>
*
* @author Ross Wayland
*/
public class DescribeRepositoryServlet
extends SpringAccessServlet
implements Constants {
private static final Logger logger =
LoggerFactory.getLogger(DescribeRepositoryServlet.class);
private static final long serialVersionUID = 1L;
/** Content type for html. */
private static final String CONTENT_TYPE_HTML = "text/html; charset=UTF-8";
/** Content type for xml. */
private static final String CONTENT_TYPE_XML = "text/xml; charset=UTF-8";
String ACTION_LABEL = "describe repository";
/**
* <p>
* Process Fedora Access Request. Parse and validate the servlet input
* parameters and then execute the specified request.
* </p>
*
* @param request
* The servlet request.
* @param response
* servlet The servlet response.
* @throws ServletException
* If an error occurs that effects the servlet's basic operation.
* @throws IOException
* If an error occurrs with an input or output operation.
*/
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
boolean xml = false;
logger.debug("Got request: {}?{}", request.getRequestURL(),
request.getQueryString());
// Check for xml parameter.
for (Enumeration<?> e = request.getParameterNames(); e.hasMoreElements();) {
String name = URLDecoder.decode((String) e.nextElement(), "UTF-8");
if (name.equalsIgnoreCase("xml")) {
xml = Boolean.parseBoolean(request.getParameter(name));
}
}
Context context =
ReadOnlyContext.getContext(Constants.HTTP_REQUEST.REST.uri,
request);
try {
describeRepository(context, xml, response);
} catch (AuthzException ae) {
throw RootException.getServletException(ae,
request,
ACTION_LABEL,
EMPTY_STRING_ARRAY);
} catch (Throwable th) {
throw new InternalError500Exception("",
th,
request,
ACTION_LABEL,
"",
EMPTY_STRING_ARRAY);
}
}
public void describeRepository(Context context,
boolean xml,
HttpServletResponse response)
throws ServerException {
OutputStreamWriter out = null;
RepositoryInfo repositoryInfo = null;
PipedWriter pw = null;
PipedReader pr = null;
try {
pw = new PipedWriter();
pr = new PipedReader(pw);
repositoryInfo = m_access.describeRepository(context);
if (repositoryInfo != null) {
// Repository info obtained.
// Serialize the RepositoryInfo object into XML
new ReposInfoSerializerThread(context, repositoryInfo, pw)
.start();
if (xml) {
// Return results as raw XML
response.setContentType(CONTENT_TYPE_XML);
// Insures stream read from PipedReader correctly translates
// utf-8
// encoded characters to OutputStreamWriter.
out =
new OutputStreamWriter(response.getOutputStream(),
"UTF-8");
int bufSize = 4096;
char[] buf = new char[bufSize];
int len = 0;
while ((len = pr.read(buf, 0, bufSize)) != -1) {
out.write(buf, 0, len);
}
out.flush();
} else {
// Transform results into an html table
response.setContentType(CONTENT_TYPE_HTML);
out =
new OutputStreamWriter(response.getOutputStream(),
"UTF-8");
File xslFile =
new File(m_server.getHomeDir(),
"access/viewRepositoryInfo.xslt");
Templates template =
XmlTransformUtility.getTemplates(xslFile);
Transformer transformer = template.newTransformer();
transformer.setParameter("fedora", context
.getEnvironmentValue(FEDORA_APP_CONTEXT_NAME));
transformer.transform(new StreamSource(pr),
new StreamResult(out));
}
out.flush();
} else {
// Describe request returned nothing.
logger.error("No repository info returned");
}
} catch (AuthzException ae) {
throw ae;
} catch (Throwable th) {
String msg = "Error describing repository";
logger.error(msg, th);
throw new GeneralException(msg, th);
} finally {
try {
if (pr != null) {
pr.close();
}
if (out != null) {
out.close();
}
} catch (Throwable th) {
String message =
"[DescribeRepositoryServlet] An error has occured. "
+ " The error was a \" "
+ th.getClass().getName() + " \". Reason: "
+ th.getMessage();
throw new StreamIOException(message);
}
}
}
/**
* <p>
* A Thread to serialize an ObjectProfile object into XML.
* </p>
*/
public class ReposInfoSerializerThread
extends Thread {
private PipedWriter pw = null;
private RepositoryInfo repositoryInfo = null;
/**
* <p>
* Constructor for ReposInfoSerializerThread.
* </p>
*
* @param repositoryInfo
* A repository info data structure.
* @param pw
* A PipedWriter to which the serialization info is written.
*/
public ReposInfoSerializerThread(Context context,
RepositoryInfo repositoryInfo,
PipedWriter pw) {
this.pw = pw;
this.repositoryInfo = repositoryInfo;
if (Constants.HTTP_REQUEST.SECURE.uri.equals(context
.getEnvironmentValue(Constants.HTTP_REQUEST.SECURITY.attributeId))) {
} else if (Constants.HTTP_REQUEST.INSECURE.uri.equals(context
.getEnvironmentValue(Constants.HTTP_REQUEST.SECURITY.attributeId))) {
}
}
/**
* <p>
* This method executes the thread.
* </p>
*/
@Override
public void run() {
if (pw != null) {
try {
pw.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
pw.write("<fedoraRepository"
+ " xmlns=\"" + REPO_DESC1_0.namespace.uri + "\""
+ " xmlns:xsd=\"" + XML_XSD.uri + "\""
+ " xmlns:xsi=\"" + XSI.uri + "\""
+ " xsi:schemaLocation=\"" + ACCESS.uri + " "
+ REPO_DESC1_0.xsdLocation + "\">");
// REPOSITORY INFO FIELDS SERIALIZATION
pw.write("<repositoryName>" + repositoryInfo.repositoryName
+ "</repositoryName>");
pw.write("<repositoryBaseURL>"
+ repositoryInfo.repositoryBaseURL
+ "</repositoryBaseURL>");
pw.write("<repositoryVersion>"
+ repositoryInfo.repositoryVersion
+ "</repositoryVersion>");
pw.write("<repositoryPID>");
pw.write(" <PID-namespaceIdentifier>"
+ repositoryInfo.repositoryPIDNamespace
+ "</PID-namespaceIdentifier>");
pw.write(" <PID-delimiter>" + ":" + "</PID-delimiter>");
pw.write(" <PID-sample>" + repositoryInfo.samplePID
+ "</PID-sample>");
String[] retainPIDs = repositoryInfo.retainPIDs;
for (String element : retainPIDs) {
pw.write(" <retainPID>" + element + "</retainPID>");
}
pw.write("</repositoryPID>");
pw.write("<repositoryOAI-identifier>");
pw.write(" <OAI-namespaceIdentifier>"
+ repositoryInfo.OAINamespace
+ "</OAI-namespaceIdentifier>");
pw.write(" <OAI-delimiter>" + ":" + "</OAI-delimiter>");
pw.write(" <OAI-sample>"
+ repositoryInfo.sampleOAIIdentifer
+ "</OAI-sample>");
pw.write("</repositoryOAI-identifier>");
pw.write("<sampleSearch-URL>"
+ repositoryInfo.sampleSearchURL
+ "</sampleSearch-URL>");
pw.write("<sampleAccess-URL>"
+ repositoryInfo.sampleAccessURL
+ "</sampleAccess-URL>");
pw.write("<sampleOAI-URL>" + repositoryInfo.sampleOAIURL
+ "</sampleOAI-URL>");
String[] emails = repositoryInfo.adminEmailList;
for (String element : emails) {
pw.write("<adminEmail>" + element + "</adminEmail>");
}
pw.write("</fedoraRepository>");
pw.flush();
pw.close();
} catch (IOException ioe) {
logger.error("WriteThread IOException", ioe);
} finally {
try {
if (pw != null) {
pw.close();
}
} catch (IOException ioe) {
logger.error("WriteThread IOException", ioe);
}
}
}
}
}
/**
* <p>
* For now, treat a HTTP POST request just like a GET request.
* </p>
*
* @param request
* The servet request.
* @param response
* The servlet response.
* @throws ServletException
* If thrown by <code>doGet</code>.
* @throws IOException
* If thrown by <code>doGet</code>.
*/
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}