001    package railo.commons.io;
002    
003    import java.io.ByteArrayInputStream;
004    import java.io.ByteArrayOutputStream;
005    import java.io.File;
006    import java.io.FileInputStream;
007    import java.io.FileOutputStream;
008    import java.io.IOException;
009    import java.io.InputStream;
010    import java.io.OutputStream;
011    import java.util.Enumeration;
012    import java.util.zip.GZIPInputStream;
013    import java.util.zip.GZIPOutputStream;
014    import java.util.zip.ZipEntry;
015    import java.util.zip.ZipFile;
016    import java.util.zip.ZipInputStream;
017    import java.util.zip.ZipOutputStream;
018    
019    import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
020    import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
021    import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
022    import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream;
023    
024    import railo.commons.io.res.Resource;
025    import railo.commons.io.res.ResourceProvider;
026    import railo.commons.io.res.ResourcesImpl;
027    import railo.commons.io.res.filter.ExtensionResourceFilter;
028    import railo.commons.io.res.filter.OrResourceFilter;
029    import railo.commons.io.res.filter.ResourceFilter;
030    import railo.commons.lang.StringUtil;
031    import railo.runtime.op.Caster;
032    
033    /**
034     * Util to manipulate zip files
035     */
036    public final class CompressUtil {
037    
038        /**
039         * Field <code>FORMAT_ZIP</code>
040         */
041        public static final int FORMAT_ZIP=0;
042        /**
043         * Field <code>FORMAT_TAR</code>
044         */
045        public static final int FORMAT_TAR=1;
046        /**
047         * Field <code>FORMAT_TGZ</code>
048         */
049        public static final int FORMAT_TGZ=2;
050        /**
051         * Field <code>FORMAT_GZIP</code>
052         */
053        public static final int FORMAT_GZIP=3;
054        /**
055         * Field <code>FORMAT_BZIP</code>
056         */
057        public static final int FORMAT_BZIP=4;
058        /**
059         * Field <code>FORMAT_BZIP</code>
060         */
061        public static final int FORMAT_BZIP2=4;
062    
063        /**
064         * Field <code>FORMAT_TBZ</code>
065         */
066        public static final int FORMAT_TBZ=5;
067        /**
068         * Field <code>FORMAT_TBZ2</code>
069         */
070        public static final int FORMAT_TBZ2=5;
071        
072        /**
073         * Constructor of the class
074         */
075        private CompressUtil(){}
076    
077        /**
078         * extract a zip file to a directory
079         * @param format 
080         * @param source 
081         * @param target 
082         * @throws IOException
083        */
084        public static void extract(int format,Resource source, Resource target) throws IOException {
085            if(format==FORMAT_ZIP)          extractZip(source,target);
086            else if(format==FORMAT_TAR)     extractTar(source,target);
087            else if(format==FORMAT_GZIP)extractGZip(source,target);
088            else if(format==FORMAT_TGZ)     extractTGZ(source,target);
089            else throw new IOException("can't extract in given format");
090        }
091        
092    
093        private static void extractTGZ(Resource source, Resource target) throws IOException {
094            //File tmpTarget = File.createTempFile("_temp","tmp");
095            Resource tmp = SystemUtil.getTempDirectory().getRealResource(System.currentTimeMillis()+".tmp");
096            try {
097                    // read Gzip
098                    extractGZip(source, tmp);
099                    
100                    // read Tar
101                    extractTar(tmp, target);
102            }
103            finally {
104                    tmp.delete();
105            }
106            }
107    
108            private static void extractGZip(Resource source, Resource target) throws IOException {
109            InputStream is=null;
110            OutputStream os=null;
111                    try {
112                            is = new GZIPInputStream(IOUtil.toBufferedInputStream(source.getInputStream()));
113                            os = IOUtil.toBufferedOutputStream(target.getOutputStream());
114                            IOUtil.copy(is,os,false,false);
115                    } 
116                    finally {
117                            IOUtil.closeEL(is, os);
118                    }
119            }
120    
121            /**
122         * extract a zip file to a directory
123         * @param format 
124         * @param sources
125         * @param target 
126         * @throws IOException
127        */
128        public static void extract(int format,Resource[] sources, Resource target) throws IOException {
129            if(format==FORMAT_ZIP  || format==FORMAT_TAR) {
130                for(int i=0;i<sources.length;i++) {
131                    extract(format,sources[i],target);
132                }
133            }
134            else throw new IOException("can't extract in given format");
135        }
136        
137        private static void extractTar(Resource tarFile, Resource targetDir) throws IOException {
138            if(!targetDir.exists() || !targetDir.isDirectory())
139                throw new IOException(targetDir+" is not a existing directory");
140            
141            if(!tarFile.exists())
142                throw new IOException(tarFile+" is not a existing file");
143            
144            if(tarFile.isDirectory()) {
145                    Resource[] files = tarFile.listResources(new ExtensionResourceFilter("tar"));
146                if(files==null) 
147                    throw new IOException("directory "+tarFile+" is empty");
148                extract(FORMAT_TAR,files,targetDir);
149                return;
150            }
151            
152    //      read the zip file and build a query from its contents
153            TarArchiveInputStream tis=null;
154            try {
155                    tis = new TarArchiveInputStream( IOUtil.toBufferedInputStream(tarFile.getInputStream()) ) ;     
156                    TarArchiveEntry entry;
157                    int mode;
158                    while ( ( entry = tis.getNextTarEntry()) != null ) {
159                            //print.ln(entry);
160                            Resource target=targetDir.getRealResource(entry.getName());
161                        if(entry.isDirectory()) {
162                            target.mkdirs();
163                        }
164                        else {
165                            Resource parent=target.getParentResource();
166                            if(!parent.exists())parent.mkdirs();
167                            IOUtil.copy(tis,target,false);
168                        }
169                        target.setLastModified(entry.getModTime().getTime());
170                        mode=entry.getMode();
171                        if(mode>0)target.setMode(mode);
172                        //tis.closeEntry() ;
173                    }
174            }
175            finally {
176                    IOUtil.closeEL(tis);
177            }
178        }
179        
180        private static void extractZip(Resource zipFile, Resource targetDir) throws IOException {
181            if(!targetDir.exists() || !targetDir.isDirectory())
182                throw new IOException(targetDir+" is not a existing directory");
183            
184            if(!zipFile.exists())
185                throw new IOException(zipFile+" is not a existing file");
186            
187            if(zipFile.isDirectory()) {
188                    Resource[] files = zipFile.listResources(
189                        new OrResourceFilter(new ResourceFilter[]{
190                                new ExtensionResourceFilter("zip"),
191                                new ExtensionResourceFilter("jar"),
192                                new ExtensionResourceFilter("war"),
193                                new ExtensionResourceFilter("tar"),
194                                new ExtensionResourceFilter("ear")
195                        })
196                );
197                if(files==null) 
198                    throw new IOException("directory "+zipFile+" is empty");
199                extract(FORMAT_ZIP,files,targetDir);
200                return;
201            }
202            
203    //      read the zip file and build a query from its contents
204            unzip(zipFile,targetDir);
205            /*ZipInputStream zis=null;
206            try {
207                    zis = new ZipInputStream( IOUtil.toBufferedInputStream(zipFile.getInputStream()) ) ;     
208                    ZipEntry entry;
209                    while ( ( entry = zis.getNextEntry()) != null ) {
210                            Resource target=targetDir.getRealResource(entry.getName());
211                        if(entry.isDirectory()) {
212                            target.mkdirs();
213                        }
214                        else {
215                            Resource parent=target.getParentResource();
216                            if(!parent.exists())parent.mkdirs();
217                            
218                            IOUtil.copy(zis,target,false);
219                        }
220                        target.setLastModified(entry.getTime());
221                       zis.closeEntry() ;
222                    }
223            }
224            finally {
225                    IOUtil.closeEL(zis);
226            }*/
227        }
228        
229    
230        private static void unzip(Resource zipFile,Resource targetDir) throws IOException {
231            /*if(zipFile instanceof File){
232                    unzip((File)zipFile, targetDir);
233                    return;
234            }*/
235        
236            ZipInputStream zis=null;
237            try {
238                    zis = new ZipInputStream( IOUtil.toBufferedInputStream(zipFile.getInputStream()) ) ;     
239                    ZipEntry entry;
240                    while ( ( entry = zis.getNextEntry()) != null ) {
241                            Resource target=targetDir.getRealResource(entry.getName());
242                        if(entry.isDirectory()) {
243                            target.mkdirs();
244                        }
245                        else {
246                            Resource parent=target.getParentResource();
247                            if(!parent.exists())parent.mkdirs();
248                            IOUtil.copy(zis,target,false);
249                        }
250                        target.setLastModified(entry.getTime());
251                        zis.closeEntry() ;
252                    }
253            }
254            finally {
255                    IOUtil.closeEL(zis);
256            }
257            }
258        
259        private static void unzip2(File zipFile,Resource targetDir) throws IOException {
260            ZipFile zf=null;
261            try {
262                    zf = new ZipFile(zipFile);
263                    
264                    ZipEntry entry;
265                    Enumeration en = zf.entries();
266                    while(en.hasMoreElements()){
267                            entry = (ZipEntry) en.nextElement();
268                            Resource target=targetDir.getRealResource(entry.getName());
269                        if(entry.isDirectory()) {
270                            target.mkdirs();
271                        }
272                        else {
273                            Resource parent=target.getParentResource();
274                            if(!parent.exists())parent.mkdirs();
275                            InputStream is = zf.getInputStream(entry);
276                            IOUtil.copy(is,target,true);
277                        }
278                        target.setLastModified(entry.getTime());
279                    }
280            }
281            finally {
282                    IOUtil.closeEL(zf);
283            }
284            }
285    
286            /**
287         * compress data to a zip file
288         * @param format format it that should by compressed usally is CompressUtil.FORMAT_XYZ
289         * @param source
290         * @param target
291         * @param includeBaseFolder 
292         * @param mode 
293         * @throws IOException
294         */
295        public static void compress(int format, Resource source, Resource target, boolean includeBaseFolder, int mode) throws IOException {
296            if(     format==FORMAT_GZIP)    compressGZip(source,target);
297            else if(format==FORMAT_BZIP2)   compressBZip2(source,target);
298            else {
299                    Resource[] sources=(!includeBaseFolder && source.isDirectory())?source.listResources():new Resource[]{source};
300                compress(format, sources, target,mode);
301            }
302        }
303    
304        /**
305         * compress data to a zip file
306         * @param format format it that should by compressed usally is CompressUtil.FORMAT_XYZ
307         * @param sources
308         * @param target
309         * @param mode 
310         * @throws IOException
311         */
312        public static void compress(int format, Resource[] sources, Resource target, int mode) throws IOException {
313                    
314            if(     format==FORMAT_ZIP)     compressZip(sources,target,null);
315            else if(format==FORMAT_TAR)     compressTar(sources,target,mode);
316            else if(format==FORMAT_TGZ)     compressTGZ(sources,target,mode);
317            else if(format==FORMAT_TBZ2)    compressTBZ2(sources,target,mode);
318    
319            else throw new IOException("can't compress in given format");
320        }
321    
322        /**
323         * compress a source file/directory to a tar/gzip file
324         * @param sources
325         * @param target
326         * @param mode 
327         * @throws IOException
328         */
329        public static void compressTGZ(Resource[] sources, Resource target,int mode) throws IOException {
330            File tmpTarget = File.createTempFile("_temp","tmp");
331            try {
332                    // write Tar
333                    OutputStream tmpOs=new FileOutputStream(tmpTarget);
334                    try {
335                            compressTar(sources,tmpOs,mode);
336                    }
337                    finally {
338                            IOUtil.closeEL(tmpOs);
339                    }
340                    
341                    // write Gzip
342                    InputStream is = null;
343                    OutputStream os = null;
344                    try {
345                            is = new FileInputStream(tmpTarget);
346                            os = target.getOutputStream();
347                            compressGZip(is, os);
348                    }
349                    finally {
350                            IOUtil.closeEL(is,os);
351                    }
352            }
353            finally {
354                    tmpTarget.delete();
355            }
356        }
357        
358        /**
359         * compress a source file/directory to a tar/bzip2 file
360         * @param sources
361         * @param target
362         * @param mode 
363         * @throws IOException
364         */
365        private static void compressTBZ2(Resource[] sources, Resource target, int mode) throws IOException {
366            //File tmpTarget = File.createTempFile("_temp","tmp");
367            ByteArrayOutputStream baos=new ByteArrayOutputStream();
368            compressTar(sources,baos,mode);
369            _compressBZip2(new ByteArrayInputStream(baos.toByteArray()), target.getOutputStream());
370            //tmpTarget.delete();
371        }
372    
373        /**
374         * compress a source file to a gzip file
375         * @param source
376         * @param target
377         * @throws IOException 
378         * @throws IOException
379         */
380        private static void compressGZip(Resource source, Resource target) throws IOException {
381            if(source.isDirectory()) {
382                throw new IOException("you can only create a GZIP File from a single source file, use TGZ (TAR-GZIP) to first TAR multiple files");
383            }
384            InputStream is=null;
385            OutputStream os=null;
386                    try {
387                            is = source.getInputStream();
388                            os = target.getOutputStream();
389                    } catch(IOException ioe) {
390                            IOUtil.closeEL(is, os);
391                            throw ioe;
392                    }
393            compressGZip(is,os);
394            
395        }
396    
397        public static void compressGZip(InputStream source, OutputStream target) throws IOException {
398            InputStream is = IOUtil.toBufferedInputStream(source);
399            if(!(target instanceof GZIPOutputStream)) target = new GZIPOutputStream(IOUtil.toBufferedOutputStream(target));
400            IOUtil.copy(is,target,true,true);       
401        }
402    
403        /**
404         * compress a source file to a bzip2 file
405         * @param source
406         * @param target
407         * @throws IOException
408         */
409        private static void compressBZip2(Resource source, Resource target) throws IOException {
410            if(source.isDirectory()) {
411                throw new IOException("you can only create a BZIP File from a single source file, use TBZ (TAR-BZIP2) to first TAR multiple files");
412            }
413            InputStream is=null;
414            OutputStream os=null;
415            try {
416                    is=source.getInputStream();
417                    os=target.getOutputStream();
418            }
419            catch(IOException ioe) {
420                    IOUtil.closeEL(is, os);
421                    throw ioe;
422            }
423            
424            _compressBZip2(is, os);
425        }
426    
427        /**
428         * compress a source file to a bzip2 file
429         * @param source
430         * @param target
431         * @throws IOException
432         */
433        private static void _compressBZip2(InputStream source, OutputStream target) throws IOException {
434            
435            InputStream is = IOUtil.toBufferedInputStream(source);
436            OutputStream os = new BZip2CompressorOutputStream(IOUtil.toBufferedOutputStream(target));
437            IOUtil.copy(is,os,true,true);
438        }
439        
440        /**
441         * compress a source file/directory to a zip file
442         * @param sources
443         * @param target
444         * @param filter 
445         * @throws IOException
446         */
447        public static void compressZip(Resource[] sources, Resource target, ResourceFilter filter) throws IOException {
448            ZipOutputStream zos = null;
449            try {
450                    zos = new ZipOutputStream(IOUtil.toBufferedOutputStream(target.getOutputStream()));
451                compressZip("",sources, zos, filter);
452            }
453            finally {
454                IOUtil.closeEL(zos);
455            }
456        }
457        
458        public static void compressZip( Resource[] sources, ZipOutputStream zos, ResourceFilter filter) throws IOException {
459            compressZip("",sources, zos, filter);
460        }
461        
462    
463        private static void compressZip(String parent, Resource[] sources, ZipOutputStream zos, ResourceFilter filter) throws IOException {
464            if(parent.length()>0)parent+="/";
465            for(int i=0;i<sources.length;i++) { 
466                compressZip(parent+sources[i].getName(),sources[i],zos,filter);
467            }
468        }
469        
470        private static void compressZip(String parent, Resource source, ZipOutputStream zos, ResourceFilter filter) throws IOException {
471            if(source.isFile()) {
472                    //if(filter.accept(source)) {
473                            ZipEntry ze=new ZipEntry(parent);
474                            ze.setTime(source.lastModified());
475                            zos.putNextEntry(ze);
476                        try {
477                            IOUtil.copy(source,zos,false);
478                        } 
479                        finally {
480                            zos.closeEntry();
481                        }
482                    //}
483            }
484            else if(source.isDirectory()) {
485                    if(!StringUtil.isEmpty(parent)) {
486                            ZipEntry ze=new ZipEntry(parent+"/");
487                            ze.setTime(source.lastModified());
488                            try {
489                                    zos.putNextEntry(ze);
490                            }
491                            catch(IOException ioe) {
492                                    if(Caster.toString(ioe.getMessage()).indexOf("duplicate")==-1)throw ioe;
493                            }
494                            zos.closeEntry();
495                    }
496                compressZip(parent, filter==null?source.listResources():source.listResources(filter),zos,filter);
497            }
498        }
499    
500        /**
501         * compress a source file/directory to a tar file
502         * @param sources
503         * @param target
504         * @param mode 
505         * @throws IOException
506         */
507        public static void compressTar(Resource[] sources,Resource target, int mode) throws IOException {
508            compressTar(sources, IOUtil.toBufferedOutputStream(target.getOutputStream()), mode);
509        }
510        
511        public static void compressTar(Resource[] sources,OutputStream target, int mode) throws IOException {
512            if(target instanceof TarArchiveOutputStream){
513                compressTar("",sources,(TarArchiveOutputStream)target,mode);
514                return;
515            }
516            TarArchiveOutputStream tos=new TarArchiveOutputStream(target);
517            tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
518            try {
519                compressTar("",sources, tos,mode);
520            }
521            finally {
522                    IOUtil.closeEL(tos);
523            }
524        }
525        
526        public static void compressTar(String parent, Resource[] sources, TarArchiveOutputStream tos, int mode) throws IOException {
527    
528            if(parent.length()>0)parent+="/";
529            for(int i=0;i<sources.length;i++) { 
530                compressTar(parent+sources[i].getName(),sources[i],tos,mode);
531            }
532        }
533        
534        private static void compressTar(String parent, Resource source,TarArchiveOutputStream tos, int mode) throws IOException {
535            if(source.isFile()) {
536                    //TarEntry entry = (source instanceof FileResource)?new TarEntry((FileResource)source):new TarEntry(parent);
537                    TarArchiveEntry entry = new TarArchiveEntry(parent);
538                
539                entry.setName(parent);
540                
541                // mode
542                //100777 TODO ist das so ok?
543                if(mode>0)       entry.setMode(mode);
544                else if((mode=source.getMode())>0)       entry.setMode(mode);
545                
546                entry.setSize(source.length());
547                entry.setModTime(source.lastModified());
548                tos.putArchiveEntry(entry);
549                try {
550                    IOUtil.copy(source,tos,false);
551                } 
552                finally {
553                            tos.closeArchiveEntry();
554                }
555            }
556            else if(source.isDirectory()) {
557                compressTar(parent, source.listResources(),tos,mode);
558            }
559        }
560    
561        public static void main(String[] args) throws IOException {
562            ResourceProvider frp = ResourcesImpl.getFileResourceProvider();
563            Resource src = frp.getResource("/Users/mic/temp/a");
564            
565            Resource tgz = frp.getResource("/Users/mic/temp/b/a.tgz");
566            tgz.getParentResource().mkdirs();
567            Resource tar = frp.getResource("/Users/mic/temp/b/a.tar");
568            tar.getParentResource().mkdirs();
569            Resource zip = frp.getResource("/Users/mic/temp/b/a.zip");
570            zip.getParentResource().mkdirs();
571            
572            Resource tgz1 = frp.getResource("/Users/mic/temp/b/tgz");
573            tgz1.mkdirs();
574            Resource tar1 = frp.getResource("/Users/mic/temp/b/tar");
575            tar1.mkdirs();
576            Resource zip1 = frp.getResource("/Users/mic/temp/b/zip");
577            zip1.mkdirs();
578            
579            compressTGZ(new Resource[]{src}, tgz, -1);
580            compressTar(new Resource[]{src}, tar, -1);
581            compressZip(new Resource[]{src}, zip, null);
582            
583            extractTGZ(tgz, tgz1);
584            extractTar(tar, tar1);
585            extractZip(src, zip1);
586            
587            }
588    }