001package io.ebean.enhance.ant; 002 003import io.ebean.enhance.EnhancementException; 004import io.ebean.enhance.Transformer; 005import io.ebean.enhance.common.InputStreamTransform; 006 007import java.io.File; 008import java.io.IOException; 009import java.lang.instrument.IllegalClassFormatException; 010import java.util.Collections; 011import java.util.LinkedHashSet; 012import java.util.Set; 013 014/** 015 * Transforms class files when they are on the file system. 016 * <p> 017 * Typically run as part of an ANT task rather than when Ebean is running. 018 * </p> 019 */ 020public class OfflineFileTransform { 021 022 protected final InputStreamTransform inputStreamTransform; 023 024 protected final String inDir; 025 026 protected TransformationListener listener; 027 028 private final int logLevel; 029 030 /** 031 * Enhance the class file and replace the file with the the enhanced 032 * version of the class. 033 * 034 * @param transformer object that actually transforms the class bytes 035 * @param classLoader the ClassLoader used as part of the transformation 036 * @param inDir the root directory where the class files are located 037 */ 038 public OfflineFileTransform(Transformer transformer, ClassLoader classLoader, String inDir) { 039 this.inputStreamTransform = new InputStreamTransform(transformer, classLoader); 040 logLevel = transformer.getLogLevel(); 041 inDir = trimSlash(inDir); 042 this.inDir = inDir; 043 } 044 045 /** 046 * Register a listener to receive event notification 047 */ 048 public void setListener(TransformationListener v) { 049 this.listener = v; 050 } 051 052 private String trimSlash(String dir) { 053 if (dir.endsWith("/")) { 054 return dir.substring(0, dir.length() - 1); 055 } else { 056 return dir; 057 } 058 } 059 060 /** 061 * Process the packageNames as comma delimited string. 062 */ 063 public void process(String packageNames) { 064 if (packageNames == null) { 065 // just process all directories 066 processPackage(""); 067 return; 068 } 069 070 Set<String> pkgNames = new LinkedHashSet<>(); 071 Collections.addAll(pkgNames, packageNames.split(",")); 072 process(pkgNames); 073 } 074 075 /** 076 * Process all the comma delimited list of packages. 077 * <p> 078 * Package names are effectively converted into a directory on the file 079 * system, and the class files are found and processed. 080 * </p> 081 */ 082 public void process(Set<String> packageNames) { 083 if (packageNames == null || packageNames.isEmpty()) { 084 // just process all directories 085 inputStreamTransform.log(2, "processing all directories (as no explicit packages)"); 086 processPackage(""); 087 return; 088 } 089 090 for (String pkgName : packageNames) { 091 String pkg = pkgName.trim().replace('.', '/'); 092 if (pkg.endsWith("**")) { 093 pkg = pkg.substring(0, pkg.length() - 2); 094 } else if (pkg.endsWith("*")) { 095 pkg = pkg.substring(0, pkg.length() - 1); 096 } 097 098 pkg = trimSlash(pkg); 099 processPackage(pkg); 100 } 101 } 102 103 private void processPackage(String dir) { 104 105 inputStreamTransform.log(3, "transform> pkg: " + dir); 106 107 String dirPath = inDir + "/" + dir; 108 File d = new File(dirPath); 109 if (!d.exists()) { 110 throw new RuntimeException("File not found " + dirPath + " currentDir:" + new File(".").getAbsolutePath()); 111 } 112 113 final File[] files = d.listFiles(); 114 if (files != null) { 115 for (final File file : files) { 116 try { 117 if (file.isDirectory()) { 118 final String subDir = dir + "/" + file.getName(); 119 processPackage(subDir); 120 } else { 121 final String fileName = file.getName(); 122 if (fileName.endsWith(".java")) { 123 // possibly a common mistake... mixing .java and .class 124 System.err.println("Expecting a .class file but got " + fileName + " ... ignoring"); 125 126 } else if (fileName.endsWith(".class")) { 127 transformFile(file); 128 } 129 } 130 } catch (final Exception e) { 131 throw new RuntimeException("Error transforming file " + file.getName(), e); 132 } 133 } 134 } else { 135 throw new RuntimeException("Can't read directory " + d.getName()); 136 } 137 } 138 139 private void transformFile(File file) throws IOException, IllegalClassFormatException { 140 String className = getClassName(file); 141 try { 142 byte[] result = inputStreamTransform.transform(className, file); 143 if (result != null) { 144 InputStreamTransform.writeBytes(result, file); 145 if (listener != null && logLevel > 0) { 146 listener.logEvent("Enhanced " + file); 147 } 148 } 149 } catch (EnhancementException e) { 150 if (listener != null) { 151 listener.logError("Error enhancing class " + className + " " + e.getMessage()); 152 } else { 153 throw e; 154 } 155 } 156 } 157 158 private String getClassName(File file) { 159 String path = file.getPath(); 160 path = path.substring(inDir.length() + 1); 161 path = path.substring(0, path.length() - ".class".length()); 162 // for windows... replace the 163 return StringReplace.replace(path, "\\", "/"); 164 } 165}