diff --git a/.gitignore b/.gitignore
index bb406be3c..51330a2eb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@
target/
logs/
log/
+ebean-profiling*.xml
/db
/mydb.db
!src/test/ddl-review/*.sql
diff --git a/ebean-autotune.xml b/ebean-autotune.xml
new file mode 100644
index 000000000..53be9c98e
--- /dev/null
+++ b/ebean-autotune.xml
@@ -0,0 +1,37 @@
+
+
- * After this amount of profiling has been obtained profiling is collected at - * the Profiling Percentage rate. - *
- */ - void setProfilingBase(int profilingBase); - - /** - * Return the minimum number of queries profiled before autoFetch will start - * automatically tuning the queries. - *- * This could be one which means start autoFetch tuning after the first - * profiling information is collected. - *
- */ - int getProfilingMin(); - - /** - * Set the minimum number of queries profiled per query point before autoFetch - * will automatically tune the queries. - *- * Increasing this number will mean more profiling is collected before - * autoFetch starts tuning the query. - *
- */ - void setProfilingMin(int autoFetchMinThreshold); - - /** - * Fire a garbage collection (hint to the JVM). Assuming garbage collection - * fires this will gather the usage profiling information. - */ - String collectUsageViaGC(); - - /** - * This will take the current profiling information and update the "tuned - * query detail". - *- * This is done periodically and can also be manually invoked. - *
- * - * @return a summary of the updates that occurred - */ - String updateTunedQueryInfo(); - - /** - * Clear all the tuned query info. - *- * Should only need do this for testing and playing around. - *
- * - * @return the amount of tuned query information cleared. - */ - int clearTunedQueryInfo(); - - /** - * Clear all the profiling information. - *- * This means the profiling information will need to be re-gathered. - *
- *- * Should only need do this for testing and playing around. - *
- * - * @return the amount of profiled information cleared. - */ - int clearProfilingInfo(); - - /** - * Clear the query execution statistics. - */ - void clearQueryStatistics(); - - /** - * Return the number of queries tuned by AutoFetch. - */ - int getTotalTunedQueryCount(); - - /** - * Return the size of the TuneQuery map. - */ - int getTotalTunedQuerySize(); - - /** - * Return the size of the profile map. - */ - int getTotalProfileSize(); - -} \ No newline at end of file diff --git a/src/main/java/com/avaje/ebean/AutoTune.java b/src/main/java/com/avaje/ebean/AutoTune.java new file mode 100644 index 000000000..c5d17ac55 --- /dev/null +++ b/src/main/java/com/avaje/ebean/AutoTune.java @@ -0,0 +1,14 @@ +package com.avaje.ebean; + +/** + * Administrative control of AutoTune during runtime. + */ +public interface AutoTune { + + /** + * Fire a garbage collection (hint to the JVM). Assuming garbage collection + * fires this will gather remaining usage profiling information. + */ + void collectProfiling(); + +} diff --git a/src/main/java/com/avaje/ebean/EbeanServer.java b/src/main/java/com/avaje/ebean/EbeanServer.java index 3f19306c8..327d89275 100644 --- a/src/main/java/com/avaje/ebean/EbeanServer.java +++ b/src/main/java/com/avaje/ebean/EbeanServer.java @@ -106,10 +106,9 @@ public interface EbeanServer { void shutdown(boolean shutdownDataSource, boolean deregisterDriver); /** - * Return the AdminAutofetch which is used to control and configure the - * Autofetch service at runtime. + * Return AutoTune which is used to control the AutoTune service at runtime. */ - AdminAutofetch getAdminAutofetch(); + AutoTune getAutoTune(); /** * Return the name. This is used with {@link Ebean#getServer(String)} to get a diff --git a/src/main/java/com/avaje/ebean/bean/CallStack.java b/src/main/java/com/avaje/ebean/bean/CallStack.java index 0f4b78f0c..f283ea591 100644 --- a/src/main/java/com/avaje/ebean/bean/CallStack.java +++ b/src/main/java/com/avaje/ebean/bean/CallStack.java @@ -87,6 +87,17 @@ public final class CallStack implements Serializable { return zeroHash + ":" + pathHash + ":" + callStack[0]; } + /** + * Return the call stack lines appended with the given newLine string. + */ + public String description(String newLine) { + StringBuilder sb = new StringBuilder(400); + for (int i = 0; i < callStack.length; i++) { + sb.append(callStack[i].toString()).append(newLine); + } + return sb.toString(); + } + public String getOriginKey(int queryHash) { return zeroHash + "." + enc(queryHash) + "." + pathHash; } diff --git a/src/main/java/com/avaje/ebean/bean/NodeUsageCollector.java b/src/main/java/com/avaje/ebean/bean/NodeUsageCollector.java index 42dc52c8c..594f38b98 100644 --- a/src/main/java/com/avaje/ebean/bean/NodeUsageCollector.java +++ b/src/main/java/com/avaje/ebean/bean/NodeUsageCollector.java @@ -1,7 +1,8 @@ package com.avaje.ebean.bean; import java.lang.ref.WeakReference; -import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Set; /** * Collects profile information for a bean (or reference/proxy bean) at a given @@ -29,7 +30,7 @@ public final class NodeUsageCollector { /** * The properties used at this profile point. */ - private final HashSet+ * If this is false then the version property will be added when profiling + * detects that the bean is possibly going to be modified. + *
+ */ + public boolean isQueryTuningAddVersion() { + return queryTuningAddVersion; + } + + /** + * Set to true to force the version property to be always added by the query + * tuning. + *+ * If this is false then the version property will be added when profiling + * detects that the bean is possibly going to be modified. + *
+ *+ * Generally this is not expected to be turned on. + *
+ */ + public void setQueryTuningAddVersion(boolean queryTuningAddVersion) { + this.queryTuningAddVersion = queryTuningAddVersion; + } + + /** + * Return true if profiling information should be collected. + */ + public boolean isProfiling() { + return profiling; + } + + /** + * Set to true if profiling information should be collected. + *+ * The profiling information is collected and then used to generate the tuned + * queries for autofetch. + *
+ */ + public void setProfiling(boolean profiling) { + this.profiling = profiling; + } + + /** + * Return the base number of queries to profile before changing to profile + * only a percentage of following queries (profileRate). + */ + public int getProfilingBase() { + return profilingBase; + } + + /** + * Set the based number of queries to profile. + */ + public void setProfilingBase(int profilingBase) { + this.profilingBase = profilingBase; + } + + /** + * Return the rate (%) of queries to be profiled after the 'base' amount of + * profiling. + */ + public double getProfilingRate() { + return profilingRate; + } + + /** + * Set the rate (%) of queries to be profiled after the 'base' amount of + * profiling. + */ + public void setProfilingRate(double profilingRate) { + this.profilingRate = profilingRate; + } + + /** + * Return the time in millis to wait after a system gc to collect profiling + * information. + *+ * The profiling information is collected on object finalise. As such we + * generally don't want to trigger GC (let the JVM do its thing) but on + * shutdown the autoTune manager will trigger System.gc() and then wait + * (default 100 millis) to hopefully collect profiling information - + * especially for short run unit tests. + *
+ */ + public int getGarbageCollectionWait() { + return garbageCollectionWait; + } + + /** + * Set the time in millis to wait after a System.gc() to collect profiling information. + */ + public void setGarbageCollectionWait(int garbageCollectionWait) { + this.garbageCollectionWait = garbageCollectionWait; + } + + /** + * Return true if profiling collection should be skipped on shutdown. + */ + public boolean isSkipCollectionOnShutdown() { + return skipCollectionOnShutdown; + } + + /** + * Set to true if profiling collection should be skipped on shutdown. + */ + public void setSkipCollectionOnShutdown(boolean skipCollectionOnShutdown) { + this.skipCollectionOnShutdown = skipCollectionOnShutdown; + } + + /** + * Load the settings from the properties file. + */ + public void loadSettings(PropertiesWrapper p) { + + queryTuning = p.getBoolean("autoTune.queryTuning", queryTuning); + queryTuningAddVersion = p.getBoolean("autoTune.queryTuningAddVersion", queryTuningAddVersion); + queryTuningFile = p.get("autoTune.queryTuningFile", queryTuningFile); + + skipCollectionOnShutdown = p.getBoolean("autoTune.skipCollectionOnShutdown", skipCollectionOnShutdown); + + mode = p.getEnum(AutoTuneMode.class, "autoTune.mode", mode); + + profiling = p.getBoolean("autoTune.profiling", profiling); + profilingBase = p.getInt("autoTune.profilingBase", profilingBase); + profilingRate = p.getDouble("autoTune.profilingRate", profilingRate); + profilingFile = p.get("autoTune.profilingFile", profilingFile); + } +} diff --git a/src/main/java/com/avaje/ebean/config/AutofetchMode.java b/src/main/java/com/avaje/ebean/config/AutoTuneMode.java similarity index 96% rename from src/main/java/com/avaje/ebean/config/AutofetchMode.java rename to src/main/java/com/avaje/ebean/config/AutoTuneMode.java index 86dcf95db..b255421ff 100644 --- a/src/main/java/com/avaje/ebean/config/AutofetchMode.java +++ b/src/main/java/com/avaje/ebean/config/AutoTuneMode.java @@ -11,7 +11,7 @@ import com.avaje.ebean.Query; * query. * */ -public enum AutofetchMode { +public enum AutoTuneMode { /** * Don't implicitly use Autofetch. Must explicitly turn it on. diff --git a/src/main/java/com/avaje/ebean/config/AutofetchConfig.java b/src/main/java/com/avaje/ebean/config/AutofetchConfig.java deleted file mode 100644 index 02dbdda38..000000000 --- a/src/main/java/com/avaje/ebean/config/AutofetchConfig.java +++ /dev/null @@ -1,245 +0,0 @@ -package com.avaje.ebean.config; - -/** - * Defines the Autofetch behaviour for a EbeanServer. - */ -public class AutofetchConfig { - - private AutofetchMode mode = AutofetchMode.DEFAULT_ONIFEMPTY; - - private boolean queryTuning = false; - - private boolean queryTuningAddVersion = false; - - private boolean profiling = false; - - private int profilingMin = 1; - - private int profilingBase = 10; - - private double profilingRate = 0.05; - - private String logDirectory; - - private int profileUpdateFrequency = 60; - - private int garbageCollectionWait = 100; - - private boolean garbageCollectionOnShutdown; - - public AutofetchConfig() { - } - - /** - * Return the mode used when autofetch has not been explicit defined on a - * query. - */ - public AutofetchMode getMode() { - return mode; - } - - /** - * Set the mode used when autofetch has not been explicit defined on a query. - */ - public void setMode(AutofetchMode mode) { - this.mode = mode; - } - - /** - * Return true if the queries are being tuned. - */ - public boolean isQueryTuning() { - return queryTuning; - } - - /** - * Set to true if the queries should be tuned by autofetch. - */ - public void setQueryTuning(boolean queryTuning) { - this.queryTuning = queryTuning; - } - - /** - * Return true if the version property should be added when the query is - * tuned. - *- * If this is false then the version property will be added when profiling - * detects that the bean is possibly going to be modified. - *
- */ - public boolean isQueryTuningAddVersion() { - return queryTuningAddVersion; - } - - /** - * Set to true to force the version property to be always added by the query - * tuning. - *- * If this is false then the version property will be added when profiling - * detects that the bean is possibly going to be modified. - *
- */ - public void setQueryTuningAddVersion(boolean queryTuningAddVersion) { - this.queryTuningAddVersion = queryTuningAddVersion; - } - - /** - * Return true if profiling information should be collected. - */ - public boolean isProfiling() { - return profiling; - } - - /** - * Set to true if profiling information should be collected. - *- * The profiling information is collected and then used to generate the tuned - * queries for autofetch. - *
- */ - public void setProfiling(boolean profiling) { - this.profiling = profiling; - } - - /** - * Return the minimum number of queries to profile before autofetch will start - * tuning the queries. - */ - public int getProfilingMin() { - return profilingMin; - } - - /** - * Set the minimum number of queries to profile before autofetch will start - * tuning the queries. - */ - public void setProfilingMin(int profilingMin) { - this.profilingMin = profilingMin; - } - - /** - * Return the base number of queries to profile before changing to profile - * only a percentage of following queries (profileRate). - */ - public int getProfilingBase() { - return profilingBase; - } - - /** - * Set the based number of queries to profile. - */ - public void setProfilingBase(int profilingBase) { - this.profilingBase = profilingBase; - } - - /** - * Return the rate (%) of queries to be profiled after the 'base' amount of - * profiling. - */ - public double getProfilingRate() { - return profilingRate; - } - - /** - * Set the rate (%) of queries to be profiled after the 'base' amount of - * profiling. - */ - public void setProfilingRate(double profilingRate) { - this.profilingRate = profilingRate; - } - - /** - * Return the log directory to put the autofetch log. - */ - public String getLogDirectory() { - return logDirectory; - } - - /** - * Set the directory to put the autofetch log in. - */ - public void setLogDirectory(String logDirectory) { - this.logDirectory = logDirectory; - } - - /** - * Return the frequency in seconds to update the autofetch tuned queries from - * the profiled information. - */ - public int getProfileUpdateFrequency() { - return profileUpdateFrequency; - } - - /** - * Set the frequency in seconds to update the autofetch tuned queries from the - * profiled information. - */ - public void setProfileUpdateFrequency(int profileUpdateFrequency) { - this.profileUpdateFrequency = profileUpdateFrequency; - } - - /** - * Return the time in millis to wait after a system gc to collect profiling - * information. - *- * The profiling information is collected on object finalise. As such we - * generally don't want to trigger GC (let the JVM do its thing) but on - * shutdown the autofetch manager will trigger System.gc() and then wait - * (default 100 millis) to hopefully collect profiling information - - * especially for short run unit tests. - *
- */ - public int getGarbageCollectionWait() { - return garbageCollectionWait; - } - - /** - * Set the time in millis to wait after a System.gc() to collect profiling - * information. - */ - public void setGarbageCollectionWait(int garbageCollectionWait) { - this.garbageCollectionWait = garbageCollectionWait; - } - - - /** - * Return true if GC should be trigger on shutdown. - *- * Autofetch profiling information is collected as part of garbage collection. - *
- */ - public boolean isGarbageCollectionOnShutdown() { - return garbageCollectionOnShutdown; - } - - /** - * Set to true if you want GC to trigger on shutdown. - *- * This would be done if you want to try and collect Autofetch profiling information - * on shutdown. - *
- */ - public void setGarbageCollectionOnShutdown(boolean garbageCollectionOnShutdown) { - this.garbageCollectionOnShutdown = garbageCollectionOnShutdown; - } - - /** - * Load the settings from the properties file. - */ - public void loadSettings(PropertiesWrapper p) { - - logDirectory = p.get("autofetch.logDirectory", logDirectory); - queryTuning = p.getBoolean("autofetch.querytuning", queryTuning); - queryTuningAddVersion = p.getBoolean("autofetch.queryTuningAddVersion", queryTuningAddVersion); - garbageCollectionOnShutdown = p.getBoolean("autofetch.garbageCollectionOnShutdown", garbageCollectionOnShutdown); - - profiling = p.getBoolean("autofetch.profiling", profiling); - mode = p.getEnum(AutofetchMode.class, "autofetch.implicitmode", mode); - - profilingMin = p.getInt("autofetch.profiling.min", profilingMin); - profilingBase = p.getInt("autofetch.profiling.base", profilingBase); - - profilingRate = p.getDouble("autofetch.profiling.rate", profilingRate); - profileUpdateFrequency = p.getInt("autofetch.profiling.updatefrequency", profileUpdateFrequency); - } -} diff --git a/src/main/java/com/avaje/ebean/config/ServerConfig.java b/src/main/java/com/avaje/ebean/config/ServerConfig.java index 7a89b00f2..927caa954 100644 --- a/src/main/java/com/avaje/ebean/config/ServerConfig.java +++ b/src/main/java/com/avaje/ebean/config/ServerConfig.java @@ -132,7 +132,7 @@ public class ServerConfig { /** * Config controlling the autofetch behaviour. */ - private AutofetchConfig autofetchConfig = new AutofetchConfig(); + private AutoTuneConfig autoTuneConfig = new AutoTuneConfig(); /** * The JSON format used for DateTime types. Default to millis. @@ -1144,17 +1144,17 @@ public class ServerConfig { } /** - * Return the configuration for the Autofetch feature. + * Return the configuration for AutoTune. */ - public AutofetchConfig getAutofetchConfig() { - return autofetchConfig; + public AutoTuneConfig getAutoTuneConfig() { + return autoTuneConfig; } /** - * Set the configuration for the Autofetch feature. + * Set the configuration for AutoTune. */ - public void setAutofetchConfig(AutofetchConfig autofetchConfig) { - this.autofetchConfig = autofetchConfig; + public void setAutoTuneConfig(AutoTuneConfig autoTuneConfig) { + this.autoTuneConfig = autoTuneConfig; } /** @@ -2042,8 +2042,8 @@ public class ServerConfig { /** * This is broken out for the same reason as above - preserve existing behaviour but let it be overridden. */ - protected void loadAutofetchSettings(PropertiesWrapper p) { - autofetchConfig.loadSettings(p); + protected void loadAutoTuneSettings(PropertiesWrapper p) { + autoTuneConfig.loadSettings(p); } /** @@ -2057,10 +2057,10 @@ public class ServerConfig { if (namingConvention != null) { namingConvention.loadFromProperties(p); } - if (autofetchConfig == null) { - autofetchConfig = new AutofetchConfig(); + if (autoTuneConfig == null) { + autoTuneConfig = new AutoTuneConfig(); } - loadAutofetchSettings(p); + loadAutoTuneSettings(p); if (dataSourceConfig == null) { dataSourceConfig = new DataSourceConfig(); diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java b/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java index f987355ec..fc85650f9 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java +++ b/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java @@ -13,7 +13,6 @@ import com.avaje.ebean.config.ServerConfig; import com.avaje.ebean.config.dbplatform.DatabasePlatform; import com.avaje.ebean.event.readaudit.ReadAuditLogger; import com.avaje.ebean.event.readaudit.ReadAuditPrepare; -import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest; import com.avaje.ebean.dbmigration.DdlGenerator; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; @@ -72,11 +71,6 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL */ DdlGenerator getDdlGenerator(); - /** - * Return the AutoFetchListener. - */ - AutoFetchManager getAutoFetchManager(); - /** * Clear the query execution statistics. */ diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java b/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java index a1b52d7bc..ea9f0fccb 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java @@ -11,7 +11,7 @@ import com.avaje.ebean.bean.ObjectGraphNode; import com.avaje.ebean.bean.PersistenceContext; import com.avaje.ebean.event.BeanQueryRequest; import com.avaje.ebean.event.readaudit.ReadEvent; -import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; +import com.avaje.ebeaninternal.server.autofetch.ProfilingListener; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; import com.avaje.ebeaninternal.server.deploy.TableJoin; @@ -371,16 +371,16 @@ public interface SpiQuery- * The profile information is periodically converted into "tuned query details" - - * which is used to automatically tune the queries that use autoFetch. - *
- *- * The "tuned query details" effectively are part of the query that has the - * select() and join() information (but not the where clause, order by, limits - * etc). These are applied to the query when tuneQuery() is called. - *
- */ -public interface AutoFetchManager extends NodeUsageListener { - - /** - * Set the owning ebean server. - */ - void setOwner(SpiEbeanServer server, ServerConfig serverConfig); - - /** - * Clear the query execution statistics. - */ - void clearQueryStatistics(); - - /** - * Clear all the tuned query info. - *- * Should only need do this for testing and playing around. - *
- */ - int clearTunedQueryInfo(); - - /** - * Clear all the profiling information. - *- * This means the profiling information will need to be re-gathered. - *
- *- * Should only need do this for testing and playing around. - *
- */ - int clearProfilingInfo(); - - /** - * On shutdown fire garbage collection and collect statistics. Note that - * usually we add a little delay (100 milliseconds) to give the garbage - * collector plenty of time to do its thing and collect the profile - * information. - */ - void shutdown(); - - /** - * Return the current tuned fetch information for a given queryPoint key. - */ - TunedQueryInfo getTunedQueryInfo(String queryPointKey); - - /** - * Return the current Statistics for a given queryPoint key. - */ - Statistics getStatistics(String queryPointKey); - - /** - * Iterate the tuned fetch info. - *- * This should be a read only iteration. - *
- */ - Iterator- * This should be a read only iteration. - *
- */ - Iterator- * We rely on garbage collection to collect the profiling information. This - * means there is a unknown delay between when a query is executed and when - * we actually collect the usage profile information. - *
- *- * Due to this garbage collection delay, when turning off profiling while - * the application is running you should consider calling - * collectUsageViaGC() BEFORE setProfiling(false). This hints to - * the JVM to perform garbage collection, and hopefully collects the - * profiling information. - *
- */ - void setProfiling(boolean enable); - - /** - * Return true if automatic query tuning is enabled. - */ - boolean isQueryTuning(); - - /** - * Set to true to enable automatic query tuning. - */ - void setQueryTuning(boolean enable); - - /** - * This controls whether autoFetch is used when it has not been explicitly - * set on a query via {@link Query#setAutofetch(boolean)}. - */ - AutofetchMode getMode(); - - /** - * Set the auto fetch mode used when a query has not had - * {@link Query#setAutofetch(boolean)}. - */ - void setMode(AutofetchMode Mode); - - /** - * Return the profiling rate (int between 0 and 100). - */ - double getProfilingRate(); - - /** - * Set the profiling rate (int between 0 and 100). - */ - void setProfilingRate(double rate); - - /** - * Return the max number of queries profiled (per query point). - *- * The number of queries profiled is collected per query point. Once a query - * point has profiled this number of queries it does not profile any more. - *
- */ - int getProfilingBase(); - - /** - * Set a max number of queries to profile per query point. - *- * This number should provide a level of confidence that no more profiling - * is required for this query point. - *
- */ - void setProfilingBase(int profilingMax); - - /** - * Return the minimum number of queries profiled before autoFetch will start - * automatically tuning the queries. - *- * This could be one which means start autoFetch tuning after the first - * profiling information is collected. - *
- */ - int getProfilingMin(); - - /** - * Set the minimum number of queries profiled per query point before - * autoFetch will automatically tune the queries. - *- * Increasing this number will mean more profiling is collected before - * autoFetch starts tuning the query. - *
- */ - void setProfilingMin(int autoFetchMinThreshold); - - /** - * Fire a garbage collection (hint to the JVM). Assuming garbage collection - * fires this will gather the usage profiling information. - */ - String collectUsageViaGC(long waitMillis); - - /** - * This will take the current profiling information and update the "tuned - * query detail". - *- * This is done periodically and can also be manually invoked. - *
- *- * This returns a string summary of the updates that occurred. - *
- */ - String updateTunedQueryInfo(); - - /** - * Called when a query thinks it should be automatically tuned by autoFetch. - *- * This internally checks that autoFetch is enabled, there is a "tuned query - * detail" to tune the query with and that the autoFetchMinThreshold has - * been reached. - *
- *- * This will also determine if the query should be profiled. - *
- */ - boolean tuneQuery(SpiQuery> query); - - /** - * Collect query profiling information. - *- * This is for the original query as well as any subsequent lazy loading - * queries that are required as the object graph is traversed. - *
- * - * @param node the node path in the object graph. - * @param beans the number of beans loaded by the query. - * @param micros the query executing time in microseconds - */ - void collectQueryInfo(ObjectGraphNode node, long beans, long micros); - - - /** - * Return the number of queries tuned by AutoFetch. - */ - int getTotalTunedQueryCount(); - - /** - * Return the size of the TuneQuery map. - */ - int getTotalTunedQuerySize(); - - /** - * Return the size of the profile map. - */ - int getTotalProfileSize(); -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManagerFactory.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManagerFactory.java deleted file mode 100644 index b19a4ee37..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManagerFactory.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.avaje.ebeaninternal.server.autofetch; - -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.server.resource.ResourceManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.persistence.PersistenceException; -import java.io.File; -import java.io.FileInputStream; -import java.io.ObjectInputStream; - -public class AutoFetchManagerFactory { - - private static final Logger logger = LoggerFactory.getLogger(AutoFetchManagerFactory.class); - - public static AutoFetchManager create(SpiEbeanServer server, ServerConfig serverConfig, ResourceManager resourceManager) { - - AutoFetchManagerFactory me = new AutoFetchManagerFactory(); - return me.createAutoFetchManager(server, serverConfig, resourceManager); - } - - private AutoFetchManager createAutoFetchManager(SpiEbeanServer server, ServerConfig serverConfig, ResourceManager resourceManager) { - - AutoFetchManager manager = createAutoFetchManager(server.getName(), resourceManager); - manager.setOwner(server, serverConfig); - - return manager; - } - - private AutoFetchManager createAutoFetchManager(String serverName, ResourceManager resourceManager) { - - File autoFetchFile = getAutoFetchFile(serverName, resourceManager); - - AutoFetchManager autoFetchManager = null; - - boolean readFile = !"false".equalsIgnoreCase(System.getProperty("autofetch.readfromfile")); - if (readFile) { - autoFetchManager = deserializeAutoFetch(autoFetchFile); - } - - if (autoFetchManager == null) { - // not deserialized from file so create as empty - // It will be populated automatically by querying the - // database meta data - autoFetchManager = new DefaultAutoFetchManager(autoFetchFile.getAbsolutePath()); - } - - return autoFetchManager; - } - - private AutoFetchManager deserializeAutoFetch(File autoFetchFile) { - try { - - if (!autoFetchFile.exists()) { - return null; - } - FileInputStream fi = new FileInputStream(autoFetchFile); - ObjectInputStream ois = new ObjectInputStream(fi); - AutoFetchManager profListener = (AutoFetchManager) ois.readObject(); - ois.close(); - - logger.info("AutoFetch deserialized from file [" + autoFetchFile.getAbsolutePath() + "]"); - - return profListener; - - } catch (Exception ex) { - logger.error("Error loading autofetch file " + autoFetchFile.getAbsolutePath(), ex); - return null; - } - } - - /** - * Return the file name of the autoFetch meta data. - */ - private File getAutoFetchFile(String serverName, ResourceManager resourceManager) { - - String fileName = ".ebean." + serverName + ".autofetch"; - - File dir = resourceManager.getAutofetchDirectory(); - - if (!dir.exists()) { - // automatically create the directory if it does not exist. - // this is probably a fairly reasonable thing to do - if (!dir.mkdirs()) { - String m = "Unable to create directory [" + dir + "] for autofetch file [" + fileName + "]"; - throw new PersistenceException(m); - } - } - - return new File(dir, fileName); - } - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneCollection.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneCollection.java new file mode 100644 index 000000000..98285d9d3 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneCollection.java @@ -0,0 +1,115 @@ +package com.avaje.ebeaninternal.server.autofetch; + +import com.avaje.ebean.bean.ObjectGraphOrigin; +import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; + +import java.util.ArrayList; +import java.util.List; + +/** + * Profiling information collected. + */ +public class AutoTuneCollection { + + List+ * The profile information is periodically converted into "tuned query details" - + * which is used to automatically tune the queries that use autoFetch. + *
+ *+ * The "tuned query details" effectively are part of the query that has the + * select() and join() information (but not the where clause, order by, limits + * etc). These are applied to the query when tuneQuery() is called. + *
+ */ +public interface AutoTuneService extends AutoTune { + + /** + * Load the query tuning information. + */ + void startup(); + + /** + * Called when a query thinks it should be automatically tuned by autoFetch. + *+ * This internally checks that autoFetch is enabled, there is a "tuned query + * detail" to tune the query with and that the autoFetchMinThreshold has + * been reached. + *
+ *+ * This will also determine if the query should be profiled. + *
+ */ + boolean tuneQuery(SpiQuery> query); + + /** + * Fire a garbage collection (hint to the JVM). Assuming garbage collection + * fires this will gather the usage profiling information. + */ + void collectProfiling(); + + /** + * On shutdown fire garbage collection and collect statistics. Note that + * usually we add a little delay (100 milliseconds) to give the garbage + * collector plenty of time to do its thing and collect the profile + * information. + */ + void shutdown(); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneStorage.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneStorage.java new file mode 100644 index 000000000..831cdeee2 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneStorage.java @@ -0,0 +1,17 @@ +package com.avaje.ebeaninternal.server.autofetch; + +/** + * + */ +public interface AutoTuneStorage { + + /** + * Load and return the tuning information. + */ + AutoTuneCollection load(); + + /** + * Store the collected profiling information. + */ + void store(AutoTuneCollection profiling); +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManager.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManager.java deleted file mode 100644 index 35dd210ba..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManager.java +++ /dev/null @@ -1,587 +0,0 @@ -package com.avaje.ebeaninternal.server.autofetch; - -import com.avaje.ebean.bean.CallStack; -import com.avaje.ebean.bean.NodeUsageCollector; -import com.avaje.ebean.bean.ObjectGraphNode; -import com.avaje.ebean.bean.ObjectGraphOrigin; -import com.avaje.ebean.config.AutofetchConfig; -import com.avaje.ebean.config.AutofetchMode; -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebeaninternal.api.ClassUtil; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.persistence.PersistenceException; -import java.io.File; -import java.io.FileOutputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * The manager of all the usage/query statistics as well as the tuned fetch - * information. - */ -public class DefaultAutoFetchManager implements AutoFetchManager, Serializable { - - private static final Logger logger = LoggerFactory.getLogger(DefaultAutoFetchManager.class); - - private static final long serialVersionUID = -6826119882781771722L; - - @SuppressWarnings("RedundantStringConstructorCall") - private final String statisticsMonitor = new String(); - - private final String fileName; - - /** - * Map of the usage and query statistics gathered. - */ - private final Map- * We should try to collect the usage statistics by calling a System.gc(). - * This is necessary for use with short lived applications where garbage - * collection may not otherwise occur at all. - *
- */ - public void shutdown() { - if (garbageCollectionOnShutdown) { - collectUsageViaGC(-1); - serialize(); - } - } - - /** - * Ask for a System.gc() so that we gather node usage information. - *- * Really only want to do this sparingly but useful just prior to shutdown - * for short run application where garbage collection may otherwise not - * occur at all. - *
- *- * waitMillis will do a thread sleep to give the garbage collection a little - * time to do its thing assuming we are shutting down the VM. - *
- *- * If waitMillis is -1 then the defaultGarbageCollectionWait is used which - * defaults to 100 milliseconds. - *
- */ - public String collectUsageViaGC(long waitMillis) { - System.gc(); - try { - if (waitMillis < 0) { - waitMillis = defaultGarbageCollectionWait; - } - Thread.sleep(waitMillis); - } catch (InterruptedException e) { - String msg = "Error while sleeping after System.gc() request."; - logging.logError(msg, e); - return msg; - } - return updateTunedQueryInfo(); - } - - /** - * Update the tuned fetch plans from the current usage information. - */ - public String updateTunedQueryInfo() { - - if (!profiling) { - // we are not collecting any profiling information at - // the moment so don't try updating the tuned query plans. - return "Not profiling"; - } - - synchronized (statisticsMonitor) { - - Counters counters = new Counters(); - - for (Statistics queryPointStatistics : statisticsMap.values()) { - if (!queryPointStatistics.hasUsage()) { - // no usage statistics collected yet... - counters.incrementNoUsage(); - } else { - updateTunedQueryFromUsage(counters, queryPointStatistics); - } - } - - String summaryInfo = counters.toString(); - - if (counters.isInteresting()) { - // only log it if its interesting - logging.logSummary(summaryInfo); - } - - return summaryInfo; - } - } - - private static class Counters { - - int newPlan; - int modified; - int unchanged; - int noUsage; - - void incrementNoUsage() { - noUsage++; - } - - void incrementNew() { - newPlan++; - } - - void incrementModified() { - modified++; - } - - void incrementUnchanged() { - unchanged++; - } - - boolean isInteresting() { - return newPlan > 0 || modified > 0; - } - - public String toString() { - return "new[" + newPlan + "] modified[" + modified + "] unchanged[" + unchanged + "] nousage[" + noUsage + "]"; - } - } - - private void updateTunedQueryFromUsage(Counters counters, Statistics statistics) { - - ObjectGraphOrigin queryPoint = statistics.getOrigin(); - String beanType = queryPoint.getBeanType(); - - try { - Class> beanClass = ClassUtil.forName(beanType, this.getClass()); - BeanDescriptor> beanDescriptor = server.getBeanDescriptor(beanClass); - if (beanDescriptor != null) { - - // Determine the fetch plan from the latest statistics. - // Use this to compare with current "tuned fetch plan". - OrmQueryDetail newFetchDetail = statistics.buildTunedFetch(beanDescriptor); - - // get the current tuned fetch info... - TunedQueryInfo currentFetch = tunedQueryInfoMap.get(queryPoint.getKey()); - - if (currentFetch == null) { - // its a new fetch plan, add it. - counters.incrementNew(); - - currentFetch = statistics.createTunedFetch(newFetchDetail); - logging.logNew(currentFetch); - tunedQueryInfoMap.put(queryPoint.getKey(), currentFetch); - - } else if (!currentFetch.isSame(newFetchDetail)) { - // the fetch plan has changed, update it. - counters.incrementModified(); - - logging.logChanged(currentFetch, newFetchDetail); - currentFetch.setTunedDetail(newFetchDetail); - - } else { - // the fetch plan has not changed... - counters.incrementUnchanged(); - } - - currentFetch.setProfileCount(statistics.getCounter()); - } - - } catch (ClassNotFoundException e) { - // expected after renaming/moving an entity bean - String msg = e.toString() + " updating autoFetch tuned query for " + beanType - + ". It isLikely this bean has been renamed or moved"; - logging.logInfo(msg, null); - statisticsMap.remove(statistics.getOrigin().getKey()); - } - } - - /** - * Return true if we should try to use autoFetch for this query. - */ - private boolean useAutoFetch(SpiQuery> query) { - - if (query.isLoadBeanCache()) { - // when loading the cache don't tune the query - // as we want full objects loaded into the cache - return false; - } - - Boolean autoFetch = query.isAutofetch(); - if (autoFetch != null) { - // explicitly set... - return autoFetch; - - } else { - // determine using implicit mode... - switch (mode) { - case DEFAULT_ON: - return true; - - case DEFAULT_OFF: - return false; - - case DEFAULT_ONIFEMPTY: - return query.isDetailEmpty(); - - default: - throw new PersistenceException("Invalid autoFetchMode " + mode); - } - } - } - - /** - * Auto tune the query and enable profiling. - */ - public boolean tuneQuery(SpiQuery> query) { - - if (!queryTuning && !profiling) { - return false; - } - - if (!useAutoFetch(query)) { - // not using autoFetch for this query - return false; - } - - ObjectGraphNode parentAutoFetchNode = query.getParentNode(); - if (parentAutoFetchNode != null) { - // This is a +lazy/+query query with profiling on. - // We continue to collect the profiling information. - query.setAutoFetchManager(this); - return true; - } - - // create a query point to identify the query - CallStack stack = server.createCallStack(); - ObjectGraphNode origin = query.setOrigin(stack); - - // get current "tuned fetch" for this query point - TunedQueryInfo tunedFetch = tunedQueryInfoMap.get(origin.getOriginQueryPoint().getKey()); - - // get the number of times we have collected profiling information - int profileCount = tunedFetch == null ? 0 : tunedFetch.getProfileCount(); - - if (profiling) { - // we want more profiling information? - if (tunedFetch == null) { - query.setAutoFetchManager(this); - - } else if (profileCount < profilingBase) { - query.setAutoFetchManager(this); - - } else if (tunedFetch.isPercentageProfile(profilingRate)) { - query.setAutoFetchManager(this); - } - } - - if (queryTuning) { - if (tunedFetch != null && profileCount >= profilingMin) { - // deemed to have enough profiling - // information for automatic tuning - if (tunedFetch.autoFetchTune(query)) { - // tunedQueryCount++ not thread-safe, could use AtomicInteger. - // But I'm happy if this statistic is a little wrong - // and this is a VERY HOT method - tunedQueryCount++; - } - return true; - } - } - - return false; - } - - /** - * Gather query execution statistics. This could either be the originating - * query in which case the parentNode will be null, or a lazy loading query - * resulting from traversal of the object graph. - */ - public void collectQueryInfo(ObjectGraphNode node, long beans, long micros) { - - if (node != null) { - ObjectGraphOrigin origin = node.getOriginQueryPoint(); - if (origin != null) { - Statistics stats = getQueryPointStats(origin); - stats.collectQueryInfo(node, beans, micros); - } - } - } - - /** - * Collect usage statistics from a node in the object graph. - *- * This is sent to use from a EntityBeanIntercept when the finalise method - * is called on the bean. - *
- */ - public void collectNodeUsage(NodeUsageCollector usageCollector) { - - ObjectGraphOrigin origin = usageCollector.getNode().getOriginQueryPoint(); - - Statistics stats = getQueryPointStats(origin); - - if (logger.isTraceEnabled()) { - logger.trace("... NodeUsageCollector " + usageCollector); - } - - stats.collectUsageInfo(usageCollector); - - if (logger.isTraceEnabled()) { - logger.trace("stats\n" + stats); - } - } - - private Statistics getQueryPointStats(ObjectGraphOrigin originQueryPoint) { - synchronized (statisticsMonitor) { - Statistics stats = statisticsMap.get(originQueryPoint.getKey()); - if (stats == null) { - stats = new Statistics(originQueryPoint, queryTuningAddVersion); - statisticsMap.put(originQueryPoint.getKey(), stats); - } - return stats; - } - } - - public String toString() { - synchronized (statisticsMonitor) { - return statisticsMap.values().toString(); - } - } - - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManagerLogging.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManagerLogging.java deleted file mode 100644 index f1cbb183b..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManagerLogging.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.avaje.ebeaninternal.server.autofetch; - -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.concurrent.TimeUnit; - -/** - * Handles the logging aspects for the DefaultAutoFetchListener. - *- * Note that java util logging loggers generally should not be serialised and - * that is one of the main reasons for pulling out the logging to this class. - *
- */ -public class DefaultAutoFetchManagerLogging { - - private static final Logger logger = LoggerFactory.getLogger(DefaultAutoFetchManagerLogging.class); - - private final DefaultAutoFetchManager manager; - - private final int updateFreqInSecs; - - public DefaultAutoFetchManagerLogging(ServerConfig serverConfig, DefaultAutoFetchManager profileListener) { - - this.manager = profileListener; - this.updateFreqInSecs = serverConfig.getAutofetchConfig().getProfileUpdateFrequency(); - } - - public void init(SpiEbeanServer ebeanServer) { - ebeanServer.getBackgroundExecutor().executePeriodically(new UpdateProfile(), updateFreqInSecs, TimeUnit.SECONDS); - } - - private final class UpdateProfile implements Runnable { - public void run() { - manager.updateTunedQueryInfo(); - } - } - - public void logInfo(String msg, Throwable e) { - logger.info(msg, e); - } - - public void logError(String msg, Throwable e) { - logger.error(msg, e); - } - - public void logSummary(String summaryInfo) { - - String msg = "\"Summary\",\"" + summaryInfo + "\",,,,"; - logger.debug(msg); - } - - public void logChanged(TunedQueryInfo tunedFetch, OrmQueryDetail newQueryDetail) { - - String msg = tunedFetch.getLogOutput(newQueryDetail); - logger.debug(msg); - } - - public void logNew(TunedQueryInfo tunedFetch) { - - String msg = tunedFetch.getLogOutput(null); - logger.debug(msg); - } -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/ProfilingListener.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/ProfilingListener.java new file mode 100644 index 000000000..f24300b91 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/ProfilingListener.java @@ -0,0 +1,26 @@ +package com.avaje.ebeaninternal.server.autofetch; + +import com.avaje.ebean.bean.NodeUsageListener; +import com.avaje.ebean.bean.ObjectGraphNode; +import com.avaje.ebeaninternal.api.SpiQuery; + +/** + * Profiling listener gets call backs for node usage and the associated query executions. + */ +public interface ProfilingListener extends NodeUsageListener { + + /** + * Collect summary statistics for a query executed for the given node. + * + * @param node the node relative to the origin point + * @param beans the number of beans loaded by the query + * @param micros the query execution in microseconds + */ + void collectQueryInfo(ObjectGraphNode node, long beans, long micros); + + /** + * Return true if this request should be profiled (based on the + * profiling ratio and collection count for this origin). + */ + boolean isProfileRequest(ObjectGraphNode origin, SpiQuery> query); +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/Statistics.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/Statistics.java deleted file mode 100644 index a9625ca70..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/Statistics.java +++ /dev/null @@ -1,174 +0,0 @@ -package com.avaje.ebeaninternal.server.autofetch; - -import com.avaje.ebean.bean.NodeUsageCollector; -import com.avaje.ebean.bean.ObjectGraphNode; -import com.avaje.ebean.bean.ObjectGraphOrigin; -import com.avaje.ebean.text.PathProperties; -import com.avaje.ebean.text.PathProperties.Props; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; - -import java.io.Serializable; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; - -public class Statistics implements Serializable { - - - private static final long serialVersionUID = -5586783791097230766L; - - private final ObjectGraphOrigin origin; - - private final boolean queryTuningAddVersion; - - private int counter; - - private final Map- * This tells us how much profiling we have done for this query. - * For example, after 100 times we may stop collecting more profiling info. - *
- */ - public int getCounter() { - return counter; - } - - /** - * Return true if this has usage statistics. - */ - public boolean hasUsage() { - synchronized (monitor) { - return !nodeUsageMap.isEmpty(); - } - } - - public OrmQueryDetail buildTunedFetch(BeanDescriptor> rootDesc) { - - synchronized (monitor) { - if (nodeUsageMap.isEmpty()) { - return null; - } - - PathProperties pathProps = new PathProperties(); - - for (StatisticsNodeUsage statsNode : nodeUsageMap.values()) { - statsNode.buildTunedFetch(pathProps, rootDesc); - } - - OrmQueryDetail detail = new OrmQueryDetail(); - - CollectionJava class for anonymous complex type. + * + *
The following schema fragment specifies the expected content contained within this class. + * + *
+ * <complexType>
+ * <complexContent>
+ * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ * <sequence>
+ * <element ref="{http://ebean-orm.github.io/xml/ns/autotune}origin" maxOccurs="unbounded" minOccurs="0"/>
+ * <element ref="{http://ebean-orm.github.io/xml/ns/autotune}profileDiff" minOccurs="0"/>
+ * <element ref="{http://ebean-orm.github.io/xml/ns/autotune}profileNew" minOccurs="0"/>
+ * </sequence>
+ * </restriction>
+ * </complexContent>
+ * </complexType>
+ *
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "", propOrder = {
+ "origin",
+ "profileDiff",
+ "profileNew"
+})
+@XmlRootElement(name = "autotune")
+public class Autotune {
+
+ protected List
+ * This accessor method returns a reference to the live list,
+ * not a snapshot. Therefore any modification you make to the
+ * returned list will be present inside the JAXB object.
+ * This is why there is not a set method for the origin property.
+ *
+ *
+ * For example, to add a new item, do as follows: + *
+ * getOrigin().add(newItem); + *+ * + * + *
+ * Objects of the following type(s) are allowed in the list
+ * {@link Origin }
+ *
+ *
+ */
+ public List An ObjectFactory allows you to programatically
+ * construct new instances of the Java representation
+ * for XML content. The Java representation of XML
+ * content can consist of schema derived interfaces
+ * and classes representing the binding of schema
+ * type definitions, element declarations and model
+ * groups. Factory methods for each of these are
+ * provided in this class.
+ *
+ */
+@XmlRegistry
+public class ObjectFactory {
+
+
+ /**
+ * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.avaje.ebeaninternal.server.autofetch.model
+ *
+ */
+ public ObjectFactory() {
+ }
+
+ /**
+ * Create an instance of {@link ProfileNew }
+ *
+ */
+ public ProfileNew createProfileNew() {
+ return new ProfileNew();
+ }
+
+ /**
+ * Create an instance of {@link Origin }
+ *
+ */
+ public Origin createOrigin() {
+ return new Origin();
+ }
+
+ /**
+ * Create an instance of {@link Autotune }
+ *
+ */
+ public Autotune createAutotune() {
+ return new Autotune();
+ }
+
+ /**
+ * Create an instance of {@link ProfileDiff }
+ *
+ */
+ public ProfileDiff createProfileDiff() {
+ return new ProfileDiff();
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/Origin.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/Origin.java
new file mode 100644
index 000000000..eb2203c92
--- /dev/null
+++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/Origin.java
@@ -0,0 +1,171 @@
+
+package com.avaje.ebeaninternal.server.autofetch.model;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+
+/**
+ * Java class for anonymous complex type.
+ *
+ * The following schema fragment specifies the expected content contained within this class.
+ *
+ * Java class for anonymous complex type.
+ *
+ * The following schema fragment specifies the expected content contained within this class.
+ *
+ *
+ * This accessor method returns a reference to the live list,
+ * not a snapshot. Therefore any modification you make to the
+ * returned list will be present inside the JAXB object.
+ * This is why there is not a
+ * For example, to add a new item, do as follows:
+ *
+ * Objects of the following type(s) are allowed in the list
+ * {@link Origin }
+ *
+ *
+ */
+ public List Java class for anonymous complex type.
+ *
+ * The following schema fragment specifies the expected content contained within this class.
+ *
+ *
+ * This accessor method returns a reference to the live list,
+ * not a snapshot. Therefore any modification you make to the
+ * returned list will be present inside the JAXB object.
+ * This is why there is not a
+ * For example, to add a new item, do as follows:
+ *
+ * Objects of the following type(s) are allowed in the list
+ * {@link Origin }
+ *
+ *
+ */
+ public List
+ * We should try to collect the usage statistics by calling a System.gc().
+ * This is necessary for use with short lived applications where garbage
+ * collection may not otherwise occur at all.
+ *
+ * Really only want to do this sparingly but useful just prior to shutdown
+ * for short run application where garbage collection may otherwise not
+ * occur at all.
+ *
+ * waitMillis will do a thread sleep to give the garbage collection a little
+ * time to do its thing assuming we are shutting down the VM.
+ *
+ * If waitMillis is -1 then the defaultGarbageCollectionWait is used which
+ * defaults to 100 milliseconds.
+ *
+ * This is sent to use from a EntityBeanIntercept when the finalise method
+ * is called on the bean.
+ *
+ * This can give us a quick overview into bad lazy loading areas etc.
+ *
- * This is accessible via {@link EbeanServer#getAdminAutofetch()} or via JMX
- * MBeans.
- *
- * The number of queries profiled is collected per query point. Once a query
- * point has profiled this number of queries it does not profile any more.
- *
- * This number should provide a level of confidence that no more profiling
- * is required for this query point.
- *
- * This could be one which means start autoFetch tuning after the first
- * profiling information is collected.
- *
- * Increasing this number will mean more profiling is collected before
- * autoFetch starts tuning the query.
- *
- * This is done periodically and can also be manually invoked.
- *
- * Should only need do this for testing and playing around.
- *
- * This means the profiling information will need to be re-gathered.
- *
- * Should only need do this for testing and playing around.
- *
- * For example, used to find the WEB-INF directory starting from the current
- * working directory.
- *
- * This does not return the full path of the file, but the path relative to
- * the FileIoSource directory.
- *
- * Typically either content from a File or a URL.
- *
- * Typically a File System Directory based source or a ServletContext URL
- * resource based source (for Servlet WAR files).
- *
- * This will return null IF the ResourceSource is an unpacked WAR file.
- *
-A service used to read deployment content such as xml files, images etc
-taking into account the environment (servlet war file or file system).
-
- * This can use URL based resource loading for web applications or file based
- * otherwise.
- *
- * This can be url based (for webapps) or otherwise file based.
- *
+ * <complexType>
+ * <complexContent>
+ * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ * <sequence>
+ * <element name="callStack" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
+ * </sequence>
+ * <attribute name="key" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+ * <attribute name="beanType" type="{http://www.w3.org/2001/XMLSchema}string" />
+ * <attribute name="detail" type="{http://www.w3.org/2001/XMLSchema}string" />
+ * <attribute name="original" type="{http://www.w3.org/2001/XMLSchema}string" />
+ * </restriction>
+ * </complexContent>
+ * </complexType>
+ *
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "", propOrder = {
+ "callStack"
+})
+@XmlRootElement(name = "origin")
+public class Origin {
+
+ protected String callStack;
+ @XmlAttribute(name = "key", required = true)
+ protected String key;
+ @XmlAttribute(name = "beanType")
+ protected String beanType;
+ @XmlAttribute(name = "detail")
+ protected String detail;
+ @XmlAttribute(name = "original")
+ protected String original;
+
+ /**
+ * Gets the value of the callStack property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getCallStack() {
+ return callStack;
+ }
+
+ /**
+ * Sets the value of the callStack property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setCallStack(String value) {
+ this.callStack = value;
+ }
+
+ /**
+ * Gets the value of the key property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getKey() {
+ return key;
+ }
+
+ /**
+ * Sets the value of the key property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setKey(String value) {
+ this.key = value;
+ }
+
+ /**
+ * Gets the value of the beanType property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getBeanType() {
+ return beanType;
+ }
+
+ /**
+ * Sets the value of the beanType property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setBeanType(String value) {
+ this.beanType = value;
+ }
+
+ /**
+ * Gets the value of the detail property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getDetail() {
+ return detail;
+ }
+
+ /**
+ * Sets the value of the detail property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setDetail(String value) {
+ this.detail = value;
+ }
+
+ /**
+ * Gets the value of the original property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getOriginal() {
+ return original;
+ }
+
+ /**
+ * Sets the value of the original property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setOriginal(String value) {
+ this.original = value;
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/ProfileDiff.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/ProfileDiff.java
new file mode 100644
index 000000000..a1bc1c91b
--- /dev/null
+++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/ProfileDiff.java
@@ -0,0 +1,69 @@
+
+package com.avaje.ebeaninternal.server.autofetch.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+
+/**
+ *
+ * <complexType>
+ * <complexContent>
+ * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ * <sequence>
+ * <element ref="{http://ebean-orm.github.io/xml/ns/autotune}origin" maxOccurs="unbounded" minOccurs="0"/>
+ * </sequence>
+ * </restriction>
+ * </complexContent>
+ * </complexType>
+ *
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "", propOrder = {
+ "origin"
+})
+@XmlRootElement(name = "profileDiff")
+public class ProfileDiff {
+
+ protected Listset method for the origin property.
+ *
+ *
+ * getOrigin().add(newItem);
+ *
+ *
+ *
+ *
+ * <complexType>
+ * <complexContent>
+ * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ * <sequence>
+ * <element ref="{http://ebean-orm.github.io/xml/ns/autotune}origin" maxOccurs="unbounded" minOccurs="0"/>
+ * </sequence>
+ * </restriction>
+ * </complexContent>
+ * </complexType>
+ *
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "", propOrder = {
+ "origin"
+})
+@XmlRootElement(name = "profileNew")
+public class ProfileNew {
+
+ protected Listset method for the origin property.
+ *
+ *
+ * getOrigin().add(newItem);
+ *
+ *
+ *
+ *
- *
- * // search to a depth of 3 from the current working directory
- * // looking for a directory WEB-INF that contains the subdirectory
- * // data
- *
- * File dir = DirectoryFinder.find(null, "WEB-INF/data", 3);
- * if (dir != null) {
- * //found the directory
- * }
- *
- */
- public static File find(File startDir, String match, int maxDepth) {
-
- String matchSub = null;
- int slashPos = match.indexOf('/');
- if (slashPos > -1) {
- // match has sub directories
- matchSub = match.substring(slashPos + 1);
- match = match.substring(0, slashPos);
- }
-
- // search for the directory
- File found = find(startDir, match, matchSub, 0, maxDepth);
-
- if (found != null && matchSub != null) {
- // match has sub directories
- return new File(found, matchSub);
- }
- return found;
- }
-
- private static File find(File dir, String match, String matchSub, int depth, int maxDepth) {
-
- if (dir == null) {
- String curDir = System.getProperty("user.dir");
- dir = new File(curDir);
- }
-
- if (dir.exists()) {
- File[] list = dir.listFiles();
- if (list != null){
- for (int i = 0; i < list.length; i++) {
- if (isMatch(list[i], match, matchSub)) {
- return list[i];
- }
- }
-
- // go through the directories again
- // Aka *NOT* a depth first search
- if (depth < maxDepth) {
- for (int i = 0; i < list.length; i++) {
- if (list[i].isDirectory()) {
- File found = find(list[i], match, matchSub, depth + 1, maxDepth);
- if (found != null) {
- return found;
- }
- }
- }
- }
- }
- }
- return null;
- }
-
- private static boolean isMatch(File f, String match, String matchSub) {
- if (f == null) {
- return false;
- }
- if (!f.isDirectory()) {
- return false;
- }
- if (!f.getName().equalsIgnoreCase(match)) {
- return false;
- }
- if (matchSub == null) {
- return true;
- }
- File sub = new File(f, matchSub);
- if (logger.isTraceEnabled()){
- logger.trace("search; " + f.getPath());
- }
- return sub.exists();
-
- }
-}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceContent.java b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceContent.java
deleted file mode 100644
index b2f6cb07e..000000000
--- a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceContent.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package com.avaje.ebeaninternal.server.lib.resource;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Date;
-
-/**
- * Content from a file system file.
- */
-public class FileResourceContent implements ResourceContent {
-
- /**
- * The underlying file.
- */
- final File file;
-
- final String entryName;
-
- /**
- * Create with a File and the entryName.
- */
- public FileResourceContent(File file, String entryName) {
- this.file = file;
- this.entryName = entryName;
- }
-
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("[").append(getName());
- sb.append("] size[").append(size());
- sb.append("] lastModified[").append(new Date(lastModified()));
- sb.append("]");
- return sb.toString();
- }
-
- /**
- * Returns the entry name which contains the path from the base directory.
- *