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 */ 018package org.apache.oozie.util; 019 020import java.io.IOException; 021import java.io.InputStream; 022import java.io.InputStreamReader; 023import java.io.OutputStream; 024import java.io.Reader; 025import java.io.Writer; 026import java.io.File; 027import java.io.FileInputStream; 028import java.io.FileOutputStream; 029import java.io.Closeable; 030import java.util.zip.ZipOutputStream; 031import java.util.zip.ZipEntry; 032import java.util.jar.JarOutputStream; 033import java.util.jar.Manifest; 034 035/** 036 * IO Utility methods. 037 */ 038public abstract class IOUtils { 039 040 /** 041 * Delete recursively a local directory. 042 * 043 * @param file directory to delete. 044 * @throws IOException thrown if the directory could not be deleted. 045 */ 046 public static void delete(File file) throws IOException { 047 ParamChecker.notNull(file, "file"); 048 if (file.getAbsolutePath().length() < 5) { 049 throw new RuntimeException(XLog.format("Path[{0}] is too short, not deleting", file.getAbsolutePath())); 050 } 051 if (file.exists()) { 052 if (file.isDirectory()) { 053 File[] children = file.listFiles(); 054 if (children != null) { 055 for (File child : children) { 056 delete(child); 057 } 058 } 059 } 060 if (!file.delete()) { 061 throw new RuntimeException(XLog.format("Could not delete path[{0}]", file.getAbsolutePath())); 062 } 063 } 064 } 065 066 /** 067 * Return a reader as string. <p/> 068 * 069 * @param reader reader to read into a string. 070 * @param maxLen max content length allowed, if -1 there is no limit. 071 * @return the reader content. 072 * @throws IOException thrown if the resource could not be read. 073 */ 074 public static String getReaderAsString(Reader reader, int maxLen) throws IOException { 075 ParamChecker.notNull(reader, "reader"); 076 StringBuffer sb = new StringBuffer(); 077 char[] buffer = new char[2048]; 078 int read; 079 int count = 0; 080 while ((read = reader.read(buffer)) > -1) { 081 count += read; 082 if (maxLen > -1 && count > maxLen) { 083 throw new IllegalArgumentException(XLog.format("stream exceeds limit [{0}]", maxLen)); 084 } 085 sb.append(buffer, 0, read); 086 } 087 reader.close(); 088 return sb.toString(); 089 } 090 091 092 /** 093 * Return a classpath resource as a stream. <p/> 094 * 095 * @param path classpath for the resource. 096 * @param maxLen max content length allowed. 097 * @return the stream for the resource. 098 * @throws IOException thrown if the resource could not be read. 099 */ 100 public static InputStream getResourceAsStream(String path, int maxLen) throws IOException { 101 ParamChecker.notEmpty(path, "path"); 102 InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path); 103 if (is == null) { 104 throw new IllegalArgumentException(XLog.format("resource [{0}] not found", path)); 105 } 106 return is; 107 } 108 109 /** 110 * Return a classpath resource as a reader. <p/> It is assumed that the resource is a text resource. 111 * 112 * @param path classpath for the resource. 113 * @param maxLen max content length allowed. 114 * @return the reader for the resource. 115 * @throws IOException thrown if the resource could not be read. 116 */ 117 public static Reader getResourceAsReader(String path, int maxLen) throws IOException { 118 return new InputStreamReader(getResourceAsStream(path, maxLen)); 119 } 120 121 /** 122 * Return a classpath resource as string. <p/> It is assumed that the resource is a text resource. 123 * 124 * @param path classpath for the resource. 125 * @param maxLen max content length allowed. 126 * @return the resource content. 127 * @throws IOException thrown if the resource could not be read. 128 */ 129 public static String getResourceAsString(String path, int maxLen) throws IOException { 130 ParamChecker.notEmpty(path, "path"); 131 InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path); 132 if (is == null) { 133 throw new IllegalArgumentException(XLog.format("resource [{0}] not found", path)); 134 } 135 Reader reader = new InputStreamReader(is); 136 return getReaderAsString(reader, maxLen); 137 } 138 139 /** 140 * Copies an inputstream into an output stream. 141 * 142 * @param is inputstream to copy from. 143 * @param os outputstream to copy to. 144 * @throws IOException thrown if the copy failed. 145 */ 146 public static void copyStream(InputStream is, OutputStream os) throws IOException { 147 ParamChecker.notNull(is, "is"); 148 ParamChecker.notNull(os, "os"); 149 byte[] buffer = new byte[4096]; 150 int read; 151 while ((read = is.read(buffer)) > -1) { 152 os.write(buffer, 0, read); 153 } 154 os.close(); 155 is.close(); 156 } 157 158 /** 159 * Copies an char input stream into an char output stream. 160 * 161 * @param reader reader to copy from. 162 * @param writer writer to copy to. 163 * @throws IOException thrown if the copy failed. 164 */ 165 public static void copyCharStream(Reader reader, Writer writer) throws IOException { 166 ParamChecker.notNull(reader, "reader"); 167 ParamChecker.notNull(writer, "writer"); 168 char[] buffer = new char[4096]; 169 int read; 170 while ((read = reader.read(buffer)) > -1) { 171 writer.write(buffer, 0, read); 172 } 173 writer.close(); 174 reader.close(); 175 } 176 177 /** 178 * Zips a local directory, recursively, into a ZIP stream. 179 * 180 * @param dir directory to ZIP. 181 * @param relativePath basePath in the ZIP for the files, normally "/". 182 * @param zos the ZIP output stream to ZIP the directory. 183 * @throws java.io.IOException thrown if the directory could not be zipped. 184 */ 185 public static void zipDir(File dir, String relativePath, ZipOutputStream zos) throws IOException { 186 zipDir(dir, relativePath, zos, true); 187 zos.close(); 188 } 189 190 private static void zipDir(File dir, String relativePath, ZipOutputStream zos, boolean start) throws IOException { 191 String[] dirList = dir.list(); 192 for (String aDirList : dirList) { 193 File f = new File(dir, aDirList); 194 if (!f.isHidden()) { 195 if (f.isDirectory()) { 196 if (!start) { 197 ZipEntry dirEntry = new ZipEntry(relativePath + f.getName() + "/"); 198 zos.putNextEntry(dirEntry); 199 zos.closeEntry(); 200 } 201 String filePath = f.getPath(); 202 File file = new File(filePath); 203 zipDir(file, relativePath + f.getName() + "/", zos, false); 204 } 205 else { 206 ZipEntry anEntry = new ZipEntry(relativePath + f.getName()); 207 zos.putNextEntry(anEntry); 208 InputStream is = new FileInputStream(f); 209 byte[] arr = new byte[4096]; 210 int read = is.read(arr); 211 while (read > -1) { 212 zos.write(arr, 0, read); 213 read = is.read(arr); 214 } 215 is.close(); 216 zos.closeEntry(); 217 } 218 } 219 } 220 } 221 222 /** 223 * Creates a JAR file with the specified classes. 224 * 225 * @param baseDir local directory to create the JAR file, the staging 'classes' directory is created in there. 226 * @param jarName JAR file name, including extesion. 227 * @param classes classes to add to the JAR. 228 * @return an absolute File to the created JAR file. 229 * @throws java.io.IOException thrown if the JAR file could not be created. 230 */ 231 public static File createJar(File baseDir, String jarName, Class... classes) throws IOException { 232 File classesDir = new File(baseDir, "classes"); 233 for (Class clazz : classes) { 234 String classPath = clazz.getName().replace(".", "/") + ".class"; 235 String classFileName = classPath; 236 if (classPath.lastIndexOf("/") > -1) { 237 classFileName = classPath.substring(classPath.lastIndexOf("/") + 1); 238 } 239 String packagePath = new File(classPath).getParent(); 240 File dir = new File(classesDir, packagePath); 241 if (!dir.exists()) { 242 if (!dir.mkdirs()) { 243 throw new IOException(XLog.format("could not create dir [{0}]", dir)); 244 } 245 } 246 InputStream is = getResourceAsStream(classPath, -1); 247 OutputStream os = new FileOutputStream(new File(dir, classFileName)); 248 copyStream(is, os); 249 } 250 File jar = new File(baseDir, jarName); 251 File jarDir = jar.getParentFile(); 252 if (!jarDir.exists()) { 253 if (!jarDir.mkdirs()) { 254 throw new IOException(XLog.format("could not create dir [{0}]", jarDir)); 255 } 256 } 257 JarOutputStream zos = new JarOutputStream(new FileOutputStream(jar), new Manifest()); 258 zipDir(classesDir, "", zos); 259 return jar; 260 } 261 262 /** 263 * Close a list of resources. </p> Any thrown exceptions are suppressed. 264 * @param objects list of objects to close 265 */ 266 public static void closeSafely(Closeable... objects) { 267 for (Closeable object : objects) { 268 try { 269 if (null != object) { 270 object.close(); 271 } 272 } catch (Throwable th) { 273 // ignore 274 } 275 } 276 } 277}