001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    package org.apache.geronimo.deployment.util;
018    
019    import java.io.BufferedOutputStream;
020    import java.io.File;
021    import java.io.FileInputStream;
022    import java.io.FileNotFoundException;
023    import java.io.FileOutputStream;
024    import java.io.IOException;
025    import java.io.InputStream;
026    import java.io.InputStreamReader;
027    import java.io.OutputStream;
028    import java.io.Reader;
029    import java.io.Writer;
030    import java.net.MalformedURLException;
031    import java.net.URL;
032    import java.util.ArrayList;
033    import java.util.Collection;
034    import java.util.Collections;
035    import java.util.Enumeration;
036    import java.util.jar.JarEntry;
037    import java.util.jar.JarFile;
038    import java.util.jar.JarOutputStream;
039    import java.util.jar.Manifest;
040    import java.util.zip.ZipEntry;
041    import java.util.zip.ZipFile;
042    
043    /**
044     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
045     */
046    public final class DeploymentUtil {
047        private DeploymentUtil() {
048        }
049    
050        public static final File DUMMY_JAR_FILE;
051        private static final boolean jarUrlRewrite;
052        static {
053            jarUrlRewrite = new Boolean(System.getProperty("org.apache.geronimo.deployment.util.DeploymentUtil.jarUrlRewrite", "false"));
054            try {
055                DUMMY_JAR_FILE = DeploymentUtil.createTempFile();
056                new JarOutputStream(new FileOutputStream(DeploymentUtil.DUMMY_JAR_FILE), new Manifest()).close();
057            } catch (IOException e) {
058                throw new ExceptionInInitializerError(e);
059            }
060        }
061    
062        // be careful to clean up the temp directory
063        public static File createTempDir() throws IOException {
064            File tempDir = File.createTempFile("geronimo-deploymentUtil", ".tmpdir");
065            tempDir.delete();
066            tempDir.mkdirs();
067            return tempDir;
068        }
069    
070        // be careful to clean up the temp file... we tell the vm to delete this on exit
071        // but VMs can't be trusted to acutally delete the file
072        public static File createTempFile() throws IOException {
073            File tempFile = File.createTempFile("geronimo-deploymentUtil", ".tmpdir");
074            tempFile.deleteOnExit();
075            return tempFile;
076        }
077        
078        // be careful to clean up the temp file... we tell the vm to delete this on exit
079        // but VMs can't be trusted to acutally delete the file
080        private static File createTempFile(String extension) throws IOException {
081            File tempFile = File.createTempFile("geronimo-deploymentUtil", extension == null? ".tmpdir": extension);
082            tempFile.deleteOnExit();
083            return tempFile;
084        }
085    
086    
087        public static void copyFile(File source, File destination) throws IOException {
088            File destinationDir = destination.getParentFile();
089            if (!destinationDir.exists() && !destinationDir.mkdirs()) {
090                throw new java.io.IOException("Cannot create directory : " + destinationDir);
091            }
092            
093            InputStream in = null;
094            OutputStream out = null;
095            try {
096                in = new FileInputStream(source);
097                out = new FileOutputStream(destination);
098                writeAll(in, out);
099            } finally {
100                close(in);
101                close(out);
102            }
103        }
104    
105        private static void writeAll(InputStream in, OutputStream out) throws IOException {
106            byte[] buffer = new byte[4096];
107            int count;
108            while ((count = in.read(buffer)) > 0) {
109                out.write(buffer, 0, count);
110            }
111            out.flush();
112        }
113        public static File toTempFile(JarFile jarFile, String path) throws IOException {
114            return toTempFile(createJarURL(jarFile, path));
115        }
116    
117        public static File toTempFile(URL url) throws IOException {
118            InputStream in = null;
119            OutputStream out = null;
120            JarFile jarFile = null;
121            try {
122                if(url.getProtocol().equalsIgnoreCase("jar")) {
123                    // url.openStream() locks the jar file and does not release the lock even after the stream is closed.
124                    // This problem is avoided by using JarFile APIs.
125                    File file = new File(url.getFile().substring(5, url.getFile().indexOf("!/")));
126                    String path = url.getFile().substring(url.getFile().indexOf("!/")+2);
127                    jarFile = new JarFile(file);
128                    JarEntry jarEntry = jarFile.getJarEntry(path);
129                    if(jarEntry != null) {
130                        in = jarFile.getInputStream(jarEntry);
131                    } else {
132                        throw new FileNotFoundException("JarEntry "+path+" not found in "+file);
133                    }
134                } else {
135                    in = url.openStream();
136                }
137                int index = url.getPath().lastIndexOf(".");
138                String extension = null;
139                if (index > 0) {
140                    extension = url.getPath().substring(index);
141                }
142                File tempFile = createTempFile(extension);
143    
144                out = new FileOutputStream(tempFile);
145    
146                writeAll(in, out);
147                return tempFile;
148            } finally {
149                close(out);
150                close(in);
151                close(jarFile);
152            }
153        }
154    
155        public static String readAll(URL url) throws IOException {
156            Reader reader = null;
157            JarFile jarFile = null;
158            try {
159                if(url.getProtocol().equalsIgnoreCase("jar")) {
160                    // url.openStream() locks the jar file and does not release the lock even after the stream is closed.
161                    // This problem is avoided by using JarFile APIs.
162                    File file = new File(url.getFile().substring(5, url.getFile().indexOf("!/")));
163                    String path = url.getFile().substring(url.getFile().indexOf("!/")+2);
164                    jarFile = new JarFile(file);
165                    JarEntry jarEntry = jarFile.getJarEntry(path);
166                    if(jarEntry != null) {
167                        reader = new InputStreamReader(jarFile.getInputStream(jarEntry));
168                    } else {
169                        throw new FileNotFoundException("JarEntry "+path+" not found in "+file);
170                    }
171                } else {
172                    reader = new InputStreamReader(url.openStream());
173                }
174                char[] buffer = new char[4000];
175                StringBuffer out = new StringBuffer();
176                for(int count = reader.read(buffer); count >= 0; count = reader.read(buffer)) {
177                    out.append(buffer, 0, count);
178                }
179                return out.toString();
180            } finally {
181                close(reader);
182                close(jarFile);
183            }
184        }
185    
186        public static File toFile(JarFile jarFile) throws IOException {
187            if (jarFile instanceof UnpackedJarFile) {
188                return ((UnpackedJarFile) jarFile).getBaseDir();
189            } else {
190                    throw new IOException("jarFile is not a directory");
191            }
192        }
193    
194        // be careful with this method as it can leave a temp lying around
195        public static File toFile(JarFile jarFile, String path) throws IOException {
196            if (jarFile instanceof UnpackedJarFile) {
197                File baseDir = ((UnpackedJarFile) jarFile).getBaseDir();
198                File file = new File(baseDir, path);
199                if (!file.isFile()) {
200                    throw new IOException("No such file: " + file.getAbsolutePath());
201                }
202                return file;
203            } else {
204                String urlString = "jar:" + new File(jarFile.getName()).toURL() + "!/" + path;
205                return toTempFile(new URL(urlString));
206            }
207        }
208    
209        public static URL createJarURL(JarFile jarFile, String path) throws MalformedURLException {
210            if (jarFile instanceof NestedJarFile) {
211                NestedJarFile nestedJar = (NestedJarFile) jarFile;
212                if (nestedJar.isUnpacked()) {
213                    JarFile baseJar = nestedJar.getBaseJar();
214                    String basePath = nestedJar.getBasePath();
215                    if (baseJar instanceof UnpackedJarFile) {
216                        File baseDir = ((UnpackedJarFile) baseJar).getBaseDir();
217                        baseDir = new File(baseDir, basePath);
218                        return new File(baseDir, path).toURL();
219                    }
220                }
221            }
222            
223            if (jarFile instanceof UnpackedJarFile) {
224                File baseDir = ((UnpackedJarFile) jarFile).getBaseDir();
225                return new File(baseDir, path).toURL();
226            } else {
227                String urlString = "jar:" + new File(jarFile.getName()).toURL() + "!/" + path;
228                if(jarUrlRewrite) {
229                    // To prevent the lockout of archive, instead of returning a jar url, write the content to a
230                    // temp file and return the url of that file.
231                    File tempFile = null;
232                    try {
233                        tempFile = toTempFile(new URL(urlString));
234                    } catch (IOException e) {
235                        // The JarEntry does not exist!
236                        // Return url of a file that does not exist.
237                        try {
238                            tempFile = createTempFile();
239                            tempFile.delete();
240                        } catch (IOException ignored) {
241                        }
242                     }
243                    return tempFile.toURL();
244                } else {
245                    return new URL(urlString);
246                }
247            }
248        }
249    
250        public static JarFile createJarFile(File jarFile) throws IOException {
251            if (jarFile.isDirectory()) {
252                return new UnpackedJarFile(jarFile);
253            } else {
254                return new JarFile(jarFile);
255            }
256        }
257    
258        public static void copyToPackedJar(JarFile inputJar, File outputFile) throws IOException {
259            if (inputJar.getClass() == JarFile.class) {
260                // this is a plain old jar... nothign special
261                copyFile(new File(inputJar.getName()), outputFile);
262            } else if (inputJar instanceof NestedJarFile && ((NestedJarFile)inputJar).isPacked()) {
263                NestedJarFile nestedJarFile = (NestedJarFile)inputJar;
264                JarFile baseJar = nestedJarFile.getBaseJar();
265                String basePath = nestedJarFile.getBasePath();
266                if (baseJar instanceof UnpackedJarFile) {
267                    // our target jar is just a file in upacked jar (a plain old directory)... now
268                    // we just need to find where it is and copy it to the outptu
269                    copyFile(((UnpackedJarFile)baseJar).getFile(basePath), outputFile);
270                } else {
271                    // out target is just a plain old jar file directly accessabel from the file system
272                    copyFile(new File(baseJar.getName()), outputFile);
273                }
274            } else {
275                // copy out the module contents to a standalone jar file (entry by entry)
276                JarOutputStream out = null;
277                try {
278                    out = new JarOutputStream(new FileOutputStream(outputFile));
279                    byte[] buffer = new byte[4096];
280                    Enumeration entries = inputJar.entries();
281                    while (entries.hasMoreElements()) {
282                        ZipEntry entry = (ZipEntry) entries.nextElement();
283                        InputStream in = inputJar.getInputStream(entry);
284                        try {
285                            out.putNextEntry(new ZipEntry(entry.getName()));
286                            try {
287                                int count;
288                                while ((count = in.read(buffer)) > 0) {
289                                    out.write(buffer, 0, count);
290                                }
291                            } finally {
292                                out.closeEntry();
293                            }
294                        } finally {
295                            close(in);
296                        }
297                    }
298                } finally {
299                    close(out);
300                }
301            }
302        }
303    
304        public static void jarDirectory(File sourceDirecotry, File destinationFile) throws IOException {
305            JarFile inputJar = new UnpackedJarFile(sourceDirecotry);
306            try {
307                copyToPackedJar(inputJar, destinationFile);
308            } finally {
309                close(inputJar);
310            }
311        }
312    
313        private static void createDirectory(File dir) throws IOException {
314            if (dir != null && !dir.exists()) {
315                boolean success = dir.mkdirs();
316                if (!success) {
317                    throw new IOException("Cannot create directory " + dir.getAbsolutePath());
318                }
319            }
320        }
321    
322        public static void unzipToDirectory(ZipFile zipFile, File destDir) throws IOException {
323            Enumeration entries = zipFile.entries();
324            try {
325                while (entries.hasMoreElements()) {
326                    ZipEntry entry = (ZipEntry) entries.nextElement();
327                    if (entry.isDirectory()) {
328                        File dir = new File(destDir, entry.getName());
329                        createDirectory(dir);
330                    } else {
331                        File file = new File(destDir, entry.getName());
332                        createDirectory(file.getParentFile());
333                        OutputStream out = null;
334                        InputStream in = null;
335                        try {
336                            out = new BufferedOutputStream(new FileOutputStream(file));
337                            in = zipFile.getInputStream(entry);
338                            writeAll(in, out);
339                        } finally {
340                            if (null != out) {
341                                out.close();
342                            }
343                            if (null != in) {
344                                in.close();
345                            }
346                        }
347                    }
348                }
349            } finally {
350                zipFile.close();
351            }
352        }
353        
354        
355        public static boolean recursiveDelete(File root, Collection<String> unableToDeleteCollection) {
356            if (root == null) {
357                return true;
358            }
359    
360            if (root.isDirectory()) {
361                File[] files = root.listFiles();
362                if (files != null) {
363                    for (int i = 0; i < files.length; i++) {
364                        File file = files[i];
365                        if (file.isDirectory()) {
366                            recursiveDelete(file, unableToDeleteCollection);
367                        } else {
368                            if (!file.delete() && unableToDeleteCollection != null) {
369                                unableToDeleteCollection.add(file.getAbsolutePath());    
370                            }
371                        }
372                        // help out the GC of file handles by nulling the references
373                        files[i] = null;
374                    }
375                }
376            }
377            boolean rootDeleteStatus;
378            if (!(rootDeleteStatus = root.delete()) && unableToDeleteCollection != null) 
379                    unableToDeleteCollection.add(root.getAbsolutePath());
380            
381            return rootDeleteStatus;
382        }
383        
384        public static boolean recursiveDelete(File root) {
385            return recursiveDelete(root, null);
386        }
387    
388        public static Collection<File> listRecursiveFiles(File file) {
389            Collection<File> list = new ArrayList<File>();
390            listRecursiveFiles(file, list);
391            return Collections.unmodifiableCollection(list);
392        }
393    
394        public static void listRecursiveFiles(File file, Collection<File> collection) {
395            File[] files = file.listFiles();
396            if ( null == files ) {
397                return;
398            }
399            for (File file1 : files) {
400                collection.add(file1);
401                if (file1.isDirectory()) {
402                    listRecursiveFiles(file1, collection);
403                }
404            }
405        }
406    
407        public static void flush(OutputStream thing) {
408            if (thing != null) {
409                try {
410                    thing.flush();
411                } catch(Exception ignored) {
412                }
413            }
414        }
415    
416        public static void flush(Writer thing) {
417            if (thing != null) {
418                try {
419                    thing.flush();
420                } catch(Exception ignored) {
421                }
422            }
423        }
424    
425        public static void close(JarFile thing) {
426            if (thing != null) {
427                try {
428                    thing.close();
429                } catch(Exception ignored) {
430                }
431            }
432        }
433    
434        public static void close(InputStream thing) {
435            if (thing != null) {
436                try {
437                    thing.close();
438                } catch(Exception ignored) {
439                }
440            }
441        }
442    
443        public static void close(OutputStream thing) {
444            if (thing != null) {
445                try {
446                    thing.close();
447                } catch(Exception ignored) {
448                }
449            }
450        }
451    
452        public static void close(Reader thing) {
453            if (thing != null) {
454                try {
455                    thing.close();
456                } catch(Exception ignored) {
457                }
458            }
459        }
460    
461        public static void close(Writer thing) {
462            if (thing != null) {
463                try {
464                    thing.close();
465                } catch(Exception ignored) {
466                }
467            }
468        }
469    
470        public static final class EmptyInputStream extends InputStream {
471            public int read() {
472                return -1;
473            }
474    
475            public int read(byte b[])  {
476                return -1;
477            }
478    
479            public int read(byte b[], int off, int len) {
480                return -1;
481            }
482    
483            public long skip(long n) {
484                return 0;
485            }
486    
487            public int available() {
488                return 0;
489            }
490    
491            public void close() {
492            }
493    
494            public synchronized void mark(int readlimit) {
495            }
496    
497            public synchronized void reset() {
498            }
499    
500            public boolean markSupported() {
501                return false;
502            }
503        }
504    }