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.TarEntry;
020    import org.apache.commons.compress.archivers.tar.TarInputStream;
021    import org.apache.commons.compress.archivers.tar.TarOutputStream;
022    import org.apache.commons.compress.bzip2.CBZip2OutputStream;
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            TarInputStream tis=null;
154            try {
155                    tis = new TarInputStream( IOUtil.toBufferedInputStream(tarFile.getInputStream()) ) ;     
156                    TarEntry entry;
157                    int mode;
158                    while ( ( entry = tis.getNextEntry()) != 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 extractZipOld(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(new ExtensionResourceFilter("tar"));
189                if(files==null) 
190                    throw new IOException("directory "+zipFile+" is empty");
191                extract(FORMAT_ZIP,files,targetDir);
192                return;
193            }
194            
195    //      read the zip file and build a query from its contents
196            TarInputStream tis=null;
197            try {
198                    tis = new TarInputStream( IOUtil.toBufferedInputStream(zipFile.getInputStream()) ) ;     
199                    TarEntry entry;
200                    int mode;
201                    while ( ( entry = tis.getNextEntry()) != null ) {
202                            //print.ln(entry);
203                            Resource target=targetDir.getRealResource(entry.getName());
204                        if(entry.isDirectory()) {
205                            target.mkdirs();
206                        }
207                        else {
208                            Resource parent=target.getParentResource();
209                            if(!parent.exists())parent.mkdirs();
210                            
211                            IOUtil.copy(tis,target,false);
212                        }
213                        target.setLastModified(entry.getModTime().getTime());
214                        mode=entry.getMode();
215                        if(mode>0)target.setMode(mode);
216                        //tis.closeEntry() ;
217                    }
218            }
219            finally {
220                    IOUtil.closeEL(tis);
221            }
222        }
223        
224        private static void extractZip(Resource zipFile, Resource targetDir) throws IOException {
225            if(!targetDir.exists() || !targetDir.isDirectory())
226                throw new IOException(targetDir+" is not a existing directory");
227            
228            if(!zipFile.exists())
229                throw new IOException(zipFile+" is not a existing file");
230            
231            if(zipFile.isDirectory()) {
232                    Resource[] files = zipFile.listResources(
233                        new OrResourceFilter(new ResourceFilter[]{
234                                new ExtensionResourceFilter("zip"),
235                                new ExtensionResourceFilter("jar"),
236                                new ExtensionResourceFilter("war"),
237                                new ExtensionResourceFilter("tar"),
238                                new ExtensionResourceFilter("ear")
239                        })
240                );
241                if(files==null) 
242                    throw new IOException("directory "+zipFile+" is empty");
243                extract(FORMAT_ZIP,files,targetDir);
244                return;
245            }
246            
247    //      read the zip file and build a query from its contents
248            unzip(zipFile,targetDir);
249            /*ZipInputStream zis=null;
250            try {
251                    zis = new ZipInputStream( IOUtil.toBufferedInputStream(zipFile.getInputStream()) ) ;     
252                    ZipEntry entry;
253                    while ( ( entry = zis.getNextEntry()) != null ) {
254                            Resource target=targetDir.getRealResource(entry.getName());
255                        if(entry.isDirectory()) {
256                            target.mkdirs();
257                        }
258                        else {
259                            Resource parent=target.getParentResource();
260                            if(!parent.exists())parent.mkdirs();
261                            
262                            IOUtil.copy(zis,target,false);
263                        }
264                        target.setLastModified(entry.getTime());
265                       zis.closeEntry() ;
266                    }
267            }
268            finally {
269                    IOUtil.closeEL(zis);
270            }*/
271        }
272        
273    
274        private static void unzip(Resource zipFile,Resource targetDir) throws IOException {
275            /*if(zipFile instanceof File){
276                    unzip((File)zipFile, targetDir);
277                    return;
278            }*/
279        
280            ZipInputStream zis=null;
281            try {
282                    zis = new ZipInputStream( IOUtil.toBufferedInputStream(zipFile.getInputStream()) ) ;     
283                    ZipEntry entry;
284                    while ( ( entry = zis.getNextEntry()) != null ) {
285                            Resource target=targetDir.getRealResource(entry.getName());
286                        if(entry.isDirectory()) {
287                            target.mkdirs();
288                        }
289                        else {
290                            Resource parent=target.getParentResource();
291                            if(!parent.exists())parent.mkdirs();
292                            IOUtil.copy(zis,target,false);
293                        }
294                        target.setLastModified(entry.getTime());
295                        zis.closeEntry() ;
296                    }
297            }
298            finally {
299                    IOUtil.closeEL(zis);
300            }
301            }
302        
303        private static void unzip2(File zipFile,Resource targetDir) throws IOException {
304            ZipFile zf=null;
305            try {
306                    zf = new ZipFile(zipFile);
307                    
308                    ZipEntry entry;
309                    Enumeration en = zf.entries();
310                    while(en.hasMoreElements()){
311                            entry = (ZipEntry) en.nextElement();
312                            Resource target=targetDir.getRealResource(entry.getName());
313                        if(entry.isDirectory()) {
314                            target.mkdirs();
315                        }
316                        else {
317                            Resource parent=target.getParentResource();
318                            if(!parent.exists())parent.mkdirs();
319                            InputStream is = zf.getInputStream(entry);
320                            IOUtil.copy(is,target,true);
321                        }
322                        target.setLastModified(entry.getTime());
323                    }
324            }
325            finally {
326                    IOUtil.closeEL(zf);
327            }
328            }
329    
330            /**
331         * compress data to a zip file
332         * @param format format it that should by compressed usally is CompressUtil.FORMAT_XYZ
333         * @param source
334         * @param target
335         * @param includeBaseFolder 
336         * @param mode 
337         * @throws IOException
338         */
339        public static void compress(int format, Resource source, Resource target, boolean includeBaseFolder, int mode) throws IOException {
340            if(     format==FORMAT_GZIP)    compressGZip(source,target);
341            else if(format==FORMAT_BZIP2)   compressBZip2(source,target);
342            else {
343                    Resource[] sources=(!includeBaseFolder && source.isDirectory())?source.listResources():new Resource[]{source};
344                compress(format, sources, target,mode);
345            }
346        }
347    
348        /**
349         * compress data to a zip file
350         * @param format format it that should by compressed usally is CompressUtil.FORMAT_XYZ
351         * @param sources
352         * @param target
353         * @param mode 
354         * @throws IOException
355         */
356        public static void compress(int format, Resource[] sources, Resource target, int mode) throws IOException {
357                    
358            if(     format==FORMAT_ZIP)     compressZip(sources,target,null);
359            else if(format==FORMAT_TAR)     compressTar(sources,target,mode);
360            else if(format==FORMAT_TGZ)     compressTGZ(sources,target,mode);
361            else if(format==FORMAT_TBZ2)    compressTBZ2(sources,target,mode);
362    
363            else throw new IOException("can't compress in given format");
364        }
365    
366        /**
367         * compress a source file/directory to a tar/gzip file
368         * @param sources
369         * @param target
370         * @param mode 
371         * @throws IOException
372         */
373        public static void compressTGZ(Resource[] sources, Resource target,int mode) throws IOException {
374            File tmpTarget = File.createTempFile("_temp","tmp");
375            try {
376                    // write Tar
377                    OutputStream tmpOs=new FileOutputStream(tmpTarget);
378                    try {
379                            compressTar(sources,tmpOs,mode);
380                    }
381                    finally {
382                            IOUtil.closeEL(tmpOs);
383                    }
384                    
385                    // write Gzip
386                    InputStream is = null;
387                    OutputStream os = null;
388                    try {
389                            is = new FileInputStream(tmpTarget);
390                            os = target.getOutputStream();
391                            compressGZip(is, os);
392                    }
393                    finally {
394                            IOUtil.closeEL(is,os);
395                    }
396            }
397            finally {
398                    tmpTarget.delete();
399            }
400        }
401        
402        /**
403         * compress a source file/directory to a tar/bzip2 file
404         * @param sources
405         * @param target
406         * @param mode 
407         * @throws IOException
408         */
409        private static void compressTBZ2(Resource[] sources, Resource target, int mode) throws IOException {
410            //File tmpTarget = File.createTempFile("_temp","tmp");
411            ByteArrayOutputStream baos=new ByteArrayOutputStream();
412            compressTar(sources,baos,mode);
413            _compressBZip2(new ByteArrayInputStream(baos.toByteArray()), target.getOutputStream());
414            //tmpTarget.delete();
415        }
416    
417        /**
418         * compress a source file to a gzip file
419         * @param source
420         * @param target
421         * @throws IOException 
422         * @throws IOException
423         */
424        private static void compressGZip(Resource source, Resource target) throws IOException {
425            if(source.isDirectory()) {
426                throw new IOException("you can only create a GZIP File from a single source file, use TGZ (TAR-GZIP) to first TAR multiple files");
427            }
428            InputStream is=null;
429            OutputStream os=null;
430                    try {
431                            is = source.getInputStream();
432                            os = target.getOutputStream();
433                    } catch(IOException ioe) {
434                            IOUtil.closeEL(is, os);
435                            throw ioe;
436                    }
437            compressGZip(is,os);
438            
439        }
440    
441        public static void compressGZip(InputStream source, OutputStream target) throws IOException {
442            InputStream is = IOUtil.toBufferedInputStream(source);
443            if(!(target instanceof GZIPOutputStream)) target = new GZIPOutputStream(IOUtil.toBufferedOutputStream(target));
444            IOUtil.copy(is,target,true,true);       
445        }
446    
447        /**
448         * compress a source file to a bzip2 file
449         * @param source
450         * @param target
451         * @throws IOException
452         */
453        private static void compressBZip2(Resource source, Resource target) throws IOException {
454            if(source.isDirectory()) {
455                throw new IOException("you can only create a BZIP File from a single source file, use TBZ (TAR-BZIP2) to first TAR multiple files");
456            }
457            InputStream is=null;
458            OutputStream os=null;
459            try {
460                    is=source.getInputStream();
461                    os=target.getOutputStream();
462            }
463            catch(IOException ioe) {
464                    IOUtil.closeEL(is, os);
465                    throw ioe;
466            }
467            
468            _compressBZip2(is, os);
469        }
470    
471        /**
472         * compress a source file to a bzip2 file
473         * @param source
474         * @param target
475         * @throws IOException
476         */
477        private static void _compressBZip2(InputStream source, OutputStream target) throws IOException {
478            
479            InputStream is = IOUtil.toBufferedInputStream(source);
480            OutputStream os = new CBZip2OutputStream(IOUtil.toBufferedOutputStream(target));
481            IOUtil.copy(is,os,true,true);
482        }
483        
484        /**
485         * compress a source file/directory to a zip file
486         * @param sources
487         * @param target
488         * @param filter 
489         * @throws IOException
490         */
491        public static void compressZip(Resource[] sources, Resource target, ResourceFilter filter) throws IOException {
492            ZipOutputStream zos = null;
493            try {
494                    zos = new ZipOutputStream(IOUtil.toBufferedOutputStream(target.getOutputStream()));
495                compressZip("",sources, zos, filter);
496            }
497            finally {
498                IOUtil.closeEL(zos);
499            }
500        }
501        
502        public static void compressZip( Resource[] sources, ZipOutputStream zos, ResourceFilter filter) throws IOException {
503            compressZip("",sources, zos, filter);
504        }
505        
506    
507        private static void compressZip(String parent, Resource[] sources, ZipOutputStream zos, ResourceFilter filter) throws IOException {
508            if(parent.length()>0)parent+="/";
509            for(int i=0;i<sources.length;i++) { 
510                compressZip(parent+sources[i].getName(),sources[i],zos,filter);
511            }
512        }
513        
514        private static void compressZip(String parent, Resource source, ZipOutputStream zos, ResourceFilter filter) throws IOException {
515            if(source.isFile()) {
516                    //if(filter.accept(source)) {
517                            ZipEntry ze=new ZipEntry(parent);
518                            ze.setTime(source.lastModified());
519                            zos.putNextEntry(ze);
520                        try {
521                            IOUtil.copy(source,zos,false);
522                        } 
523                        finally {
524                            zos.closeEntry();
525                        }
526                    //}
527            }
528            else if(source.isDirectory()) {
529                    if(!StringUtil.isEmpty(parent)) {
530                            ZipEntry ze=new ZipEntry(parent+"/");
531                            ze.setTime(source.lastModified());
532                            try {
533                                    zos.putNextEntry(ze);
534                            }
535                            catch(IOException ioe) {
536                                    if(Caster.toString(ioe.getMessage()).indexOf("duplicate")==-1)throw ioe;
537                            }
538                            zos.closeEntry();
539                    }
540                compressZip(parent, filter==null?source.listResources():source.listResources(filter),zos,filter);
541            }
542        }
543    
544        /**
545         * compress a source file/directory to a tar file
546         * @param sources
547         * @param target
548         * @param mode 
549         * @throws IOException
550         */
551        public static void compressTar(Resource[] sources,Resource target, int mode) throws IOException {
552            compressTar(sources, IOUtil.toBufferedOutputStream(target.getOutputStream()), mode);
553        }
554        
555        public static void compressTar(Resource[] sources,OutputStream target, int mode) throws IOException {
556            if(target instanceof TarOutputStream){
557                compressTar("",sources,(TarOutputStream)target,mode);
558                return;
559            }
560            TarOutputStream tos=new TarOutputStream(target);
561            tos.setLongFileMode(TarOutputStream.LONGFILE_GNU);
562            try {
563                compressTar("",sources, tos,mode);
564            }
565            finally {
566                    IOUtil.closeEL(tos);
567            }
568        }
569        
570        public static void compressTar(String parent, Resource[] sources, TarOutputStream tos, int mode) throws IOException {
571    
572            if(parent.length()>0)parent+="/";
573            for(int i=0;i<sources.length;i++) { 
574                compressTar(parent+sources[i].getName(),sources[i],tos,mode);
575            }
576        }
577        
578        private static void compressTar(String parent, Resource source,TarOutputStream tos, int mode) throws IOException {
579            if(source.isFile()) {
580                    //TarEntry entry = (source instanceof FileResource)?new TarEntry((FileResource)source):new TarEntry(parent);
581                    TarEntry entry = new TarEntry(parent);
582                
583                entry.setName(parent);
584                
585                // mode
586                //100777 TODO ist das so ok?
587                if(mode>0)       entry.setMode(mode);
588                else if((mode=source.getMode())>0)       entry.setMode(mode);
589                
590                entry.setSize(source.length());
591                entry.setModTime(source.lastModified());
592                tos.putNextEntry(entry);
593                try {
594                    IOUtil.copy(source,tos,false);
595                } 
596                finally {
597                            tos.closeEntry();
598                }
599            }
600            else if(source.isDirectory()) {
601                compressTar(parent, source.listResources(),tos,mode);
602            }
603        }
604    
605        public static void main(String[] args) throws IOException {
606            ResourceProvider frp = ResourcesImpl.getFileResourceProvider();
607            Resource src = frp.getResource("/Users/mic/temp/a");
608            
609            Resource tgz = frp.getResource("/Users/mic/temp/b/a.tgz");
610            tgz.getParentResource().mkdirs();
611            Resource tar = frp.getResource("/Users/mic/temp/b/a.tar");
612            tar.getParentResource().mkdirs();
613            Resource zip = frp.getResource("/Users/mic/temp/b/a.zip");
614            zip.getParentResource().mkdirs();
615            
616            Resource tgz1 = frp.getResource("/Users/mic/temp/b/tgz");
617            tgz1.mkdirs();
618            Resource tar1 = frp.getResource("/Users/mic/temp/b/tar");
619            tar1.mkdirs();
620            Resource zip1 = frp.getResource("/Users/mic/temp/b/zip");
621            zip1.mkdirs();
622            
623            compressTGZ(new Resource[]{src}, tgz, -1);
624            compressTar(new Resource[]{src}, tar, -1);
625            compressZip(new Resource[]{src}, zip, null);
626            
627            extractTGZ(tgz, tgz1);
628            extractTar(tar, tar1);
629            extractZip(src, zip1);
630            
631            }
632    }