001    /**
002     * Copyright (c) 2010 Yahoo! Inc. All rights reserved.
003     * Licensed under the Apache License, Version 2.0 (the "License");
004     * you may not use this file except in compliance with the License.
005     * You may obtain a copy of the License at
006     *
007     *   http://www.apache.org/licenses/LICENSE-2.0
008     *
009     *  Unless required by applicable law or agreed to in writing, software
010     *  distributed under the License is distributed on an "AS IS" BASIS,
011     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012     *  See the License for the specific language governing permissions and
013     *  limitations under the License. See accompanying LICENSE file.
014     */
015    package org.apache.oozie.util;
016    
017    import java.io.IOException;
018    import java.io.InputStream;
019    import java.io.InputStreamReader;
020    import java.io.OutputStream;
021    import java.io.Reader;
022    import java.io.Writer;
023    import java.io.File;
024    import java.io.FileInputStream;
025    import java.io.FileOutputStream;
026    import java.util.zip.ZipOutputStream;
027    import java.util.zip.ZipEntry;
028    import java.util.jar.JarOutputStream;
029    import java.util.jar.Manifest;
030    
031    /**
032     * IO Utility methods.
033     */
034    public abstract class IOUtils {
035    
036        /**
037         * Delete recursively a local directory.
038         *
039         * @param file directory to delete.
040         * @throws IOException thrown if the directory could not be deleted.
041         */
042        public static void delete(File file) throws IOException {
043            ParamChecker.notNull(file, "file");
044            if (file.getAbsolutePath().length() < 5) {
045                throw new RuntimeException(XLog.format("Path[{0}] is too short, not deleting", file.getAbsolutePath()));
046            }
047            if (file.exists()) {
048                if (file.isDirectory()) {
049                    File[] children = file.listFiles();
050                    if (children != null) {
051                        for (File child : children) {
052                            delete(child);
053                        }
054                    }
055                }
056                if (!file.delete()) {
057                    throw new RuntimeException(XLog.format("Could not delete path[{0}]", file.getAbsolutePath()));
058                }
059            }
060        }
061    
062        /**
063         * Return a reader as string. <p/>
064         *
065         * @param reader reader to read into a string.
066         * @param maxLen max content length allowed, if -1 there is no limit.
067         * @return the reader content.
068         * @throws IOException thrown if the resource could not be read.
069         */
070        public static String getReaderAsString(Reader reader, int maxLen) throws IOException {
071            ParamChecker.notNull(reader, "reader");
072            StringBuffer sb = new StringBuffer();
073            char[] buffer = new char[2048];
074            int read;
075            int count = 0;
076            while ((read = reader.read(buffer)) > -1) {
077                count += read;
078                if (maxLen > -1 && count > maxLen) {
079                    throw new IllegalArgumentException(XLog.format("stream exceeds limit [{0}]", maxLen));
080                }
081                sb.append(buffer, 0, read);
082            }
083            reader.close();
084            return sb.toString();
085        }
086    
087    
088        /**
089         * Return a classpath resource as a stream. <p/>
090         *
091         * @param path classpath for the resource.
092         * @param maxLen max content length allowed.
093         * @return the stream for the resource.
094         * @throws IOException thrown if the resource could not be read.
095         */
096        public static InputStream getResourceAsStream(String path, int maxLen) throws IOException {
097            ParamChecker.notEmpty(path, "path");
098            InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
099            if (is == null) {
100                throw new IllegalArgumentException(XLog.format("resource [{0}] not found", path));
101            }
102            return is;
103        }
104    
105        /**
106         * Return a classpath resource as a reader. <p/> It is assumed that the resource is a text resource.
107         *
108         * @param path classpath for the resource.
109         * @param maxLen max content length allowed.
110         * @return the reader for the resource.
111         * @throws IOException thrown if the resource could not be read.
112         */
113        public static Reader getResourceAsReader(String path, int maxLen) throws IOException {
114            return new InputStreamReader(getResourceAsStream(path, maxLen));
115        }
116    
117        /**
118         * Return a classpath resource as string. <p/> It is assumed that the resource is a text resource.
119         *
120         * @param path classpath for the resource.
121         * @param maxLen max content length allowed.
122         * @return the resource content.
123         * @throws IOException thrown if the resource could not be read.
124         */
125        public static String getResourceAsString(String path, int maxLen) throws IOException {
126            ParamChecker.notEmpty(path, "path");
127            InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
128            if (is == null) {
129                throw new IllegalArgumentException(XLog.format("resource [{0}] not found", path));
130            }
131            Reader reader = new InputStreamReader(is);
132            return getReaderAsString(reader, maxLen);
133        }
134    
135        /**
136         * Copies an inputstream into an output stream.
137         *
138         * @param is inputstream to copy from.
139         * @param os outputstream to  copy to.
140         * @throws IOException thrown if the copy failed.
141         */
142        public static void copyStream(InputStream is, OutputStream os) throws IOException {
143            ParamChecker.notNull(is, "is");
144            ParamChecker.notNull(os, "os");
145            byte[] buffer = new byte[4096];
146            int read;
147            while ((read = is.read(buffer)) > -1) {
148                os.write(buffer, 0, read);
149            }
150            os.close();
151            is.close();
152        }
153    
154        /**
155         * Copies an char input stream into an char output stream.
156         *
157         * @param reader reader to copy from.
158         * @param writer writer to  copy to.
159         * @throws IOException thrown if the copy failed.
160         */
161        public static void copyCharStream(Reader reader, Writer writer) throws IOException {
162            ParamChecker.notNull(reader, "reader");
163            ParamChecker.notNull(writer, "writer");
164            char[] buffer = new char[4096];
165            int read;
166            while ((read = reader.read(buffer)) > -1) {
167                writer.write(buffer, 0, read);
168            }
169            writer.close();
170            reader.close();
171        }
172    
173        /**
174         * Zips a local directory, recursively, into a ZIP stream.
175         *
176         * @param dir directory to ZIP.
177         * @param relativePath basePath in the ZIP for the files, normally "/".
178         * @param zos the ZIP output stream to ZIP the directory.
179         * @throws java.io.IOException thrown if the directory could not be zipped.
180         */
181        public static void zipDir(File dir, String relativePath, ZipOutputStream zos) throws IOException {
182            zipDir(dir, relativePath, zos, true);
183            zos.close();
184        }
185    
186        private static void zipDir(File dir, String relativePath, ZipOutputStream zos, boolean start) throws IOException {
187            String[] dirList = dir.list();
188            for (String aDirList : dirList) {
189                File f = new File(dir, aDirList);
190                if (!f.isHidden()) {
191                    if (f.isDirectory()) {
192                        if (!start) {
193                            ZipEntry dirEntry = new ZipEntry(relativePath + f.getName() + "/");
194                            zos.putNextEntry(dirEntry);
195                            zos.closeEntry();
196                        }
197                        String filePath = f.getPath();
198                        File file = new File(filePath);
199                        zipDir(file, relativePath + f.getName() + "/", zos, false);
200                    }
201                    else {
202                        ZipEntry anEntry = new ZipEntry(relativePath + f.getName());
203                        zos.putNextEntry(anEntry);
204                        InputStream is = new FileInputStream(f);
205                        byte[] arr = new byte[4096];
206                        int read = is.read(arr);
207                        while (read > -1) {
208                            zos.write(arr, 0, read);
209                            read = is.read(arr);
210                        }
211                        is.close();
212                        zos.closeEntry();
213                    }
214                }
215            }
216        }
217    
218        /**
219         * Creates a JAR file with the specified classes.
220         *
221         * @param baseDir local directory to create the JAR file, the staging 'classes' directory is created in there.
222         * @param jarName JAR file name, including extesion.
223         * @param classes classes to add to the JAR.
224         * @return an absolute File to the created JAR file.
225         * @throws java.io.IOException thrown if the JAR file could not be created.
226         */
227        public static File createJar(File baseDir, String jarName, Class... classes) throws IOException {
228            File classesDir = new File(baseDir, "classes");
229            for (Class clazz : classes) {
230                String classPath = clazz.getName().replace(".", "/") + ".class";
231                String classFileName = classPath;
232                if (classPath.lastIndexOf("/") > -1) {
233                    classFileName = classPath.substring(classPath.lastIndexOf("/") + 1);
234                }
235                String packagePath = new File(classPath).getParent();
236                File dir = new File(classesDir, packagePath);
237                if (!dir.exists()) {
238                    if (!dir.mkdirs()) {
239                        throw new IOException(XLog.format("could not create dir [{0}]", dir));
240                    }
241                }
242                InputStream is = getResourceAsStream(classPath, -1);
243                OutputStream os = new FileOutputStream(new File(dir, classFileName));
244                copyStream(is, os);
245            }
246            File jar = new File(baseDir, jarName);
247            File jarDir = jar.getParentFile();
248            if (!jarDir.exists()) {
249                if (!jarDir.mkdirs()) {
250                    throw new IOException(XLog.format("could not create dir [{0}]", jarDir));
251                }
252            }
253            JarOutputStream zos = new JarOutputStream(new FileOutputStream(jar), new Manifest());
254            zipDir(classesDir, "", zos);
255            return jar;
256        }
257    }