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