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