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