package org.fcrepo.server.rest; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PipedInputStream; import java.io.PipedOutputStream; import javax.servlet.ServletException; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; import javax.ws.rs.core.StreamingOutput; import javax.xml.transform.Templates; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; 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.Server; import org.fcrepo.server.access.RepositoryInfo; import org.fcrepo.server.errors.GeneralException; import org.fcrepo.server.errors.ServerException; import org.fcrepo.server.errors.authorization.AuthzException; import org.fcrepo.utilities.XmlTransformUtility; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * 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 * @author Benjamin Armintor armintor@gmail.com * @version $Id$ */ @Path("/") @Component public class DescribeRepositoryResource extends BaseRestResource implements Constants { private static final Logger logger = LoggerFactory.getLogger(DescribeRepositoryResource.class); @SuppressWarnings("unused") 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"; public DescribeRepositoryResource(Server server) { super(server); } /** * <p> * Process Fedora Access Request. Parse and validate the servlet input * parameters and then execute the specified request. * </p> * * @param xml * whether the request is for xml or html. * @throws ServletException * If an error occurs that effects the servlet's basic operation. * @throws IOException * If an error occurs with an input or output operation. */ @Path("/") @GET public Response describe(@QueryParam("xml") @DefaultValue("false") boolean xml) throws ServletException, IOException { Context context = getContext(); try { return describeRepository(context, xml); } catch (Exception ae) { return handleException(ae, false); } } @Path("/") @POST public Response postDescribe(@QueryParam("xml") @DefaultValue("false") boolean xml) throws ServletException, IOException { return describe(xml); } public Response describeRepository(Context context, boolean xml) throws ServerException { RepositoryInfo repositoryInfo = null; try { repositoryInfo = m_access.describeRepository(context); if (repositoryInfo != null) { // Repository info obtained. // Serialize the RepositoryInfo object into XML ReposInfoSerializer result = new ReposInfoSerializer(context, repositoryInfo); if (xml) { // Return results as raw XML return Response.ok(result,CONTENT_TYPE_XML).build(); } else { // Transform results into an html table return Response.ok(new HtmlTransformation(context, result),CONTENT_TYPE_HTML).build(); } } else { // Describe request returned nothing. logger.error("No repository info returned"); return Response.noContent().build(); } } catch (AuthzException ae) { throw ae; } catch (Throwable th) { String msg = "Error describing repository"; logger.error(msg, th); throw new GeneralException(msg, th); } } /** * <p> * Serializes RepositoryInfo object into XML. * </p> */ public class ReposInfoSerializer implements StreamingOutput { private RepositoryInfo repositoryInfo = null; /** * <p> * Constructor for ReposInfoSerializerThread. * </p> * * @param repositoryInfo * A repository info data structure. */ public ReposInfoSerializer(Context context, RepositoryInfo repositoryInfo) { this.repositoryInfo = repositoryInfo; } /** * <p> * This method executes the thread. * </p> */ @Override public void write(OutputStream output) throws IOException { OutputStreamWriter pw = new OutputStreamWriter(output,"UTF-8"); 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(); } } public class HtmlTransformation implements StreamingOutput { private final ReposInfoSerializer reposInfo; private final Context context; public HtmlTransformation(Context context, ReposInfoSerializer input) { this.reposInfo = input; this.context = context; } @Override public void write(OutputStream out) throws IOException { File xslFile = new File(m_server.getHomeDir(), "access/viewRepositoryInfo.xslt"); PipedOutputStream po = new PipedOutputStream(); PipedInputStream pr = new PipedInputStream(po); this.reposInfo.write(po); try { 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)); } catch (TransformerException te) { throw new IOException("Transform error" + te.toString(), te); } out.flush(); } } }