001: /*
002: * JFox - The most lightweight Java EE Application Server!
003: * more details please visit http://www.huihoo.org/jfox or http://www.jfox.org.cn.
004: *
005: * JFox is licenced and re-distributable under GNU LGPL.
006: */
007: package org.jfox.util;
008:
009: import java.io.BufferedInputStream;
010: import java.io.BufferedOutputStream;
011: import java.io.File;
012: import java.io.FileFilter;
013: import java.io.FileInputStream;
014: import java.io.FileOutputStream;
015: import java.io.IOException;
016: import java.io.InputStream;
017: import java.io.OutputStream;
018: import java.net.URL;
019: import java.net.URLClassLoader;
020: import java.util.ArrayList;
021: import java.util.Arrays;
022: import java.util.Collections;
023: import java.util.Enumeration;
024: import java.util.HashMap;
025: import java.util.List;
026: import java.util.Map;
027: import java.util.jar.JarEntry;
028: import java.util.jar.JarFile;
029: import java.util.jar.JarInputStream;
030: import java.util.jar.Manifest;
031: import java.util.zip.ZipEntry;
032:
033: /**
034: * @author <a href="mailto:jfox.young@gmail.com">Young Yang</a>
035: */
036:
037: public class FileUtils {
038: /**
039: * The extension separator character.
040: */
041: private static final char EXTENSION_SEPARATOR = '.';
042:
043: /**
044: * The Unix separator character.
045: */
046: private static final char UNIX_SEPARATOR = '/';
047:
048: /**
049: * The Windows separator character.
050: */
051: private static final char WINDOWS_SEPARATOR = '\\';
052:
053: /**
054: * The system separator character.
055: */
056: private static final char SYSTEM_SEPARATOR = File.separatorChar;
057:
058: /**
059: * The separator character that is the opposite of the system separator.
060: */
061: private static final char OTHER_SEPARATOR;
062:
063: static {
064: if (SYSTEM_SEPARATOR == WINDOWS_SEPARATOR) {
065: OTHER_SEPARATOR = UNIX_SEPARATOR;
066: } else {
067: OTHER_SEPARATOR = WINDOWS_SEPARATOR;
068: }
069: }
070:
071: /**
072: * <p/> Delete a file. If file is a directory, delete it and all
073: * sub-directories.
074: * </p>
075: * <p/> The difference between File.delete() and this method are:
076: * </p>
077: * <ul>
078: * <li>A directory to be deleted does not have to be empty.</li>
079: * <li>You get exceptions when a file or directory cannot be deleted.
080: * (java.io.File methods returns a boolean)</li>
081: * </ul>
082: *
083: * @param file file or directory to delete.
084: */
085: public static boolean delete(File file) {
086: if (!file.exists()) {
087: return true;
088: }
089: if (file.isDirectory()) {
090: return deleteDirectory(file);
091: } else {
092: return file.delete();
093: }
094: }
095:
096: /**
097: * Delete a file, or a directory and all of its contents.
098: *
099: * @param dir The directory or file to delete.
100: * @return True if all delete operations were successfull.
101: */
102: public static boolean deleteDirectory(File dir) {
103:
104: if (!dir.exists())
105: return true;
106: boolean result = cleanDirectory(dir);
107: return result & dir.delete();
108: }
109:
110: /**
111: * Clean a directory without deleting it.
112: *
113: * @param directory directory to clean
114: */
115: public static boolean cleanDirectory(File directory) {
116: if (!directory.exists()) {
117: return true;
118: }
119: if (!directory.isDirectory()) {
120: return false;
121: }
122: boolean result = true;
123:
124: File[] files = directory.listFiles();
125: for (int i = 0; i < files.length; i++) {
126: File file = files[i];
127: result = result && delete(file);
128: }
129: return result;
130: }
131:
132: /**
133: * Schedule a file to be deleted when JVM exits. If file is directory delete
134: * it and all sub-directories.
135: *
136: * @param file file or directory to delete.
137: * @throws IOException in case deletion is unsuccessful
138: */
139: public static void deleteOnExit(File file) throws IOException {
140: if (file.isDirectory()) {
141: deleteDirectoryOnExit(file);
142: } else {
143: file.deleteOnExit();
144: }
145: }
146:
147: /**
148: * Recursively schedule directory for deletion on JVM exit.
149: *
150: * @param directory directory to delete.
151: * @throws IOException in case deletion is unsuccessful
152: */
153: private static void deleteDirectoryOnExit(File directory)
154: throws IOException {
155: if (!directory.exists()) {
156: return;
157: }
158:
159: cleanDirectoryOnExit(directory);
160: directory.deleteOnExit();
161: }
162:
163: /**
164: * Clean a directory without deleting it.
165: *
166: * @param directory directory to clean.
167: * @throws IOException in case cleaning is unsuccessful
168: */
169: private static void cleanDirectoryOnExit(File directory)
170: throws IOException {
171: if (!directory.exists()) {
172: // do nothing
173: return;
174: }
175:
176: if (!directory.isDirectory()) {
177: String message = directory + " is not a directory";
178: throw new IllegalArgumentException(message);
179: }
180:
181: IOException exception = null;
182:
183: File[] files = directory.listFiles();
184: for (int i = 0; i < files.length; i++) {
185: File file = files[i];
186: try {
187: deleteOnExit(file);
188: } catch (IOException ioe) {
189: exception = ioe;
190: }
191: }
192:
193: if (null != exception) {
194: throw exception;
195: }
196: }
197:
198: /**
199: * Copy a file.
200: *
201: * @param source Source file to copy.
202: * @param dir Destination target dir.
203: * @throws java.io.IOException Failed to copy file.
204: */
205: public static File copy(final File source, final File dir)
206: throws IOException {
207: if (source.getParentFile().equals(dir)) {
208: return source;
209: }
210: if (!dir.exists()) {
211: dir.mkdirs();
212: }
213: if (!dir.isDirectory()) {
214: throw new IOException("Destination must be a directory.");
215: }
216:
217: File target = new File(dir, source.getName());
218: BufferedInputStream in = new BufferedInputStream(
219: new FileInputStream(source));
220: BufferedOutputStream out = new BufferedOutputStream(
221: new FileOutputStream(target));
222: try {
223: IOUtils.copy(in, out);
224: } finally {
225: out.flush();
226: IOUtils.closeQuietly(in);
227: IOUtils.closeQuietly(out);
228: }
229: return target;
230: }
231:
232: /**
233: * move the source file to the targe directory
234: *
235: * @param source
236: * @param dir
237: */
238: public static File move(final File source, final File dir)
239: throws IOException {
240: File target = copy(source, dir);
241: source.delete();
242: return target;
243: }
244:
245: /**
246: * Implements the same behaviour as the "touch" utility on Unix. It creates
247: * a new file with size 0 or, if the file exists already, it is opened and
248: * closed without modifying it, but updating the file date and time.
249: *
250: * @param file the File to touch
251: * @throws IOException If an I/O problem occurs
252: */
253: public static void touch(File file) throws IOException {
254: OutputStream out = new java.io.FileOutputStream(file);
255: IOUtils.closeQuietly(out);
256: }
257:
258: /**
259: * Convert the array of Files into a list of URLs.
260: *
261: * @param files the array of files
262: * @return the array of URLs
263: * @throws IOException if an error occurs
264: */
265: public static URL[] toURLs(File[] files) throws IOException {
266: URL[] urls = new URL[files.length];
267:
268: for (int i = 0; i < urls.length; i++) {
269: urls[i] = files[i].toURI().toURL();
270: }
271:
272: return urls;
273: }
274:
275: /**
276: * Convert from a <code>URL</code> to a <code>File</code>.
277: *
278: * @param url File URL.
279: * @return The equivalent <code>File</code> object, or <code>null</code>
280: * if the URL's protocol is not <code>file</code>
281: */
282: public static File toFile(URL url) {
283: if (!url.getProtocol().equals("file")) {
284: return null;
285: } else {
286: String filename = url.getFile().replace('/',
287: File.separatorChar);
288: return new File(filename);
289: }
290: }
291:
292: /**
293: * get file content
294: */
295: public static String content(File file) throws IOException {
296: InputStream in = new java.io.FileInputStream(file);
297: try {
298: return IOUtils.toString(in);
299: } finally {
300: IOUtils.closeQuietly(in);
301: }
302: }
303:
304: /**
305: * list Files recursively
306: *
307: * @param dir directory
308: * @param filter file filter
309: * @return File list
310: */
311: public static List<File> listFiles(File dir, FileFilter filter) {
312: List<File> files = new ArrayList<File>();
313: if (!dir.exists() || dir.isFile())
314: return files;
315: listFiles(files, dir, filter);
316: return files;
317: }
318:
319: private static void listFiles(List filesList, File dir,
320: FileFilter filter) {
321: File[] files = dir.listFiles(filter);
322: List temp = Arrays.asList(files);
323: Collections.sort(temp);
324: filesList.addAll(temp);
325:
326: File[] subDirs = dir.listFiles(FileFilterUtils
327: .directoryFileFilter());
328: for (int i = 0; i < subDirs.length; i++) {
329: listFiles(filesList, subDirs[i], filter);
330: }
331: }
332:
333: /**
334: * Dump the contents of a JarArchive to the dpecified destination.
335: */
336: public static void extractJar(File jarFile, File dest)
337: throws IOException {
338: if (!dest.exists()) {
339: dest.mkdirs();
340: }
341: if (!dest.isDirectory()) {
342: throw new IOException("Destination must be a directory.");
343: }
344: JarInputStream jin = new JarInputStream(new FileInputStream(
345: jarFile));
346: byte[] buffer = new byte[1024];
347:
348: ZipEntry entry = jin.getNextEntry();
349: while (entry != null) {
350: String fileName = entry.getName();
351: if (fileName.charAt(fileName.length() - 1) == '/') {
352: fileName = fileName.substring(0, fileName.length() - 1);
353: }
354: if (fileName.charAt(0) == '/') {
355: fileName = fileName.substring(1);
356: }
357: if (File.separatorChar != '/') {
358: fileName = fileName.replace('/', File.separatorChar);
359: }
360: File file = new File(dest, fileName);
361: if (entry.isDirectory()) {
362: // make sure the directory exists
363: file.mkdirs();
364: jin.closeEntry();
365: } else {
366: // make sure the directory exists
367: File parent = file.getParentFile();
368: if (parent != null && !parent.exists()) {
369: parent.mkdirs();
370: }
371:
372: // dump the file
373: OutputStream out = new FileOutputStream(file);
374: int len = 0;
375: while ((len = jin.read(buffer, 0, buffer.length)) != -1) {
376: out.write(buffer, 0, len);
377: }
378: out.flush();
379: out.close();
380: jin.closeEntry();
381: file.setLastModified(entry.getTime());
382: }
383: entry = jin.getNextEntry();
384: }
385: /*
386: * Explicity write out the META-INF/MANIFEST.MF so that any headers such
387: * as the Class-Path are see for the unpackaged jar
388: */
389: Manifest mf = jin.getManifest();
390: if (mf != null) {
391: File file = new File(dest, "META-INF/MANIFEST.MF");
392: File parent = file.getParentFile();
393: if (parent.exists() == false) {
394: parent.mkdirs();
395: }
396: OutputStream out = new FileOutputStream(file);
397: mf.write(out);
398: out.flush();
399: out.close();
400: }
401: jin.close();
402: }
403:
404: /**
405: * use URLClassLoader.findResource, this method will not find resource use
406: * parent
407: *
408: * @param loader
409: * @param descriptorName
410: * @return null if no such descriptor
411: */
412: public static URL getDescriptor(URLClassLoader loader,
413: String descriptorName) {
414: return loader.findResource(descriptorName);
415: }
416:
417: public static Manifest getManifest(File jarFile) throws IOException {
418: return new JarFile(jarFile).getManifest();
419: }
420:
421: /**
422: * 获得一个 URL ä¸æ‰€æœ‰çš„Class Name
423: *
424: * @param location url location
425: * @throws IOException ioexception
426: */
427: public static Map<String, byte[]> getClassBytesMap(URL location)
428: throws IOException {
429: File file = toFile(location);
430: return getClassBytesMap(file);
431: }
432:
433: /**
434: * 从文件或目录ä¸æ?œç´¢æ‰€æœ‰çš„ class 文件
435: *
436: * @param file file
437: * @return classname=>class bytes
438: * @throws IOException ioexception
439: */
440: public static Map<String, byte[]> getClassBytesMap(File file)
441: throws IOException {
442: Map<String, byte[]> contents = new HashMap<String, byte[]>();
443: if (file.isDirectory()) {
444: List<File> files = listFiles(file, FileFilterUtils.and(
445: FileFilterUtils.suffixFileFilter(".class"),
446: FileFilterUtils.not(FileFilterUtils
447: .nameFileFilter("package-info.class"))));
448: for (File _file : files) {
449: String className = _file.getPath().substring(
450: file.getPath().length() + 1);
451: className = className.replaceFirst(".class$", "");
452: className = className.replace(File.separatorChar, '.');
453: byte[] content = IOUtils
454: .toByteArray(new FileInputStream(_file));
455: contents.put(className, content);
456: }
457: } else {
458: JarFile jarFile = new JarFile(file);
459: Enumeration<JarEntry> enu = jarFile.entries();
460: try {
461: while (enu.hasMoreElements()) {
462: JarEntry entry = enu.nextElement();
463: if (entry.isDirectory()
464: || !entry.getName().endsWith(".class")
465: || entry.getName().endsWith(
466: "package-info.class")) {
467: continue;
468: }
469: String className = entry.getName();
470: className = className.replaceFirst(".class$", "");
471: className = className.replace('/', '.');
472: InputStream in = null;
473: try {
474: in = new BufferedInputStream(jarFile
475: .getInputStream(entry));
476: byte[] content = IOUtils.toByteArray(in);
477: contents.put(className, content);
478: } finally {
479: if (in != null) {
480: in.close();
481: }
482: }
483: }
484: } finally {
485: jarFile.close();
486: }
487: }
488: return Collections.unmodifiableMap(contents);
489: }
490:
491: public static void main(String[] args) throws Exception {
492: System.out.println(getClassBytesMap(new File(
493: "framework/lib/log4j-1.2.14.jar").toURI().toURL()));
494: System.out.println(getClassBytesMap(new File(
495: "framework/classes").toURI().toURL()));
496: }
497: }
|