From 34d05ef3a034f8f6fedd22bf79b9903266c3a166 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 1 Dec 2014 11:14:19 +0100 Subject: [PATCH] implement gzip compression for unbundle command --- .../scm/cli/cmd/ImportBundleSubCommand.java | 40 ++++++- .../resources/sonia/resources/i18n.properties | 1 + .../sonia/scm/client/ImportBundleRequest.java | 91 ++++++++++++++-- .../client/JerseyRepositoryClientHandler.java | 7 +- .../api/UnbundleCommandBuilder.java | 100 +++++++++++++++++- .../resources/RepositoryImportResource.java | 20 ++-- .../sonia.repository.importwindow.js | 9 +- 7 files changed, 241 insertions(+), 27 deletions(-) diff --git a/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/cmd/ImportBundleSubCommand.java b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/cmd/ImportBundleSubCommand.java index f107255e42..d746df0828 100644 --- a/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/cmd/ImportBundleSubCommand.java +++ b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/cmd/ImportBundleSubCommand.java @@ -81,6 +81,17 @@ public class ImportBundleSubCommand extends ImportSubCommand return name; } + /** + * Method description + * + * + * @return + */ + public boolean isCompressed() + { + return compressed; + } + //~--- set methods ---------------------------------------------------------- /** @@ -94,6 +105,17 @@ public class ImportBundleSubCommand extends ImportSubCommand this.bundle = bundle; } + /** + * Method description + * + * + * @param compressed + */ + public void setCompressed(boolean compressed) + { + this.compressed = compressed; + } + /** * Method description * @@ -122,9 +144,13 @@ public class ImportBundleSubCommand extends ImportSubCommand { ScmClientSession session = createSession(); - Repository repository = session.getRepositoryHandler().importFromBundle( - new ImportBundleRequest( - getType(), name, Files.asByteSource(bundle))); + ImportBundleRequest req = new ImportBundleRequest(getType(), name, + Files.asByteSource(bundle)); + + req.setCompressed(compressed); + + Repository repository = + session.getRepositoryHandler().importFromBundle(req); printImportedRepository(repository); } @@ -141,6 +167,14 @@ public class ImportBundleSubCommand extends ImportSubCommand ) private File bundle; + /** Field description */ + @Option( + name = "--compressed", + usage = "optionRepositoryBundleCompressed", + aliases = { "-c" } + ) + private boolean compressed = false; + /** Field description */ @Option( name = "--name", diff --git a/scm-clients/scm-cli-client/src/main/resources/sonia/resources/i18n.properties b/scm-clients/scm-cli-client/src/main/resources/sonia/resources/i18n.properties index 47c4ee4ff2..9d6afd3dda 100644 --- a/scm-clients/scm-cli-client/src/main/resources/sonia/resources/i18n.properties +++ b/scm-clients/scm-cli-client/src/main/resources/sonia/resources/i18n.properties @@ -53,6 +53,7 @@ optionRepositoryPublic = Repository public readable optionRepositoryArchive = Repository archived optionRemoteRepositoryUrl = Remote repository url optionRepositoryBundle = Import repository from a bundle file (e.g. svn dump) +optionRepositoryBundleCompressed = Indicates that the bundle is gzip compressed optionPermissionGroup = Group optionPermissionName = Group or user name diff --git a/scm-clients/scm-client-api/src/main/java/sonia/scm/client/ImportBundleRequest.java b/scm-clients/scm-client-api/src/main/java/sonia/scm/client/ImportBundleRequest.java index 96dbb7d79f..c6c9e71016 100644 --- a/scm-clients/scm-client-api/src/main/java/sonia/scm/client/ImportBundleRequest.java +++ b/scm-clients/scm-client-api/src/main/java/sonia/scm/client/ImportBundleRequest.java @@ -26,8 +26,13 @@ * http://bitbucket.org/sdorra/scm-manager * */ + + + package sonia.scm.client; +//~--- non-JDK imports -------------------------------------------------------- + import com.google.common.io.ByteSource; /** @@ -37,14 +42,21 @@ import com.google.common.io.ByteSource; */ public class ImportBundleRequest { - private String type; - private String name; - private ByteSource bundle; - ImportBundleRequest() - { - } + /** + * Constructs ... + * + */ + ImportBundleRequest() {} + /** + * Constructs ... + * + * + * @param type + * @param name + * @param bundle + */ public ImportBundleRequest(String type, String name, ByteSource bundle) { this.type = type; @@ -52,19 +64,76 @@ public class ImportBundleRequest this.bundle = bundle; } - public String getType() + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + public ByteSource getBundle() { - return type; + return bundle; } + /** + * Method description + * + * + * @return + */ public String getName() { return name; } - public ByteSource getBundle() + /** + * Method description + * + * + * @return + */ + public String getType() { - return bundle; + return type; } - + + /** + * Method description + * + * + * @return + */ + public boolean isCompressed() + { + return compressed; + } + + //~--- set methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param compressed + */ + public void setCompressed(boolean compressed) + { + this.compressed = compressed; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private ByteSource bundle; + + /** Field description */ + private boolean compressed = false; + + /** Field description */ + private String name; + + /** Field description */ + private String type; } diff --git a/scm-clients/scm-client-impl/src/main/java/sonia/scm/client/JerseyRepositoryClientHandler.java b/scm-clients/scm-client-impl/src/main/java/sonia/scm/client/JerseyRepositoryClientHandler.java index 8b91db96ee..ff611c9aa2 100644 --- a/scm-clients/scm-client-impl/src/main/java/sonia/scm/client/JerseyRepositoryClientHandler.java +++ b/scm-clients/scm-client-impl/src/main/java/sonia/scm/client/JerseyRepositoryClientHandler.java @@ -58,6 +58,7 @@ import java.io.InputStream; import java.util.Collection; import java.util.List; + import javax.ws.rs.core.MediaType; /** @@ -80,6 +81,9 @@ public class JerseyRepositoryClientHandler /** Field description */ private static final String PARAM_BUNDLE = "bundle"; + /** Field description */ + private static final String PARAM_COMPRESSED = "compressed"; + /** Field description */ private static final String PARAM_NAME = "name"; @@ -113,7 +117,8 @@ public class JerseyRepositoryClientHandler public Repository importFromBundle(ImportBundleRequest request) { WebResource r = client.resource(getImportUrl(request.getType(), - IMPORT_TYPE_BUNDLE)); + IMPORT_TYPE_BUNDLE)).queryParam(PARAM_COMPRESSED, + Boolean.toString(request.isCompressed())); Repository repository = null; InputStream stream = null; diff --git a/scm-core/src/main/java/sonia/scm/repository/api/UnbundleCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/UnbundleCommandBuilder.java index 6e94073244..7860513b51 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/UnbundleCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/UnbundleCommandBuilder.java @@ -56,8 +56,10 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.util.zip.GZIPInputStream; + /** - * The unbundle command can restore an empty repository from a bundle. The + * The unbundle command can restore an empty repository from a bundle. The * bundle can be created with the {@link BundleCommandBuilder}. * * @author Sebastian Sdorra @@ -106,7 +108,7 @@ public final class UnbundleCommandBuilder "existing file is required"); UnbundleCommandRequest request = - new UnbundleCommandRequest(Files.asByteSource(inputFile)); + createRequest(Files.asByteSource(inputFile)); logger.info("unbundle archive {} at {}", inputFile, repository.getId()); @@ -130,8 +132,7 @@ public final class UnbundleCommandBuilder checkNotNull(inputStream, "input stream is required"); logger.info("unbundle archive from stream"); - return unbundleCommand.unbundle( - new UnbundleCommandRequest(asByteSource(inputStream))); + return unbundleCommand.unbundle(createRequest(asByteSource(inputStream))); } /** @@ -151,9 +152,28 @@ public final class UnbundleCommandBuilder checkNotNull(byteSource, "byte source is required"); logger.info("unbundle from byte source"); - return unbundleCommand.unbundle(new UnbundleCommandRequest(byteSource)); + return unbundleCommand.unbundle(createRequest(byteSource)); } + //~--- set methods ---------------------------------------------------------- + + /** + * Set to {@code true} if bundle is gzip compressed. Default is {@code false}. + * + * + * @param compressed {@code true} if bundle is gzip compressed + * + * @return {@code this} + */ + public UnbundleCommandBuilder setCompressed(boolean compressed) + { + this.compressed = compressed; + + return this; + } + + //~--- methods -------------------------------------------------------------- + /** * Converts an {@link InputStream} into a {@link ByteSource}. * @@ -175,6 +195,73 @@ public final class UnbundleCommandBuilder }); } + /** + * Creates the {@link UnbundleCommandRequest}. + * + * + * @param source byte source + * + * @return the create request + */ + private UnbundleCommandRequest createRequest(ByteSource source) + { + ByteSource bs; + + if (compressed) + { + logger.debug("decode gzip stream for unbundle command"); + bs = new CompressedByteSource(source); + } + else + { + bs = source; + } + + return new UnbundleCommandRequest(bs); + } + + //~--- inner classes -------------------------------------------------------- + + /** + * ByteSource which is able to handle gzip compressed resources. + */ + private static class CompressedByteSource extends ByteSource + { + + /** + * Constructs ... + * + * + * @param wrapped + */ + public CompressedByteSource(ByteSource wrapped) + { + this.wrapped = wrapped; + } + + //~--- methods ------------------------------------------------------------ + + /** + * Opens the stream for reading the compressed source. + * + * + * @return input stream + * + * @throws IOException + */ + @Override + public InputStream openStream() throws IOException + { + return new GZIPInputStream(wrapped.openStream()); + } + + //~--- fields ------------------------------------------------------------- + + /** Field description */ + private final ByteSource wrapped; + } + + //~--- fields --------------------------------------------------------------- /** repository */ @@ -182,4 +269,7 @@ public final class UnbundleCommandBuilder /** unbundle command implementation */ private final UnbundleCommand unbundleCommand; + + /** Field description */ + private boolean compressed = false; } diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java index 5a3be7dd26..c6c78b0136 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java @@ -87,11 +87,13 @@ import java.util.List; import java.util.Set; import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.GenericEntity; @@ -156,6 +158,7 @@ public class RepositoryImportResource * @param type repository type * @param name name of the repository * @param inputStream input bundle + * @param compressed true if the bundle is gzip compressed * * @return empty response with location header which points to the imported * repository @@ -166,9 +169,11 @@ public class RepositoryImportResource @Consumes(MediaType.MULTIPART_FORM_DATA) public Response importFromBundle(@Context UriInfo uriInfo, @PathParam("type") String type, @FormDataParam("name") String name, - @FormDataParam("bundle") InputStream inputStream) + @FormDataParam("bundle") InputStream inputStream, @QueryParam("compressed") + @DefaultValue("false") boolean compressed) { - Repository repository = doImportFromBundle(type, name, inputStream); + Repository repository = doImportFromBundle(type, name, inputStream, + compressed); return buildResponse(uriInfo, repository); } @@ -193,6 +198,7 @@ public class RepositoryImportResource * @param type repository type * @param name name of the repository * @param inputStream input bundle + * @param compressed true if the bundle is gzip compressed * * @return empty response with location header which points to the imported * repository @@ -204,13 +210,14 @@ public class RepositoryImportResource @Produces(MediaType.TEXT_HTML) public Response importFromBundleUI(@PathParam("type") String type, @FormDataParam("name") String name, - @FormDataParam("bundle") InputStream inputStream) + @FormDataParam("bundle") InputStream inputStream, @QueryParam("compressed") + @DefaultValue("false") boolean compressed) { Response response; try { - doImportFromBundle(type, name, inputStream); + doImportFromBundle(type, name, inputStream, compressed); response = Response.ok(new RestActionUploadResult(true)).build(); } catch (WebApplicationException ex) @@ -569,11 +576,12 @@ public class RepositoryImportResource * @param type repository type * @param name name of the repository * @param inputStream bundle stream + * @param compressed true if the bundle is gzip compressed * * @return imported repository */ private Repository doImportFromBundle(String type, String name, - InputStream inputStream) + InputStream inputStream, boolean compressed) { SecurityUtils.getSubject().checkRole(Role.ADMIN); @@ -601,7 +609,7 @@ public class RepositoryImportResource logger.info("copied {} bytes to temp, start bundle import", length); service = serviceFactory.create(repository); - service.getUnbundleCommand().unbundle(file); + service.getUnbundleCommand().setCompressed(compressed).unbundle(file); } catch (RepositoryException ex) { diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.importwindow.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.importwindow.js index a60a93c73d..87a0380503 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.importwindow.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.importwindow.js @@ -91,6 +91,7 @@ Sonia.repository.ImportPanel = Ext.extend(Ext.Panel, { importFileNameHelpText: 'The name of the repository in SCM-Manager.', importFileHelpText: 'Choose the dump file you want to import to SCM-Manager.', + importFileGZipCompressedHelpText: 'The file is gzip compressed.', // tips tipRepositoryType: 'Choose your repository type for the import.', @@ -314,6 +315,11 @@ Sonia.repository.ImportPanel = Ext.extend(Ext.Panel, { buttonCfg: { iconCls: 'upload-icon' } + },{ + id: 'importFileGZipCompressed', + xtype: 'checkbox', + fieldLabel: 'GZip compressed', + helpText: this.importFileGZipCompressedHelpText },{ xtype: 'scmTip', content: 'Please insert name and upload the repository file.', @@ -479,9 +485,10 @@ Sonia.repository.ImportPanel = Ext.extend(Ext.Panel, { }, importFromFile: function(layout, form){ + var compressed = Ext.getCmp('importFileGZipCompressed').getValue(); var lbox = this.showLoadingBox(); form.submit({ - url: restUrl + 'import/repositories/' + this.repositoryType + '/bundle.html', + url: restUrl + 'import/repositories/' + this.repositoryType + '/bundle.html?compressed=' + compressed, scope: this, success: function(form){ lbox.hide();