Skip to content
Snippets Groups Projects
Commit 105fb570 authored by Thorsten Vitt's avatar Thorsten Vitt
Browse files

Refactored ZIP export

parent 44164466
No related branches found
No related tags found
No related merge requests found
package info.textgrid.services.aggregator.zip; 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.AuthFault;
import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.IoFault; 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.MetadataParseFault;
import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.ObjectNotFoundFault; import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.ObjectNotFoundFault;
import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.TGCrudService; import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.ProtocolNotImplementedFault;
import info.textgrid.services.aggregator.ArgUtils;
import info.textgrid.services.aggregator.ITextGridRep; 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.io.IOException;
import java.util.logging.Logger;
import javax.servlet.ServletContext;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
...@@ -23,17 +17,14 @@ ...@@ -23,17 +17,14 @@
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.Request; import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response; 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; import org.apache.cxf.jaxrs.model.wadl.Description;
@Path("/zip") @Path("/zip")
public class ZIP { public class ZIP {
private final ITextGridRep repository; private final ITextGridRep repository;
final Logger logger = Logger.getLogger(ZIP.class.getCanonicalName());
@Context
private ServletContext servlet;
public ZIP(final ITextGridRep repository) { public ZIP(final ITextGridRep repository) {
this.repository = repository; this.repository = repository;
...@@ -49,42 +40,14 @@ public Response get( ...@@ -49,42 +40,14 @@ public Response get(
@QueryParam("sid") final String sid, @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.") @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, @QueryParam("title") String title,
@Context final Request request) throws MetadataParseFault, ObjectNotFoundFault, IoFault, AuthFault { @Context final Request request) throws MetadataParseFault, ObjectNotFoundFault, IoFault, AuthFault, ProtocolNotImplementedFault, IOException, SaxonApiException {
final TGCrudService crud = repository.getCRUDService(); ZipResult zipResult = new ZipResult(repository, request, uriList);
if (title != null)
if (uriList == null) zipResult.setTitle(title);
throw new IllegalArgumentException("Specify at least one URI to zip"); if (sid != null)
zipResult.sid(sid);
final ObjectType[] objects = ArgUtils.extractRootObjects(uriList, sid, crud); return zipResult.createResponse().build();
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();
} }
// 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');
// }
// }
} }
...@@ -3,6 +3,11 @@ ...@@ -3,6 +3,11 @@
import info.textgrid._import.ImportObject; import info.textgrid._import.ImportObject;
import info.textgrid._import.RewriteMethod; import info.textgrid._import.RewriteMethod;
import info.textgrid.namespaces.metadata.core._2010.ObjectType; 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.ITextGridRep;
import info.textgrid.services.aggregator.tree.AggregationTreeFactory; import info.textgrid.services.aggregator.tree.AggregationTreeFactory;
import info.textgrid.utils.export.aggregations.AggregationEntry; import info.textgrid.utils.export.aggregations.AggregationEntry;
...@@ -27,6 +32,8 @@ ...@@ -27,6 +32,8 @@
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import javax.ws.rs.WebApplicationException; 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.ws.rs.core.StreamingOutput;
import javax.xml.bind.JAXB; import javax.xml.bind.JAXB;
import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamException;
...@@ -42,110 +49,136 @@ ...@@ -42,110 +49,136 @@
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import com.google.common.io.FileBackedOutputStream; 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 policy;
private final IFilenamePolicy metaPolicy; private final IFilenamePolicy metaPolicy;
private ImportMapping mapping; private ImportMapping mapping;
private final static Logger logger = Logger.getLogger(ZipResult.class.getCanonicalName()); private final static Logger logger = Logger.getLogger(ZipResult.class
.getCanonicalName());
private final Set<URI> written = Sets.newHashSet();
private final Set<URI> written = Sets.newHashSet();
private static final Function<ObjectType, String> GetURI = new Function<ObjectType, String>() { private static final Function<ObjectType, String> GetURI = new Function<ObjectType, String>() {
@Override @Override
public String apply(final ObjectType input) { public String apply(final ObjectType input) {
return input.getGeneric().getGenerated().getTextgridUri().getValue(); return input.getGeneric().getGenerated().getTextgridUri()
.getValue();
} }
}; };
public ZipResult(final ITextGridRep repository, final Request request,
public ZipResult(final ITextGridRep repository, final String sid, final ObjectType... rootObjects) { final String uriList) {
this.rootObjects = rootObjects; super(repository, request, uriList);
this.repository = repository;
this.sid = sid;
this.policy = new DefaultFilenamePolicy(); this.policy = new DefaultFilenamePolicy();
this.metaPolicy = new DefaultMetaFilenamePolicy(policy); this.metaPolicy = new DefaultMetaFilenamePolicy(policy);
setFileExtension("zip");
setMediaType("application/zip");
} }
@Override @Override
public void write(final OutputStream output) throws IOException, public void write(final OutputStream output) throws IOException,
WebApplicationException { 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); 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); final List<IAggregationEntry> roots = Lists
for (final ObjectType rootMetadata : rootObjects) { .newArrayListWithCapacity(rootObjects.length);
final IAggregationEntry entry; for (final ObjectType rootMetadata : rootObjects) {
if (rootMetadata.getGeneric().getProvided().getFormat().contains("aggregation")) { final IAggregationEntry entry;
entry = AggregationTreeFactory.create(rootMetadata, repository, sid); if (rootMetadata.getGeneric().getProvided().getFormat()
} else { .contains("aggregation")) {
entry = new AggregationEntry(rootMetadata, null); 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 // now serializing the mapping
zip.putNextEntry(new ZipEntry(".INDEX.imex")); zip.putNextEntry(new ZipEntry(".INDEX.imex"));
JAXB.marshal(mapping.toImportSpec(), zip); JAXB.marshal(mapping.toImportSpec(), zip);
zip.closeEntry(); zip.closeEntry();
zip.close(); } catch (MetadataParseFault e) {
logger.log(Level.INFO, MessageFormat.format( throw new WebApplicationException(e);
"Finished exporting after {0,number} ms", } catch (ObjectNotFoundFault e) {
System.currentTimeMillis() - startTime)); 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 // FIXME refactor -> Rewrite library
private static ImmutableMap<String, String> REWRITE_CONFIGS = null; private static ImmutableMap<String, String> REWRITE_CONFIGS = null;
private static Optional<String> getRewriteConfig(final String contentType) { private static Optional<String> getRewriteConfig(final String contentType) {
if (REWRITE_CONFIGS == null) { if (REWRITE_CONFIGS == null) {
REWRITE_CONFIGS = ImmutableMap.<String, String>builder() REWRITE_CONFIGS = ImmutableMap
.put("text/tg.aggregation+xml", "internal:textgrid#aggregation") .<String, String> builder()
.put("text/tg.edition+tg.aggregation+xml", "internal:textgrid#aggregation") .put("text/tg.aggregation+xml",
.put("text/tg.collection+tg.aggregation+xml", "internal:textgrid#aggregation") "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/xsd+xml", "internal:schema#xsd")
.put("text/linkeditorlinkedfile", "internal:tei#tei") .put("text/linkeditorlinkedfile", "internal:tei#tei")
.put("text/xml", "internal:tei#tei") .put("text/xml", "internal:tei#tei")
.put("application/xhtml+xml", "internal:html#html") .put("application/xhtml+xml", "internal:html#html").build();
.build();
} }
return Optional.fromNullable(REWRITE_CONFIGS.get(contentType)); return Optional.fromNullable(REWRITE_CONFIGS.get(contentType));
} }
/** /**
* Recursively adds the entry to the mapping. * Recursively adds the entry to the mapping.
*
* @param mapping * @param mapping
* @param entry * @param entry
*/ */
private void addToMapping(final ImportMapping mapping, final IAggregationEntry entry) { private void addToMapping(final ImportMapping mapping,
final Optional<String> rewriteConfig = getRewriteConfig(((AggregationEntry) entry).getFormat()); final IAggregationEntry entry) {
final Optional<String> rewriteConfig = getRewriteConfig(((AggregationEntry) entry)
.getFormat());
final ImportObject importObject = new ImportObject(); final ImportObject importObject = new ImportObject();
importObject.setTextgridUri(entry.getTextGridURI().toString()); importObject.setTextgridUri(entry.getTextGridURI().toString());
importObject.setLocalData(policy.getFilename(entry, false).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()) { if (rewriteConfig.isPresent()) {
importObject.setRewriteMethod(RewriteMethod.XML); importObject.setRewriteMethod(RewriteMethod.XML);
importObject.setRewriteConfig(rewriteConfig.get()); importObject.setRewriteConfig(rewriteConfig.get());
...@@ -162,16 +195,19 @@ private void addToMapping(final ImportMapping mapping, final IAggregationEntry e ...@@ -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(); final URI uri = root.getTextGridURI();
if (written.contains(uri)) { if (written.contains(uri)) {
logger.log(Level.WARNING, "Skipping duplicate aggregation {0}", uri); logger.log(Level.WARNING, "Skipping duplicate aggregation {0}", uri);
return; return;
} }
writeFile(zip, root); 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); zip.putNextEntry(zipEntry);
zipEntry.setTime(root.getMetadata().getGeneric().getGenerated().getLastModified().toGregorianCalendar().getTimeInMillis()); zipEntry.setTime(root.getMetadata().getGeneric().getGenerated()
.getLastModified().toGregorianCalendar().getTimeInMillis());
zip.closeEntry(); zip.closeEntry();
for (final IAggregationEntry child : root.getChildren()) { for (final IAggregationEntry child : root.getChildren()) {
...@@ -183,7 +219,8 @@ private void writeAggregation(final ZipOutputStream zip, final IAggregation root ...@@ -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(); URI uri = child.getTextGridURI();
if (written.contains(uri)) { if (written.contains(uri)) {
logger.log(Level.WARNING, "Skipping duplicate object {0}", uri); logger.log(Level.WARNING, "Skipping duplicate object {0}", uri);
...@@ -191,34 +228,42 @@ private void writeFile(final ZipOutputStream zip, final IAggregationEntry child) ...@@ -191,34 +228,42 @@ private void writeFile(final ZipOutputStream zip, final IAggregationEntry child)
} }
written.add(uri); written.add(uri);
writeMetadata(zip, child); writeMetadata(zip, child);
final ZipEntry zipEntry = new ZipEntry(policy.getFilename(child, false).toString()); final ZipEntry zipEntry = new ZipEntry(policy.getFilename(child, false)
zipEntry.setTime(child.getMetadata().getGeneric().getGenerated().getLastModified().toGregorianCalendar().getTimeInMillis()); .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 { try {
final InputStream content = repository.getContent(child.getTextGridURI(), sid); final InputStream content = repository.getContent(
child.getTextGridURI(), getSid().orNull());
if (importObject.getRewriteMethod().equals(RewriteMethod.XML)) { 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())); rewriter.configure(URI.create(importObject.getRewriteConfig()));
final Optional<URI> base = policy.getBase(child); final Optional<URI> base = policy.getBase(child);
if (base.isPresent()) { if (base.isPresent()) {
rewriter.setBase(base.get()); rewriter.setBase(base.get());
} }
final FileBackedOutputStream buffer = new FileBackedOutputStream(1024 * 1024); final FileBackedOutputStream buffer = new FileBackedOutputStream(
1024 * 1024);
try { try {
rewriter.rewrite(content, buffer); rewriter.rewrite(content, buffer);
zip.putNextEntry(zipEntry); zip.putNextEntry(zipEntry);
ByteStreams.copy(buffer.getSupplier(), zip); ByteStreams.copy(buffer.getSupplier(), zip);
} catch (final XMLStreamException e) { } 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); logger.log(Level.WARNING, errorMsg, e);
zipEntry.setComment(errorMsg); zipEntry.setComment(errorMsg);
importObject.setRewriteMethod(RewriteMethod.NONE); importObject.setRewriteMethod(RewriteMethod.NONE);
zip.putNextEntry(zipEntry); zip.putNextEntry(zipEntry);
ByteStreams.copy(buffer.getSupplier(), zip); ByteStreams.copy(buffer.getSupplier(), zip);
} }
} } else {
else {
zip.putNextEntry(zipEntry); zip.putNextEntry(zipEntry);
ByteStreams.copy(content, zip); ByteStreams.copy(content, zip);
} }
...@@ -229,23 +274,30 @@ private void writeFile(final ZipOutputStream zip, final IAggregationEntry child) ...@@ -229,23 +274,30 @@ private void writeFile(final ZipOutputStream zip, final IAggregationEntry child)
} }
} }
private void writeMetadata(final ZipOutputStream zip, final IAggregationEntry child) throws IOException { private void writeMetadata(final ZipOutputStream zip,
final ZipEntry zipEntry = new ZipEntry(metaPolicy.getFilename(child, false).toString()); final IAggregationEntry child) throws IOException {
zipEntry.setTime(child.getMetadata().getGeneric().getGenerated().getLastModified().toGregorianCalendar().getTimeInMillis()); final ZipEntry zipEntry = new ZipEntry(metaPolicy.getFilename(child,
false).toString());
zipEntry.setTime(child.getMetadata().getGeneric().getGenerated()
.getLastModified().toGregorianCalendar().getTimeInMillis());
zip.putNextEntry(zipEntry); zip.putNextEntry(zipEntry);
final ConfigurableXMLRewriter rewriter = new ConfigurableXMLRewriter(mapping, true); final ConfigurableXMLRewriter rewriter = new ConfigurableXMLRewriter(
mapping, true);
rewriter.configure(URI.create("internal:textgrid#metadata")); rewriter.configure(URI.create("internal:textgrid#metadata"));
final Optional<URI> base = metaPolicy.getBase(child); final Optional<URI> base = metaPolicy.getBase(child);
if (base.isPresent()) { if (base.isPresent()) {
rewriter.setBase(base.get()); rewriter.setBase(base.get());
} }
final FileBackedOutputStream buffer = new FileBackedOutputStream(1024*1024); final FileBackedOutputStream buffer = new FileBackedOutputStream(
1024 * 1024);
JAXB.marshal(child.getMetadata(), buffer); JAXB.marshal(child.getMetadata(), buffer);
try { try {
rewriter.rewrite(buffer.getSupplier().getInput(), zip); rewriter.rewrite(buffer.getSupplier().getInput(), zip);
} catch (final XMLStreamException e) { } 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(); zip.closeEntry();
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment