From 105fb570ccaa853cc7ba09f81ec5e8bf61e752ed Mon Sep 17 00:00:00 2001 From: Thorsten Vitt <thorsten.vitt@uni-wuerzburg.de> Date: Mon, 20 Jan 2014 12:51:26 +0100 Subject: [PATCH] Refactored ZIP export --- .../textgrid/services/aggregator/zip/ZIP.java | 61 ++---- .../services/aggregator/zip/ZipResult.java | 202 +++++++++++------- 2 files changed, 139 insertions(+), 124 deletions(-) diff --git a/src/main/java/info/textgrid/services/aggregator/zip/ZIP.java b/src/main/java/info/textgrid/services/aggregator/zip/ZIP.java index bb97193..9007b62 100644 --- a/src/main/java/info/textgrid/services/aggregator/zip/ZIP.java +++ b/src/main/java/info/textgrid/services/aggregator/zip/ZIP.java @@ -1,20 +1,14 @@ package info.textgrid.services.aggregator.zip; -import info.textgrid.namespaces.metadata.core._2010.ObjectType; import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.AuthFault; import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.IoFault; import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.MetadataParseFault; import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.ObjectNotFoundFault; -import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.TGCrudService; -import info.textgrid.services.aggregator.ArgUtils; +import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.ProtocolNotImplementedFault; import info.textgrid.services.aggregator.ITextGridRep; -import info.textgrid.services.aggregator.RESTUtils; -import info.textgrid.utils.export.filenames.DefaultFilenamePolicy; -import java.util.Date; -import java.util.logging.Logger; +import java.io.IOException; -import javax.servlet.ServletContext; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; @@ -23,17 +17,14 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.Request; import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.ResponseBuilder; + +import net.sf.saxon.s9api.SaxonApiException; import org.apache.cxf.jaxrs.model.wadl.Description; @Path("/zip") public class ZIP { private final ITextGridRep repository; - final Logger logger = Logger.getLogger(ZIP.class.getCanonicalName()); - - @Context - private ServletContext servlet; public ZIP(final ITextGridRep repository) { this.repository = repository; @@ -49,42 +40,14 @@ public Response get( @QueryParam("sid") final String sid, @Description("(optional) title for the exported data, currently only used for generating the filename. If none is given, the first title of the first object will be used.") @QueryParam("title") String title, - @Context final Request request) throws MetadataParseFault, ObjectNotFoundFault, IoFault, AuthFault { - - final TGCrudService crud = repository.getCRUDService(); - - if (uriList == null) - throw new IllegalArgumentException("Specify at least one URI to zip"); - - final ObjectType[] objects = ArgUtils.extractRootObjects(uriList, sid, crud); - final Date lastModified = RESTUtils.createLastModified(objects); - if (request != null) { - ResponseBuilder builder = request.evaluatePreconditions(lastModified); - if (builder != null) - return builder.build(); - } - - if (title == null) { - title = objects[0].getGeneric().getProvided().getTitle().get(0); - } - - ResponseBuilder builder = RESTUtils - .attachmentResponse(DefaultFilenamePolicy.INSTANCE.translate(title) + ".zip") - .type("application/zip"); - RESTUtils.configureCache(builder, lastModified, sid != null); - return builder - .entity(new ZipResult(repository, sid, objects)) - .build(); + @Context final Request request) throws MetadataParseFault, ObjectNotFoundFault, IoFault, AuthFault, ProtocolNotImplementedFault, IOException, SaxonApiException { + + ZipResult zipResult = new ZipResult(repository, request, uriList); + if (title != null) + zipResult.setTitle(title); + if (sid != null) + zipResult.sid(sid); + return zipResult.createResponse().build(); } -// private void appendLS(final Aggregation aggregation, final StringBuilder output) { -// output.append(aggregation.getFileName(true)).append(":\n"); -// for(final IAggregationEntry child : aggregation.getChildren()) -// if (child instanceof Aggregation) { -// appendLS((Aggregation) child, output); -// } else { -// output.append(' ').append(child.getFileName(true)).append('\n'); -// } -// } - } diff --git a/src/main/java/info/textgrid/services/aggregator/zip/ZipResult.java b/src/main/java/info/textgrid/services/aggregator/zip/ZipResult.java index 1bdbae5..022c463 100644 --- a/src/main/java/info/textgrid/services/aggregator/zip/ZipResult.java +++ b/src/main/java/info/textgrid/services/aggregator/zip/ZipResult.java @@ -3,6 +3,11 @@ import info.textgrid._import.ImportObject; import info.textgrid._import.RewriteMethod; import info.textgrid.namespaces.metadata.core._2010.ObjectType; +import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.AuthFault; +import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.IoFault; +import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.MetadataParseFault; +import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.ObjectNotFoundFault; +import info.textgrid.services.aggregator.AbstractExporter; import info.textgrid.services.aggregator.ITextGridRep; import info.textgrid.services.aggregator.tree.AggregationTreeFactory; import info.textgrid.utils.export.aggregations.AggregationEntry; @@ -27,6 +32,8 @@ import java.util.zip.ZipOutputStream; import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Request; +import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.StreamingOutput; import javax.xml.bind.JAXB; import javax.xml.stream.XMLStreamException; @@ -42,110 +49,136 @@ import com.google.common.io.ByteStreams; import com.google.common.io.FileBackedOutputStream; -public class ZipResult implements StreamingOutput { +public class ZipResult extends AbstractExporter implements StreamingOutput { - private final ObjectType[] rootObjects; - private final ITextGridRep repository; - private final String sid; private final IFilenamePolicy policy; private final IFilenamePolicy metaPolicy; private ImportMapping mapping; - private final static Logger logger = Logger.getLogger(ZipResult.class.getCanonicalName()); - - private final Set<URI> written = Sets.newHashSet(); + private final static Logger logger = Logger.getLogger(ZipResult.class + .getCanonicalName()); + private final Set<URI> written = Sets.newHashSet(); private static final Function<ObjectType, String> GetURI = new Function<ObjectType, String>() { @Override public String apply(final ObjectType input) { - return input.getGeneric().getGenerated().getTextgridUri().getValue(); + return input.getGeneric().getGenerated().getTextgridUri() + .getValue(); } }; - - public ZipResult(final ITextGridRep repository, final String sid, final ObjectType... rootObjects) { - this.rootObjects = rootObjects; - this.repository = repository; - this.sid = sid; + public ZipResult(final ITextGridRep repository, final Request request, + final String uriList) { + super(repository, request, uriList); this.policy = new DefaultFilenamePolicy(); this.metaPolicy = new DefaultMetaFilenamePolicy(policy); + setFileExtension("zip"); + setMediaType("application/zip"); } @Override public void write(final OutputStream output) throws IOException, WebApplicationException { - final long startTime = System.currentTimeMillis(); - final String uriList = Joiner.on(", ").join(Iterators.transform(Iterators.forArray(rootObjects), GetURI)); - logger.log(Level.INFO, "Starting ZIP export of {0}", uriList); final ZipOutputStream zip = new ZipOutputStream(output); - zip.setComment(MessageFormat.format("# Exported from TextGrid -- www.textgrid.de\nRoot objects: {0}", uriList)); + try { + ObjectType[] rootObjects = getRootObjects(); + final String uriList = Joiner.on(", ") + .join(Iterators.transform(Iterators.forArray(rootObjects), + GetURI)); + logger.log(Level.INFO, MessageFormat.format( + "Starting ZIP export of {0} after {1}", uriList, stopwatch)); + zip.setComment(MessageFormat + .format("# Exported from TextGrid -- www.textgrid.de\nRoot objects: {0}", + uriList)); - mapping = new ImportMapping(); + mapping = new ImportMapping(); - final List<IAggregationEntry> roots = Lists.newArrayListWithCapacity(rootObjects.length); - for (final ObjectType rootMetadata : rootObjects) { - final IAggregationEntry entry; - if (rootMetadata.getGeneric().getProvided().getFormat().contains("aggregation")) { - entry = AggregationTreeFactory.create(rootMetadata, repository, sid); - } else { - entry = new AggregationEntry(rootMetadata, null); + final List<IAggregationEntry> roots = Lists + .newArrayListWithCapacity(rootObjects.length); + for (final ObjectType rootMetadata : rootObjects) { + final IAggregationEntry entry; + if (rootMetadata.getGeneric().getProvided().getFormat() + .contains("aggregation")) { + entry = AggregationTreeFactory.create(rootMetadata, + repository, getSid().orNull()); + } else { + entry = new AggregationEntry(rootMetadata, null); + } + logger.log(Level.INFO, MessageFormat.format( + " Built aggregation tree for {1} after {0}", + stopwatch, GetURI.apply(rootMetadata))); + roots.add(entry); + addToMapping(mapping, entry); } - logger.log(Level.INFO, MessageFormat.format(" Built aggregation tree for {1} after {0,number} ms", - System.currentTimeMillis() - startTime, GetURI.apply(rootMetadata))); - roots.add(entry); - addToMapping(mapping, entry); - } - - for (final IAggregationEntry root : roots) { - if (root instanceof IAggregation) - writeAggregation(zip, (IAggregation) root); - else - writeFile(zip, root); - logger.log(Level.INFO, MessageFormat.format(" Zipped {1} after {0,number} ms", - System.currentTimeMillis() - startTime, root.getTextGridURI())); - } + for (final IAggregationEntry root : roots) { + if (root instanceof IAggregation) + writeAggregation(zip, (IAggregation) root); + else + writeFile(zip, root); + logger.log(Level.INFO, MessageFormat.format( + " Zipped {1} after {0}", stopwatch, + root.getTextGridURI())); + } - // now serializing the mapping - zip.putNextEntry(new ZipEntry(".INDEX.imex")); - JAXB.marshal(mapping.toImportSpec(), zip); - zip.closeEntry(); - zip.close(); - logger.log(Level.INFO, MessageFormat.format( - "Finished exporting after {0,number} ms", - System.currentTimeMillis() - startTime)); + // now serializing the mapping + zip.putNextEntry(new ZipEntry(".INDEX.imex")); + JAXB.marshal(mapping.toImportSpec(), zip); + zip.closeEntry(); + } catch (MetadataParseFault e) { + throw new WebApplicationException(e); + } catch (ObjectNotFoundFault e) { + throw new WebApplicationException(e, Status.NOT_FOUND); + } catch (IoFault e) { + throw new WebApplicationException(e); + } catch (AuthFault e) { + throw new WebApplicationException(e, Status.FORBIDDEN); + } finally { + zip.close(); + stopwatch.stop(); + } + logger.log(Level.INFO, + MessageFormat.format("Finished exporting after {0}", stopwatch)); } // FIXME refactor -> Rewrite library private static ImmutableMap<String, String> REWRITE_CONFIGS = null; + private static Optional<String> getRewriteConfig(final String contentType) { if (REWRITE_CONFIGS == null) { - REWRITE_CONFIGS = ImmutableMap.<String, String>builder() - .put("text/tg.aggregation+xml", "internal:textgrid#aggregation") - .put("text/tg.edition+tg.aggregation+xml", "internal:textgrid#aggregation") - .put("text/tg.collection+tg.aggregation+xml", "internal:textgrid#aggregation") + REWRITE_CONFIGS = ImmutableMap + .<String, String> builder() + .put("text/tg.aggregation+xml", + "internal:textgrid#aggregation") + .put("text/tg.edition+tg.aggregation+xml", + "internal:textgrid#aggregation") + .put("text/tg.collection+tg.aggregation+xml", + "internal:textgrid#aggregation") .put("text/xsd+xml", "internal:schema#xsd") .put("text/linkeditorlinkedfile", "internal:tei#tei") .put("text/xml", "internal:tei#tei") - .put("application/xhtml+xml", "internal:html#html") - .build(); + .put("application/xhtml+xml", "internal:html#html").build(); } return Optional.fromNullable(REWRITE_CONFIGS.get(contentType)); } /** * Recursively adds the entry to the mapping. + * * @param mapping * @param entry */ - private void addToMapping(final ImportMapping mapping, final IAggregationEntry entry) { - final Optional<String> rewriteConfig = getRewriteConfig(((AggregationEntry) entry).getFormat()); + private void addToMapping(final ImportMapping mapping, + final IAggregationEntry entry) { + final Optional<String> rewriteConfig = getRewriteConfig(((AggregationEntry) entry) + .getFormat()); final ImportObject importObject = new ImportObject(); importObject.setTextgridUri(entry.getTextGridURI().toString()); importObject.setLocalData(policy.getFilename(entry, false).toString()); - importObject.setLocalMetadata(metaPolicy.getFilename(entry, false).toString()); + importObject.setLocalMetadata(metaPolicy.getFilename(entry, false) + .toString()); if (rewriteConfig.isPresent()) { importObject.setRewriteMethod(RewriteMethod.XML); importObject.setRewriteConfig(rewriteConfig.get()); @@ -162,16 +195,19 @@ private void addToMapping(final ImportMapping mapping, final IAggregationEntry e } } - private void writeAggregation(final ZipOutputStream zip, final IAggregation root) throws IOException { + private void writeAggregation(final ZipOutputStream zip, + final IAggregation root) throws IOException { final URI uri = root.getTextGridURI(); if (written.contains(uri)) { logger.log(Level.WARNING, "Skipping duplicate aggregation {0}", uri); return; } writeFile(zip, root); - final ZipEntry zipEntry = new ZipEntry(policy.getFilename(root, true).toString()); + final ZipEntry zipEntry = new ZipEntry(policy.getFilename(root, true) + .toString()); zip.putNextEntry(zipEntry); - zipEntry.setTime(root.getMetadata().getGeneric().getGenerated().getLastModified().toGregorianCalendar().getTimeInMillis()); + zipEntry.setTime(root.getMetadata().getGeneric().getGenerated() + .getLastModified().toGregorianCalendar().getTimeInMillis()); zip.closeEntry(); for (final IAggregationEntry child : root.getChildren()) { @@ -183,7 +219,8 @@ private void writeAggregation(final ZipOutputStream zip, final IAggregation root } } - private void writeFile(final ZipOutputStream zip, final IAggregationEntry child) throws IOException { + private void writeFile(final ZipOutputStream zip, + final IAggregationEntry child) throws IOException { URI uri = child.getTextGridURI(); if (written.contains(uri)) { logger.log(Level.WARNING, "Skipping duplicate object {0}", uri); @@ -191,34 +228,42 @@ private void writeFile(final ZipOutputStream zip, final IAggregationEntry child) } written.add(uri); writeMetadata(zip, child); - final ZipEntry zipEntry = new ZipEntry(policy.getFilename(child, false).toString()); - zipEntry.setTime(child.getMetadata().getGeneric().getGenerated().getLastModified().toGregorianCalendar().getTimeInMillis()); + final ZipEntry zipEntry = new ZipEntry(policy.getFilename(child, false) + .toString()); + zipEntry.setTime(child.getMetadata().getGeneric().getGenerated() + .getLastModified().toGregorianCalendar().getTimeInMillis()); - final ImportObject importObject = mapping.getImportObjectForTextGridURI(child.getTextGridURI().toString()); + final ImportObject importObject = mapping + .getImportObjectForTextGridURI(child.getTextGridURI() + .toString()); try { - final InputStream content = repository.getContent(child.getTextGridURI(), sid); + final InputStream content = repository.getContent( + child.getTextGridURI(), getSid().orNull()); if (importObject.getRewriteMethod().equals(RewriteMethod.XML)) { - final ConfigurableXMLRewriter rewriter = new ConfigurableXMLRewriter(mapping, true); + final ConfigurableXMLRewriter rewriter = new ConfigurableXMLRewriter( + mapping, true); rewriter.configure(URI.create(importObject.getRewriteConfig())); final Optional<URI> base = policy.getBase(child); if (base.isPresent()) { rewriter.setBase(base.get()); } - final FileBackedOutputStream buffer = new FileBackedOutputStream(1024 * 1024); + final FileBackedOutputStream buffer = new FileBackedOutputStream( + 1024 * 1024); try { rewriter.rewrite(content, buffer); zip.putNextEntry(zipEntry); ByteStreams.copy(buffer.getSupplier(), zip); } catch (final XMLStreamException e) { - final String errorMsg = MessageFormat.format("Failed to rewrite {0} (error: {1}). Exported with verbatim links instead.", child, e.getMessage()); + final String errorMsg = MessageFormat + .format("Failed to rewrite {0} (error: {1}). Exported with verbatim links instead.", + child, e.getMessage()); logger.log(Level.WARNING, errorMsg, e); zipEntry.setComment(errorMsg); importObject.setRewriteMethod(RewriteMethod.NONE); zip.putNextEntry(zipEntry); ByteStreams.copy(buffer.getSupplier(), zip); } - } - else { + } else { zip.putNextEntry(zipEntry); ByteStreams.copy(content, zip); } @@ -229,23 +274,30 @@ private void writeFile(final ZipOutputStream zip, final IAggregationEntry child) } } - private void writeMetadata(final ZipOutputStream zip, final IAggregationEntry child) throws IOException { - final ZipEntry zipEntry = new ZipEntry(metaPolicy.getFilename(child, false).toString()); - zipEntry.setTime(child.getMetadata().getGeneric().getGenerated().getLastModified().toGregorianCalendar().getTimeInMillis()); + private void writeMetadata(final ZipOutputStream zip, + final IAggregationEntry child) throws IOException { + final ZipEntry zipEntry = new ZipEntry(metaPolicy.getFilename(child, + false).toString()); + zipEntry.setTime(child.getMetadata().getGeneric().getGenerated() + .getLastModified().toGregorianCalendar().getTimeInMillis()); zip.putNextEntry(zipEntry); - final ConfigurableXMLRewriter rewriter = new ConfigurableXMLRewriter(mapping, true); + final ConfigurableXMLRewriter rewriter = new ConfigurableXMLRewriter( + mapping, true); rewriter.configure(URI.create("internal:textgrid#metadata")); final Optional<URI> base = metaPolicy.getBase(child); if (base.isPresent()) { rewriter.setBase(base.get()); } - final FileBackedOutputStream buffer = new FileBackedOutputStream(1024*1024); + final FileBackedOutputStream buffer = new FileBackedOutputStream( + 1024 * 1024); JAXB.marshal(child.getMetadata(), buffer); try { rewriter.rewrite(buffer.getSupplier().getInput(), zip); } catch (final XMLStreamException e) { - logger.log(Level.SEVERE, MessageFormat.format("Error rewriting the metadata of {0}. Should not happen.", child), e); + logger.log(Level.SEVERE, MessageFormat.format( + "Error rewriting the metadata of {0}. Should not happen.", + child), e); } zip.closeEntry(); } -- GitLab