James Moger
2014-07-03 efdb2b3d0c6f03a9aac9e65892cbc8ff755f246f
commit | author | age
59b817 1 /*
JM 2  * Copyright 2012 gitblit.com.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.gitblit.utils;
17
efdb2b 18 import java.io.ByteArrayOutputStream;
59b817 19 import java.io.IOException;
JM 20 import java.io.OutputStream;
21 import java.text.MessageFormat;
22 import java.util.ArrayList;
23 import java.util.List;
24
25 import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
26 import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
1d9ac5 27 import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
JM 28 import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
59b817 29 import org.apache.commons.compress.compressors.CompressorException;
JM 30 import org.apache.commons.compress.compressors.CompressorStreamFactory;
31 import org.eclipse.jgit.lib.Constants;
32 import org.eclipse.jgit.lib.FileMode;
1d9ac5 33 import org.eclipse.jgit.lib.MutableObjectId;
59b817 34 import org.eclipse.jgit.lib.ObjectLoader;
1d9ac5 35 import org.eclipse.jgit.lib.ObjectReader;
59b817 36 import org.eclipse.jgit.lib.Repository;
JM 37 import org.eclipse.jgit.revwalk.RevCommit;
38 import org.eclipse.jgit.revwalk.RevWalk;
39 import org.eclipse.jgit.treewalk.TreeWalk;
40 import org.eclipse.jgit.treewalk.filter.PathFilter;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 /**
45  * Collection of static methods for retrieving information from a repository.
699e71 46  *
59b817 47  * @author James Moger
699e71 48  *
59b817 49  */
JM 50 public class CompressionUtils {
51
52     static final Logger LOGGER = LoggerFactory.getLogger(CompressionUtils.class);
53
54     /**
55      * Log an error message and exception.
699e71 56      *
59b817 57      * @param t
JM 58      * @param repository
59      *            if repository is not null it MUST be the {0} parameter in the
60      *            pattern.
61      * @param pattern
62      * @param objects
63      */
64     private static void error(Throwable t, Repository repository, String pattern, Object... objects) {
65         List<Object> parameters = new ArrayList<Object>();
66         if (objects != null && objects.length > 0) {
67             for (Object o : objects) {
68                 parameters.add(o);
69             }
70         }
71         if (repository != null) {
72             parameters.add(0, repository.getDirectory().getAbsolutePath());
73         }
74         LOGGER.error(MessageFormat.format(pattern, parameters.toArray()), t);
75     }
76
77     /**
78      * Zips the contents of the tree at the (optionally) specified revision and
79      * the (optionally) specified basepath to the supplied outputstream.
699e71 80      *
59b817 81      * @param repository
JM 82      * @param basePath
83      *            if unspecified, entire repository is assumed.
84      * @param objectId
85      *            if unspecified, HEAD is assumed.
86      * @param os
87      * @return true if repository was successfully zipped to supplied output
88      *         stream
89      */
90     public static boolean zip(Repository repository, String basePath, String objectId,
91             OutputStream os) {
92         RevCommit commit = JGitUtils.getCommit(repository, objectId);
93         if (commit == null) {
94             return false;
95         }
96         boolean success = false;
97         RevWalk rw = new RevWalk(repository);
98         TreeWalk tw = new TreeWalk(repository);
99         try {
1d9ac5 100             tw.reset();
59b817 101             tw.addTree(commit.getTree());
1d9ac5 102             ZipArchiveOutputStream zos = new ZipArchiveOutputStream(os);
59b817 103             zos.setComment("Generated by Gitblit");
JM 104             if (!StringUtils.isEmpty(basePath)) {
105                 PathFilter f = PathFilter.create(basePath);
106                 tw.setFilter(f);
107             }
108             tw.setRecursive(true);
1d9ac5 109             MutableObjectId id = new MutableObjectId();
JM 110             ObjectReader reader = tw.getObjectReader();
111             long modified = commit.getAuthorIdent().getWhen().getTime();
59b817 112             while (tw.next()) {
1d9ac5 113                 FileMode mode = tw.getFileMode(0);
JM 114                 if (mode == FileMode.GITLINK || mode == FileMode.TREE) {
59b817 115                     continue;
JM 116                 }
1d9ac5 117                 tw.getObjectId(id, 0);
JM 118
119                 ZipArchiveEntry entry = new ZipArchiveEntry(tw.getPathString());
120                 entry.setSize(reader.getObjectSize(id, Constants.OBJ_BLOB));
59b817 121                 entry.setComment(commit.getName());
1d9ac5 122                 entry.setUnixMode(mode.getBits());
JM 123                 entry.setTime(modified);
124                 zos.putArchiveEntry(entry);
59b817 125
1d9ac5 126                 ObjectLoader ldr = repository.open(id);
JM 127                 ldr.copyTo(zos);
128                 zos.closeArchiveEntry();
59b817 129             }
JM 130             zos.finish();
131             success = true;
132         } catch (IOException e) {
133             error(e, repository, "{0} failed to zip files from commit {1}", commit.getName());
134         } finally {
135             tw.release();
136             rw.dispose();
137         }
138         return success;
139     }
699e71 140
59b817 141     /**
JM 142      * tar the contents of the tree at the (optionally) specified revision and
143      * the (optionally) specified basepath to the supplied outputstream.
699e71 144      *
59b817 145      * @param repository
JM 146      * @param basePath
147      *            if unspecified, entire repository is assumed.
148      * @param objectId
149      *            if unspecified, HEAD is assumed.
150      * @param os
151      * @return true if repository was successfully zipped to supplied output
152      *         stream
153      */
154     public static boolean tar(Repository repository, String basePath, String objectId,
155             OutputStream os) {
156         return tar(null, repository, basePath, objectId, os);
157     }
699e71 158
59b817 159     /**
JM 160      * tar.gz the contents of the tree at the (optionally) specified revision and
161      * the (optionally) specified basepath to the supplied outputstream.
699e71 162      *
59b817 163      * @param repository
JM 164      * @param basePath
165      *            if unspecified, entire repository is assumed.
166      * @param objectId
167      *            if unspecified, HEAD is assumed.
168      * @param os
169      * @return true if repository was successfully zipped to supplied output
170      *         stream
171      */
172     public static boolean gz(Repository repository, String basePath, String objectId,
173             OutputStream os) {
174         return tar(CompressorStreamFactory.GZIP, repository, basePath, objectId, os);
175     }
699e71 176
59b817 177     /**
JM 178      * tar.xz the contents of the tree at the (optionally) specified revision and
179      * the (optionally) specified basepath to the supplied outputstream.
699e71 180      *
59b817 181      * @param repository
JM 182      * @param basePath
183      *            if unspecified, entire repository is assumed.
184      * @param objectId
185      *            if unspecified, HEAD is assumed.
186      * @param os
187      * @return true if repository was successfully zipped to supplied output
188      *         stream
189      */
190     public static boolean xz(Repository repository, String basePath, String objectId,
191             OutputStream os) {
192         return tar(CompressorStreamFactory.XZ, repository, basePath, objectId, os);
193     }
699e71 194
59b817 195     /**
JM 196      * tar.bzip2 the contents of the tree at the (optionally) specified revision and
197      * the (optionally) specified basepath to the supplied outputstream.
699e71 198      *
59b817 199      * @param repository
JM 200      * @param basePath
201      *            if unspecified, entire repository is assumed.
202      * @param objectId
203      *            if unspecified, HEAD is assumed.
204      * @param os
205      * @return true if repository was successfully zipped to supplied output
206      *         stream
207      */
208     public static boolean bzip2(Repository repository, String basePath, String objectId,
209             OutputStream os) {
699e71 210
59b817 211         return tar(CompressorStreamFactory.BZIP2, repository, basePath, objectId, os);
JM 212     }
699e71 213
59b817 214     /**
JM 215      * Compresses/archives the contents of the tree at the (optionally)
216      * specified revision and the (optionally) specified basepath to the
217      * supplied outputstream.
699e71 218      *
59b817 219      * @param algorithm
JM 220      *            compression algorithm for tar (optional)
221      * @param repository
222      * @param basePath
223      *            if unspecified, entire repository is assumed.
224      * @param objectId
225      *            if unspecified, HEAD is assumed.
226      * @param os
227      * @return true if repository was successfully zipped to supplied output
228      *         stream
229      */
230     private static boolean tar(String algorithm, Repository repository, String basePath, String objectId,
231             OutputStream os) {
232         RevCommit commit = JGitUtils.getCommit(repository, objectId);
233         if (commit == null) {
234             return false;
235         }
699e71 236
59b817 237         OutputStream cos = os;
JM 238         if (!StringUtils.isEmpty(algorithm)) {
239             try {
240                 cos = new CompressorStreamFactory().createCompressorOutputStream(algorithm, os);
241             } catch (CompressorException e1) {
242                 error(e1, repository, "{0} failed to open {1} stream", algorithm);
243             }
244         }
245         boolean success = false;
246         RevWalk rw = new RevWalk(repository);
247         TreeWalk tw = new TreeWalk(repository);
248         try {
1d9ac5 249             tw.reset();
59b817 250             tw.addTree(commit.getTree());
JM 251             TarArchiveOutputStream tos = new TarArchiveOutputStream(cos);
252             tos.setAddPaxHeadersForNonAsciiNames(true);
253             tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
254             if (!StringUtils.isEmpty(basePath)) {
255                 PathFilter f = PathFilter.create(basePath);
256                 tw.setFilter(f);
257             }
258             tw.setRecursive(true);
1d9ac5 259             MutableObjectId id = new MutableObjectId();
JM 260             long modified = commit.getAuthorIdent().getWhen().getTime();
59b817 261             while (tw.next()) {
JM 262                 FileMode mode = tw.getFileMode(0);
1d9ac5 263                 if (mode == FileMode.GITLINK || mode == FileMode.TREE) {
59b817 264                     continue;
JM 265                 }
1d9ac5 266                 tw.getObjectId(id, 0);
699e71 267
8ee931 268                 ObjectLoader loader = repository.open(id);
JM 269                 if (FileMode.SYMLINK == mode) {
270                     TarArchiveEntry entry = new TarArchiveEntry(tw.getPathString(),TarArchiveEntry.LF_SYMLINK);
271                     ByteArrayOutputStream bos = new ByteArrayOutputStream();
272                     loader.copyTo(bos);
273                     entry.setLinkName(bos.toString());
274                     entry.setModTime(modified);
275                     tos.putArchiveEntry(entry);
276                     tos.closeArchiveEntry();
277                 } else {
278                     TarArchiveEntry entry = new TarArchiveEntry(tw.getPathString());
279                     entry.setMode(mode.getBits());
280                     entry.setModTime(modified);
281                     entry.setSize(loader.getSize());
699e71 282                     tos.putArchiveEntry(entry);
8ee931 283                     loader.copyTo(tos);
JM 284                     tos.closeArchiveEntry();
285                 }
59b817 286             }
JM 287             tos.finish();
288             tos.close();
289             cos.close();
290             success = true;
291         } catch (IOException e) {
292             error(e, repository, "{0} failed to {1} stream files from commit {2}", algorithm, commit.getName());
293         } finally {
294             tw.release();
295             rw.dispose();
296         }
297         return success;
298     }
299 }