001package io.ebean.enhance.common; 002 003import java.io.IOException; 004import java.io.InputStream; 005import java.net.URL; 006import java.util.Enumeration; 007import java.util.HashSet; 008import java.util.Set; 009import java.util.jar.Attributes; 010import java.util.jar.Manifest; 011 012/** 013 * Reads all the META-INF/ebean.mf and META-INF/ebean-typequery.mf resources with the locations 014 * of all the entity beans (and hence locations of query beans). 015 */ 016public class AgentManifest { 017 018 enum TxProfileMode { 019 NONE, 020 ENABLED, 021 MANUAL 022 } 023 024 private final Set<String> entityPackages = new HashSet<>(); 025 026 private final Set<String> transactionalPackages = new HashSet<>(); 027 028 private final Set<String> querybeanPackages = new HashSet<>(); 029 030 private TxProfileMode transactionProfilingMode = TxProfileMode.NONE; 031 032 /** 033 * Start profileId when automatically assigned by enhancement. 034 */ 035 private int transactionProfilingStart = 1000; 036 037 private int debugLevel = -1; 038 039 private boolean transientInternalFields; 040 041 private boolean checkNullManyFields = true; 042 043 private boolean enableProfileLocation; 044 045 private boolean enableQueryAutoLabel; 046 047 public static AgentManifest read(ClassLoader classLoader, Set<String> initialPackages) { 048 049 try { 050 return new AgentManifest(initialPackages) 051 .readManifests(classLoader, "META-INF/ebean-typequery.mf") 052 .readManifests(classLoader, "META-INF/ebean.mf") 053 .readManifests(classLoader, "ebean.mf"); 054 055 } catch (IOException e) { 056 // log to standard error and return empty 057 System.err.println("Agent: error reading ebean manifest resources"); 058 e.printStackTrace(); 059 return new AgentManifest(); 060 } 061 } 062 063 /** 064 * Construct with some packages defined externally. 065 */ 066 public AgentManifest(Set<String> initialPackages) { 067 if (initialPackages != null) { 068 entityPackages.addAll(initialPackages); 069 } 070 } 071 072 /** 073 * Construct with no initial packages (to use with addRaw()). 074 */ 075 public AgentManifest() { 076 } 077 078 @Override 079 public String toString() { 080 return "entityPackages:" + entityPackages + " querybeanPackages:" + querybeanPackages 081 + " transactionalPackages:" + transactionalPackages + " profilingMode:" + transactionProfilingMode; 082 } 083 084 /** 085 * Return true if enhancement of profileLocations should be added. 086 */ 087 public boolean isEnableProfileLocation() { 088 return enableProfileLocation; 089 } 090 091 /** 092 * Return true if enhancement should add labels to query bean queries. 093 */ 094 public boolean isEnableQueryAutoLabel() { 095 return enableQueryAutoLabel; 096 } 097 098 /** 099 * Return the initial starting profileId when automatically assigned. 100 */ 101 int transactionProfilingStart() { 102 switch (transactionProfilingMode) { 103 case NONE: 104 return -1; 105 case MANUAL: 106 return 0; 107 case ENABLED: 108 return transactionProfilingStart; 109 default: { 110 return transactionProfilingStart; 111 } 112 } 113 } 114 115 /** 116 * Return the debug level read from ebean.mf 117 */ 118 public int getDebugLevel() { 119 return debugLevel; 120 } 121 122 /** 123 * Return the parsed set of packages that type query beans are in. 124 */ 125 public Set<String> getEntityPackages() { 126 return entityPackages; 127 } 128 129 /** 130 * Return true if transactional enhancement is turned off. 131 */ 132 public boolean isTransactionalNone() { 133 return transactionalPackages.contains("none") && transactionalPackages.size() == 1; 134 } 135 136 /** 137 * Return true if we should use transient internal fields. 138 */ 139 public boolean isTransientInternalFields() { 140 return transientInternalFields; 141 } 142 143 /** 144 * Return false if enhancement should skip checking for null many fields. 145 */ 146 public boolean isCheckNullManyFields() { 147 return checkNullManyFields; 148 } 149 150 /** 151 * Return true if query bean enhancement is turned off. 152 */ 153 public boolean isQueryBeanNone() { 154 return querybeanPackages.contains("none") && querybeanPackages.size() == 1; 155 } 156 157 /** 158 * Return the packages that should be enhanced for transactional. 159 * An empty set means all packages are scanned for transaction classes and methods. 160 */ 161 public Set<String> getTransactionalPackages() { 162 return transactionalPackages; 163 } 164 165 /** 166 * Return the packages that should be enhanced for query bean use. 167 * An empty set means all packages are scanned for transaction classes and methods. 168 */ 169 public Set<String> getQuerybeanPackages() { 170 return querybeanPackages; 171 } 172 173 /** 174 * Read all the specific manifest files and return the set of packages containing type query beans. 175 */ 176 AgentManifest readManifests(ClassLoader classLoader, String path) throws IOException { 177 Enumeration<URL> resources = classLoader.getResources(path); 178 while (resources.hasMoreElements()) { 179 URL url = resources.nextElement(); 180 try { 181 addResource(UrlHelper.openNoCache(url)); 182 } catch (IOException e) { 183 System.err.println("Error reading manifest resources " + url); 184 e.printStackTrace(); 185 } 186 } 187 return this; 188 } 189 190 /** 191 * Add given the manifest InputStream. 192 */ 193 public void addResource(InputStream is) throws IOException { 194 try { 195 addManifest(new Manifest(is)); 196 } finally { 197 try { 198 is.close(); 199 } catch (IOException e) { 200 System.err.println("Error closing manifest resource"); 201 e.printStackTrace(); 202 } 203 } 204 } 205 206 void readProfilingMode(Attributes attributes) { 207 208 String debug = attributes.getValue("debug"); 209 if (debug != null) { 210 debugLevel = Integer.parseInt(debug); 211 } 212 213 String locationMode = attributes.getValue("profile-location"); 214 if (locationMode != null) { 215 enableProfileLocation = Boolean.parseBoolean(locationMode); 216 } 217 218 String queryLabelMode = attributes.getValue("query-labels"); 219 if (queryLabelMode != null) { 220 enableQueryAutoLabel = Boolean.parseBoolean(queryLabelMode); 221 } 222 223 String mode = attributes.getValue("transaction-profiling"); 224 if (mode != null) { 225 transactionProfilingMode = parseMode(mode); 226 } 227 } 228 229 private TxProfileMode parseMode(String mode) { 230 switch (mode.trim().toLowerCase()) { 231 case "enabled": 232 case "auto": 233 case "enable": 234 return TxProfileMode.ENABLED; 235 case "manual": 236 return TxProfileMode.MANUAL; 237 case "none": 238 return TxProfileMode.NONE; 239 default: 240 return TxProfileMode.NONE; 241 } 242 } 243 244 void readProfilingStart(Attributes attributes) { 245 String start = attributes.getValue("transaction-profiling-startvalue"); 246 if (start != null) { 247 try { 248 transactionProfilingStart = Integer.parseInt(start); 249 } catch (NumberFormatException e) { 250 // ignore 251 } 252 } 253 } 254 255 private void addManifest(Manifest manifest) { 256 Attributes attributes = manifest.getMainAttributes(); 257 readProfilingMode(attributes); 258 readProfilingStart(attributes); 259 readOptions(attributes); 260 261 add(entityPackages, attributes.getValue("packages")); 262 add(entityPackages, attributes.getValue("entity-packages")); 263 add(transactionalPackages, attributes.getValue("transactional-packages")); 264 add(querybeanPackages, attributes.getValue("querybean-packages")); 265 } 266 267 private void readOptions(Attributes attributes) { 268 transientInternalFields = bool("transient-internal-fields", transientInternalFields, attributes); 269 checkNullManyFields = bool("check-null-many-fields", checkNullManyFields, attributes); 270 } 271 272 private boolean bool(String key, boolean defaultValue, Attributes attributes) { 273 String val = attributes.getValue(key); 274 return val != null ? Boolean.parseBoolean(val) : defaultValue; 275 } 276 277 /** 278 * Collect each individual package splitting by delimiters. 279 */ 280 private void add(Set<String> addTo, String packages) { 281 if (packages != null) { 282 String[] split = packages.split(",|;| "); 283 for (String aSplit : split) { 284 String pkg = aSplit.trim(); 285 if (!pkg.isEmpty()) { 286 addTo.add(pkg); 287 } 288 } 289 } 290 } 291}