iterateStatistics();
-
- /**
- * Return true if profiling is enabled.
- */
- boolean isProfiling();
-
- /**
- * Set to true to enable profiling.
- *
- * 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/AutoTuneService.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneService.java
new file mode 100644
index 000000000..291cfe9f3
--- /dev/null
+++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneService.java
@@ -0,0 +1,63 @@
+package com.avaje.ebeaninternal.server.autofetch;
+
+import com.avaje.ebean.AdminAutofetch;
+import com.avaje.ebean.config.ServerConfig;
+import com.avaje.ebeaninternal.api.SpiEbeanServer;
+import com.avaje.ebeaninternal.api.SpiQuery;
+
+/**
+ * Collects and manages the the profile information.
+ *
+ * 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 AdminAutofetch {
+
+ /**
+ * 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 collectUsageViaGC();
+
+ /**
+ * This will take the current profiling information and update the "tuned
+ * query detail".
+ *
+ * This is done periodically and can also be manually invoked.
+ *
+ */
+ void updateTunedQueryInfo();
+
+ /**
+ * 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/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 statisticsMap = new ConcurrentHashMap();
-
- /**
- * Map of the tuned query details per profile query point.
- */
- private final Map tunedQueryInfoMap = new ConcurrentHashMap();
-
- private transient long defaultGarbageCollectionWait = 100;
-
- /**
- * Left without synchronized for now.
- */
- private transient int tunedQueryCount;
-
- /**
- * Converted from a 0-100 int to a double. Effectively a percentage rate at
- * which to collect profiling information.
- */
- private transient double profilingRate = 0.1d;
-
- private transient int profilingBase = 10;
-
- private transient int profilingMin = 1;
-
- private transient boolean profiling;
-
- private transient boolean queryTuning;
-
- private transient boolean queryTuningAddVersion;
-
- private transient boolean garbageCollectionOnShutdown;
-
- private transient AutofetchMode mode;
-
- /**
- * Server that owns this Profile Listener.
- */
- private transient SpiEbeanServer server;
-
- /**
- * The logger.
- */
- private transient DefaultAutoFetchManagerLogging logging;
-
- public DefaultAutoFetchManager(String fileName) {
- this.fileName = fileName;
- }
-
- /**
- * Set up this profile listener before it is active.
- */
- public void setOwner(SpiEbeanServer server, ServerConfig serverConfig) {
- this.server = server;
- this.logging = new DefaultAutoFetchManagerLogging(serverConfig, this);
-
- AutofetchConfig autofetchConfig = serverConfig.getAutofetchConfig();
-
- garbageCollectionOnShutdown = autofetchConfig.isGarbageCollectionOnShutdown();
- queryTuning = autofetchConfig.isQueryTuning();
- queryTuningAddVersion = autofetchConfig.isQueryTuningAddVersion();
- profiling = autofetchConfig.isProfiling();
- profilingMin = autofetchConfig.getProfilingMin();
- profilingBase = autofetchConfig.getProfilingBase();
-
- setProfilingRate(autofetchConfig.getProfilingRate());
-
- defaultGarbageCollectionWait = (long) autofetchConfig.getGarbageCollectionWait();
-
- // determine the mode to use when Query.setAutoFetch() was
- // not explicitly set
- mode = autofetchConfig.getMode();
-
- if (profiling || queryTuning) {
- // log the guts of the autoFetch setup
- String msg = "AutoFetch queryTuning[" + queryTuning + "] profiling[" + profiling
- + "] mode[" + mode + "] profiling rate[" + profilingRate
- + "] min[" + profilingMin + "] base[" + profilingBase + "]";
- logging.logInfo(msg, null);
-
- // Register a periodic update of the profiling informations
- this.logging.init(server);
- }
- }
-
-
- public void clearQueryStatistics() {
- server.clearQueryStatistics();
- }
-
- /**
- * Return the number of queries tuned by AutoFetch.
- */
- public int getTotalTunedQueryCount() {
- return tunedQueryCount;
- }
-
- /**
- * Return the size of the TuneQuery map.
- */
- public int getTotalTunedQuerySize() {
- return tunedQueryInfoMap.size();
- }
-
- /**
- * Return the size of the profile map.
- */
- public int getTotalProfileSize() {
- return statisticsMap.size();
- }
-
- public int clearTunedQueryInfo() {
-
- // reset the rough count as well
- tunedQueryCount = 0;
-
- // clear the map...
- int size = tunedQueryInfoMap.size();
- tunedQueryInfoMap.clear();
- return size;
- }
-
- public int clearProfilingInfo() {
- int size = statisticsMap.size();
- statisticsMap.clear();
- return size;
- }
-
-
- public void serialize() {
-
- File autoFetchFile = new File(fileName);
-
- try {
- FileOutputStream fout = new FileOutputStream(autoFetchFile);
-
- ObjectOutputStream oout = new ObjectOutputStream(fout);
- oout.writeObject(this);
- oout.flush();
- oout.close();
-
- } catch (Exception e) {
- String msg = "Error serializing autofetch file";
- logging.logError(msg, e);
- }
- }
-
- /**
- * Return the current Tuned query info for a given origin key.
- */
- public TunedQueryInfo getTunedQueryInfo(String originKey) {
- return tunedQueryInfoMap.get(originKey);
- }
-
- /**
- * Return the current Statistics for a given originKey key.
- */
- public Statistics getStatistics(String originKey) {
- return statisticsMap.get(originKey);
- }
-
- public Iterator iterateTunedQueryInfo() {
- return tunedQueryInfoMap.values().iterator();
- }
-
- public Iterator iterateStatistics() {
- return statisticsMap.values().iterator();
- }
-
- public boolean isProfiling() {
- return profiling;
- }
-
- /**
- * When the application is running, BEFORE turning off profiling you
- * probably should call collectUsageViaGC() as there is a delay (waiting for
- * garbage collection) collecting usage profiling information.
- */
- public void setProfiling(boolean profiling) {
- this.profiling = profiling;
- }
-
- public boolean isQueryTuning() {
- return queryTuning;
- }
-
- public void setQueryTuning(boolean queryTuning) {
- this.queryTuning = queryTuning;
- }
-
- public double getProfilingRate() {
- return profilingRate;
- }
-
- public AutofetchMode getMode() {
- return mode;
- }
-
- public void setMode(AutofetchMode mode) {
- this.mode = mode;
- }
-
- public void setProfilingRate(double rate) {
- if (rate < 0) {
- rate = 0d;
- } else if (rate > 1) {
- rate = 1d;
- }
- profilingRate = rate;
- }
-
- public int getProfilingBase() {
- return profilingBase;
- }
-
- public void setProfilingBase(int profilingBase) {
- this.profilingBase = profilingBase;
- }
-
- public int getProfilingMin() {
- return profilingMin;
- }
-
- public void setProfilingMin(int profilingMin) {
- this.profilingMin = profilingMin;
- }
-
- /**
- * Shutdown the listener.
- *
- * 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/ProfilingListener.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/ProfilingListener.java
new file mode 100644
index 000000000..9885f7435
--- /dev/null
+++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/ProfilingListener.java
@@ -0,0 +1,13 @@
+package com.avaje.ebeaninternal.server.autofetch;
+
+import com.avaje.ebean.bean.NodeUsageListener;
+import com.avaje.ebean.bean.ObjectGraphNode;
+
+/**
+ * Profiling listener gets call backs for node usage and the associated query executions.
+ */
+public interface ProfilingListener extends NodeUsageListener {
+
+ void collectQueryInfo(ObjectGraphNode node, long beans, long micros);
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/package.html b/src/main/java/com/avaje/ebeaninternal/server/autofetch/package.html
deleted file mode 100644
index f0e805a5c..000000000
--- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/package.html
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- AutoFetch Implementation
-
-
-AutoFetch Implementation
-
-
\ No newline at end of file
diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManagerFactory.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/AutoTuneServiceFactory.java
similarity index 69%
rename from src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManagerFactory.java
rename to src/main/java/com/avaje/ebeaninternal/server/autofetch/service/AutoTuneServiceFactory.java
index b19a4ee37..1e6c2d1cd 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManagerFactory.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/AutoTuneServiceFactory.java
@@ -1,7 +1,8 @@
-package com.avaje.ebeaninternal.server.autofetch;
+package com.avaje.ebeaninternal.server.autofetch.service;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
+import com.avaje.ebeaninternal.server.autofetch.AutoTuneService;
import com.avaje.ebeaninternal.server.resource.ResourceManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -11,29 +12,29 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
-public class AutoFetchManagerFactory {
+public class AutoTuneServiceFactory {
- private static final Logger logger = LoggerFactory.getLogger(AutoFetchManagerFactory.class);
+ private static final Logger logger = LoggerFactory.getLogger(AutoTuneServiceFactory.class);
- public static AutoFetchManager create(SpiEbeanServer server, ServerConfig serverConfig, ResourceManager resourceManager) {
+ public static AutoTuneService create(SpiEbeanServer server, ServerConfig serverConfig, ResourceManager resourceManager) {
- AutoFetchManagerFactory me = new AutoFetchManagerFactory();
+ AutoTuneServiceFactory me = new AutoTuneServiceFactory();
return me.createAutoFetchManager(server, serverConfig, resourceManager);
}
- private AutoFetchManager createAutoFetchManager(SpiEbeanServer server, ServerConfig serverConfig, ResourceManager resourceManager) {
+ private AutoTuneService createAutoFetchManager(SpiEbeanServer server, ServerConfig serverConfig, ResourceManager resourceManager) {
- AutoFetchManager manager = createAutoFetchManager(server.getName(), resourceManager);
+ AutoTuneService manager = createAutoFetchManager(server.getName(), resourceManager);
manager.setOwner(server, serverConfig);
return manager;
}
- private AutoFetchManager createAutoFetchManager(String serverName, ResourceManager resourceManager) {
+ private AutoTuneService createAutoFetchManager(String serverName, ResourceManager resourceManager) {
File autoFetchFile = getAutoFetchFile(serverName, resourceManager);
- AutoFetchManager autoFetchManager = null;
+ AutoTuneService autoFetchManager = null;
boolean readFile = !"false".equalsIgnoreCase(System.getProperty("autofetch.readfromfile"));
if (readFile) {
@@ -44,13 +45,13 @@ public class AutoFetchManagerFactory {
// not deserialized from file so create as empty
// It will be populated automatically by querying the
// database meta data
- autoFetchManager = new DefaultAutoFetchManager(autoFetchFile.getAbsolutePath());
+ autoFetchManager = new BaseAutoTuneService(autoFetchFile.getAbsolutePath());
}
return autoFetchManager;
}
- private AutoFetchManager deserializeAutoFetch(File autoFetchFile) {
+ private AutoTuneService deserializeAutoFetch(File autoFetchFile) {
try {
if (!autoFetchFile.exists()) {
@@ -58,7 +59,7 @@ public class AutoFetchManagerFactory {
}
FileInputStream fi = new FileInputStream(autoFetchFile);
ObjectInputStream ois = new ObjectInputStream(fi);
- AutoFetchManager profListener = (AutoFetchManager) ois.readObject();
+ AutoTuneService profListener = (AutoTuneService) ois.readObject();
ois.close();
logger.info("AutoFetch deserialized from file [" + autoFetchFile.getAbsolutePath() + "]");
diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseAutoTuneService.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseAutoTuneService.java
new file mode 100644
index 000000000..81ee660b7
--- /dev/null
+++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseAutoTuneService.java
@@ -0,0 +1,207 @@
+package com.avaje.ebeaninternal.server.autofetch.service;
+
+import com.avaje.ebean.config.AutofetchConfig;
+import com.avaje.ebean.config.ServerConfig;
+import com.avaje.ebeaninternal.api.SpiEbeanServer;
+import com.avaje.ebeaninternal.api.SpiQuery;
+import com.avaje.ebeaninternal.server.autofetch.AutoTuneService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+/**
+ * Implementation of the AutoTuneService which is comprised of profiling and query tuning.
+ */
+public class BaseAutoTuneService implements AutoTuneService {
+
+ private static final Logger logger = LoggerFactory.getLogger(BaseAutoTuneService.class);
+
+ private final long defaultGarbageCollectionWait;
+
+ private final boolean garbageCollectionOnShutdown;
+
+ private final BaseQueryTuner queryTuner;
+
+ public BaseAutoTuneService(SpiEbeanServer server, ServerConfig serverConfig) {
+
+ AutofetchConfig config = serverConfig.getAutofetchConfig();
+
+ ProfileManager profileManager = new ProfileManager(config, server);
+ this.queryTuner = new BaseQueryTuner(config, server, profileManager);
+
+ this.garbageCollectionOnShutdown = config.isGarbageCollectionOnShutdown();
+ this.defaultGarbageCollectionWait = (long) config.getGarbageCollectionWait();
+ }
+
+ /**
+ * Load the query tuning information from it's data store.
+ */
+ public void startup() {
+
+// File autoFetchFile = new File(fileName);
+//
+// try {
+// FileOutputStream fout = new FileOutputStream(autoFetchFile);
+//
+// ObjectOutputStream oout = new ObjectOutputStream(fout);
+// oout.writeObject(this);
+// oout.flush();
+// oout.close();
+//
+// } catch (Exception e) {
+// String msg = "Error serializing autofetch file";
+// logging.logError(msg, e);
+// }
+ }
+
+ private void saveProfiling() {
+
+ }
+
+ /**
+ * Shutdown the listener.
+ *
+ * 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);
+ saveProfiling();
+ }
+ }
+
+ /**
+ * 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 void collectUsageViaGC() {
+ collectUsageViaGC(-1);
+ }
+
+ public void collectUsageViaGC(long waitMillis) {
+ System.gc();
+ try {
+ if (waitMillis < 0) {
+ waitMillis = defaultGarbageCollectionWait;
+ }
+ Thread.sleep(waitMillis);
+ } catch (InterruptedException e) {
+ logger.warn("Error while sleeping after System.gc() request.", e);
+ }
+ updateTunedQueryInfo();
+ }
+
+ /**
+ * Update the tuned fetch plans from the current usage information.
+ */
+ public void 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 (ProfileOrigin 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 void updateTunedQueryFromUsage(Counters counters, ProfileOrigin 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());
+// }
+// }
+//
+
+ /**
+ * Auto tune the query and enable profiling.
+ */
+ public boolean tuneQuery(SpiQuery> query) {
+ return queryTuner.tuneQuery(query);
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseQueryTuner.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseQueryTuner.java
new file mode 100644
index 000000000..a60f73fd9
--- /dev/null
+++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseQueryTuner.java
@@ -0,0 +1,150 @@
+package com.avaje.ebeaninternal.server.autofetch.service;
+
+import com.avaje.ebean.bean.CallStack;
+import com.avaje.ebean.bean.ObjectGraphNode;
+import com.avaje.ebean.config.AutofetchConfig;
+import com.avaje.ebean.config.AutofetchMode;
+import com.avaje.ebeaninternal.api.SpiEbeanServer;
+import com.avaje.ebeaninternal.api.SpiQuery;
+import com.avaje.ebeaninternal.server.autofetch.ProfilingListener;
+
+import javax.persistence.PersistenceException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ *
+ */
+public class BaseQueryTuner {
+
+ private final boolean queryTuning;
+
+ /**
+ * Converted from a 0-100 int to a double. Effectively a percentage rate at
+ * which to collect profiling information.
+ */
+ private final double profilingRate;
+
+ private final int profilingBase;
+
+ private final int profilingMin;
+
+ private boolean profiling;
+
+ private final AutofetchMode mode;
+
+ /**
+ * Map of the tuned query details per profile query point.
+ */
+ private final Map tunedQueryInfoMap = new ConcurrentHashMap();
+
+
+ private final SpiEbeanServer server;
+
+ private final ProfilingListener profilingListener;
+
+ public BaseQueryTuner(AutofetchConfig config, SpiEbeanServer server, ProfilingListener profilingListener) {
+ this.server = server;
+ this.profilingListener = profilingListener;
+ this.mode = config.getMode();
+ this.queryTuning = config.isQueryTuning();
+ this.profiling = config.isProfiling();
+ this.profilingRate = config.getProfilingRate();
+ this.profilingBase = config.getProfilingBase();
+ this.profilingMin = config.getProfilingMin();
+ }
+
+ /**
+ * Load the tuned query information.
+ */
+ public void load(String key, TunedQueryInfo queryInfo) {
+ tunedQueryInfoMap.put(key, queryInfo);
+ }
+
+ /**
+ * 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.setProfilingListener(profilingListener);
+ 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.setProfilingListener(profilingListener);
+
+ } else if (profileCount < profilingBase) {
+ query.setProfilingListener(profilingListener);
+
+ } else if (tunedFetch.isPercentageProfile(profilingRate)) {
+ query.setProfilingListener(profilingListener);
+ }
+ }
+
+ if (queryTuning && tunedFetch != null && profileCount >= profilingMin) {
+ // deemed to have enough profiling information for automatic tuning
+ return tunedFetch.autoFetchTune(query);
+ }
+
+ return false;
+ }
+
+ /**
+ * 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);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManagerLogging.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoFetchManagerLogging.java
similarity index 92%
rename from src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManagerLogging.java
rename to src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoFetchManagerLogging.java
index f1cbb183b..8ba42bb6b 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManagerLogging.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoFetchManagerLogging.java
@@ -1,4 +1,4 @@
-package com.avaje.ebeaninternal.server.autofetch;
+package com.avaje.ebeaninternal.server.autofetch.service;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
@@ -19,11 +19,11 @@ public class DefaultAutoFetchManagerLogging {
private static final Logger logger = LoggerFactory.getLogger(DefaultAutoFetchManagerLogging.class);
- private final DefaultAutoFetchManager manager;
+ private final BaseAutoTuneService manager;
private final int updateFreqInSecs;
- public DefaultAutoFetchManagerLogging(ServerConfig serverConfig, DefaultAutoFetchManager profileListener) {
+ public DefaultAutoFetchManagerLogging(ServerConfig serverConfig, BaseAutoTuneService profileListener) {
this.manager = profileListener;
this.updateFreqInSecs = serverConfig.getAutofetchConfig().getProfileUpdateFrequency();
diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileManager.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileManager.java
new file mode 100644
index 000000000..a9bada622
--- /dev/null
+++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileManager.java
@@ -0,0 +1,112 @@
+package com.avaje.ebeaninternal.server.autofetch.service;
+
+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.ebeaninternal.api.SpiEbeanServer;
+import com.avaje.ebeaninternal.server.autofetch.ProfilingListener;
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ *
+ */
+public class ProfileManager implements ProfilingListener {
+
+ private static final Logger logger = LoggerFactory.getLogger(ProfileManager.class);
+
+ private final boolean queryTuningAddVersion;
+
+ private final boolean profiling;
+
+ /**
+ * Map of the usage and query statistics gathered.
+ */
+ private final Map profileMap = new ConcurrentHashMap();
+
+ private final Object monitor = new Object();
+
+ private final SpiEbeanServer server;
+
+ public ProfileManager(AutofetchConfig config, SpiEbeanServer server) {
+ this.server = server;
+ this.profiling = config.isProfiling();
+ this.queryTuningAddVersion = config.isQueryTuningAddVersion();
+ }
+
+ /**
+ * 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) {
+ ProfileOrigin stats = getProfileOrigin(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) {
+
+ ProfileOrigin profileOrigin = getProfileOrigin(usageCollector.getNode().getOriginQueryPoint());
+ profileOrigin.collectUsageInfo(usageCollector);
+ }
+
+ private ProfileOrigin getProfileOrigin(ObjectGraphOrigin originQueryPoint) {
+ synchronized (monitor) {
+ ProfileOrigin stats = profileMap.get(originQueryPoint.getKey());
+ if (stats == null) {
+ stats = new ProfileOrigin(originQueryPoint, queryTuningAddVersion);
+ profileMap.put(originQueryPoint.getKey(), stats);
+ }
+ return stats;
+ }
+ }
+
+
+ /**
+ * Update the tuned fetch plans from the current usage information.
+ */
+ public void 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 (monitor) {
+
+ for (ProfileOrigin origin : profileMap.values()) {
+ if (origin.hasUsage()) {
+ OrmQueryDetail ormQueryDetail = updateTunedQueryFromUsage(origin);
+
+ }
+ }
+ }
+ }
+
+
+ private OrmQueryDetail updateTunedQueryFromUsage(ProfileOrigin statistics) {
+
+ BeanDescriptor> desc = server.getBeanDescriptorById(statistics.getOrigin().getBeanType());
+ return desc == null ? null : statistics.buildTunedFetch(desc);
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/Statistics.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOrigin.java
similarity index 52%
rename from src/main/java/com/avaje/ebeaninternal/server/autofetch/Statistics.java
rename to src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOrigin.java
index a9625ca70..959b740a2 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/Statistics.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOrigin.java
@@ -1,4 +1,4 @@
-package com.avaje.ebeaninternal.server.autofetch;
+package com.avaje.ebeaninternal.server.autofetch.service;
import com.avaje.ebean.bean.NodeUsageCollector;
import com.avaje.ebean.bean.ObjectGraphNode;
@@ -13,7 +13,7 @@ import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
-public class Statistics implements Serializable {
+public class ProfileOrigin implements Serializable {
private static final long serialVersionUID = -5586783791097230766L;
@@ -24,14 +24,14 @@ public class Statistics implements Serializable {
private int counter;
- private final Map queryStatsMap = new LinkedHashMap();
+ private final Map queryStatsMap = new LinkedHashMap();
- private final Map nodeUsageMap = new LinkedHashMap();
+ private final Map nodeUsageMap = new LinkedHashMap();
@SuppressWarnings("RedundantStringConstructorCall")
private final String monitor = new String();
- public Statistics(ObjectGraphOrigin origin, boolean queryTuningAddVersion) {
+ public ProfileOrigin(ObjectGraphOrigin origin, boolean queryTuningAddVersion) {
this.origin = origin;
this.queryTuningAddVersion = queryTuningAddVersion;
}
@@ -40,13 +40,13 @@ public class Statistics implements Serializable {
return origin;
}
- public TunedQueryInfo createTunedFetch(OrmQueryDetail newFetchDetail) {
- synchronized (monitor) {
- // NB: create a copy of queryPoint allowing garbage
- // collection of source...
- return new TunedQueryInfo(origin, newFetchDetail, counter);
- }
- }
+// public TunedQueryInfo createTunedFetch(OrmQueryDetail newFetchDetail) {
+// synchronized (monitor) {
+// // NB: create a copy of queryPoint allowing garbage
+// // collection of source...
+// return new TunedQueryInfo(origin, newFetchDetail, counter);
+// }
+// }
/**
* Return the number of times the root query has executed.
@@ -77,7 +77,7 @@ public class Statistics implements Serializable {
PathProperties pathProps = new PathProperties();
- for (StatisticsNodeUsage statsNode : nodeUsageMap.values()) {
+ for (ProfileOriginNodeUsage statsNode : nodeUsageMap.values()) {
statsNode.buildTunedFetch(pathProps, rootDesc);
}
@@ -98,23 +98,22 @@ public class Statistics implements Serializable {
public void collectQueryInfo(ObjectGraphNode node, long beansLoaded, long micros) {
- synchronized (monitor) {
- String key = node.getPath();
- if (key == null) {
- key = "";
- // this is basically the number of times the root query
- // has executed which gives us an indication of how
- // much profiling information we have gathered.
- counter++;
- }
-
- StatisticsQuery stats = queryStatsMap.get(key);
- if (stats == null) {
- stats = new StatisticsQuery(key);
- queryStatsMap.put(key, stats);
- }
- stats.add(beansLoaded, micros);
+ String key = node.getPath();
+ if (key == null) {
+ key = "";
+ // this is basically the number of times the root query
+ // has executed which gives us an indication of how
+ // much profiling information we have gathered.
+ counter++;
}
+
+ ProfileOriginQuery stats = queryStatsMap.get(key);
+ if (stats == null) {
+ // a race condition but we don't care
+ stats = new ProfileOriginQuery(key);
+ queryStatsMap.put(key, stats);
+ }
+ stats.add(beansLoaded, micros);
}
@@ -126,49 +125,49 @@ public class Statistics implements Serializable {
if (!profile.isEmpty()) {
ObjectGraphNode node = profile.getNode();
- StatisticsNodeUsage nodeStats = getNodeStats(node.getPath());
+ ProfileOriginNodeUsage nodeStats = getNodeStats(node.getPath());
nodeStats.publish(profile);
}
}
- private StatisticsNodeUsage getNodeStats(String path) {
+ private ProfileOriginNodeUsage getNodeStats(String path) {
synchronized (monitor) {
- StatisticsNodeUsage nodeStats = nodeUsageMap.get(path);
+ ProfileOriginNodeUsage nodeStats = nodeUsageMap.get(path);
if (nodeStats == null) {
- nodeStats = new StatisticsNodeUsage(path, queryTuningAddVersion);
+ nodeStats = new ProfileOriginNodeUsage(path, queryTuningAddVersion);
nodeUsageMap.put(path, nodeStats);
}
return nodeStats;
}
}
- public String getUsageDebug() {
- synchronized (monitor) {
- StringBuilder sb = new StringBuilder();
- sb.append("root[").append(origin.getBeanType()).append("] ");
- for (StatisticsNodeUsage node : nodeUsageMap.values()) {
- sb.append(node.toString()).append("\n");
- }
- return sb.toString();
- }
- }
-
- public String getQueryStatDebug() {
- synchronized (monitor) {
- StringBuilder sb = new StringBuilder();
- for (StatisticsQuery queryStat : queryStatsMap.values()) {
- sb.append(queryStat.toString()).append("\n");
- }
- return sb.toString();
- }
- }
-
- public String toString() {
-
- synchronized (monitor) {
- return getUsageDebug();
- }
- }
+// public String getUsageDebug() {
+// synchronized (monitor) {
+// StringBuilder sb = new StringBuilder();
+// sb.append("root[").append(origin.getBeanType()).append("] ");
+// for (ProfileOriginNodeUsage node : nodeUsageMap.values()) {
+// sb.append(node.toString()).append("\n");
+// }
+// return sb.toString();
+// }
+// }
+//
+// public String getQueryStatDebug() {
+// synchronized (monitor) {
+// StringBuilder sb = new StringBuilder();
+// for (ProfileOriginQuery queryStat : queryStatsMap.values()) {
+// sb.append(queryStat.toString()).append("\n");
+// }
+// return sb.toString();
+// }
+// }
+//
+// public String toString() {
+//
+// synchronized (monitor) {
+// return getUsageDebug();
+// }
+// }
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/StatisticsNodeUsage.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginNodeUsage.java
similarity index 91%
rename from src/main/java/com/avaje/ebeaninternal/server/autofetch/StatisticsNodeUsage.java
rename to src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginNodeUsage.java
index 868f8f356..a051bc31f 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/StatisticsNodeUsage.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginNodeUsage.java
@@ -1,4 +1,4 @@
-package com.avaje.ebeaninternal.server.autofetch;
+package com.avaje.ebeaninternal.server.autofetch.service;
import com.avaje.ebean.bean.NodeUsageCollector;
import com.avaje.ebean.text.PathProperties;
@@ -18,11 +18,11 @@ import java.util.Set;
/**
* Collects usages statistics for a given node in the object graph.
*/
-public class StatisticsNodeUsage implements Serializable {
+public class ProfileOriginNodeUsage implements Serializable {
private static final long serialVersionUID = -1663951463963779547L;
- private static final Logger logger = LoggerFactory.getLogger(StatisticsNodeUsage.class);
+ private static final Logger logger = LoggerFactory.getLogger(ProfileOriginNodeUsage.class);
@SuppressWarnings("RedundantStringConstructorCall")
private final String monitor = new String();
@@ -39,7 +39,7 @@ public class StatisticsNodeUsage implements Serializable {
private final Set aggregateUsed = new LinkedHashSet();
- public StatisticsNodeUsage(String path, boolean queryTuningAddVersion) {
+ public ProfileOriginNodeUsage(String path, boolean queryTuningAddVersion) {
this.path = path;
this.queryTuningAddVersion = queryTuningAddVersion;
}
@@ -99,7 +99,7 @@ public class StatisticsNodeUsage implements Serializable {
synchronized (monitor) {
- HashSet used = profile.getUsed();
+ Set used = profile.getUsed();
profileCount++;
if (!used.isEmpty()) {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/StatisticsQuery.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginQuery.java
similarity index 81%
rename from src/main/java/com/avaje/ebeaninternal/server/autofetch/StatisticsQuery.java
rename to src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginQuery.java
index 506087b31..b4d2b6bfa 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/StatisticsQuery.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginQuery.java
@@ -1,11 +1,11 @@
-package com.avaje.ebeaninternal.server.autofetch;
+package com.avaje.ebeaninternal.server.autofetch.service;
import java.io.Serializable;
/**
* Used to accumulate query execution statistics.
*/
-public class StatisticsQuery implements Serializable {
+public class ProfileOriginQuery implements Serializable {
private static final long serialVersionUID = -1133958958072778811L;
@@ -17,7 +17,7 @@ public class StatisticsQuery implements Serializable {
private long totalMicros;
- public StatisticsQuery(String path) {
+ public ProfileOriginQuery(String path) {
this.path = path;
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfo.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/TunedQueryInfo.java
similarity index 98%
rename from src/main/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfo.java
rename to src/main/java/com/avaje/ebeaninternal/server/autofetch/service/TunedQueryInfo.java
index dec85ebee..8a6a5d046 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfo.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/TunedQueryInfo.java
@@ -1,4 +1,4 @@
-package com.avaje.ebeaninternal.server.autofetch;
+package com.avaje.ebeaninternal.server.autofetch.service;
import com.avaje.ebean.bean.ObjectGraphOrigin;
import com.avaje.ebeaninternal.api.SpiQuery;
diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java
index 2f0a2db78..e09e3873e 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java
@@ -50,7 +50,7 @@ public class DefaultContainer implements SpiContainer {
private final JndiDataSourceLookup jndiDataSourceFactory;
- private final AtomicInteger serverId = new AtomicInteger(1);
+// private final AtomicInteger serverId = new AtomicInteger(1);
public DefaultContainer(ContainerConfig containerConfig) {
@@ -119,7 +119,7 @@ public class DefaultContainer implements SpiContainer {
ServerCacheManager cacheManager = getCacheManager(serverConfig);
- int uniqueServerId = serverId.incrementAndGet();
+// int uniqueServerId = serverId.incrementAndGet();
SpiBackgroundExecutor bgExecutor = createBackgroundExecutor(serverConfig);
XmlConfigLoader xmlConfigLoader = new XmlConfigLoader(null);
@@ -131,18 +131,18 @@ public class DefaultContainer implements SpiContainer {
cacheManager.init(server);
- if (serverConfig.isRegisterJmxMBeans()) {
- MBeanServer mbeanServer;
- ArrayList> list = MBeanServerFactory.findMBeanServer(null);
- if (list.size() == 0) {
- // probably not running in a server
- mbeanServer = MBeanServerFactory.createMBeanServer();
- } else {
- // use the first MBeanServer
- mbeanServer = (MBeanServer) list.get(0);
- }
- server.registerMBeans(mbeanServer, uniqueServerId);
- }
+// if (serverConfig.isRegisterJmxMBeans()) {
+// MBeanServer mbeanServer;
+// ArrayList> list = MBeanServerFactory.findMBeanServer(null);
+// if (list.size() == 0) {
+// // probably not running in a server
+// mbeanServer = MBeanServerFactory.createMBeanServer();
+// } else {
+// // use the first MBeanServer
+// mbeanServer = (MBeanServer) list.get(0);
+// }
+// server.registerMBeans(mbeanServer, uniqueServerId);
+// }
// generate and run DDL if required
// if there are any other tasks requiring action in their plugins, do them as well
diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java
index a716516c5..4b14fc42b 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java
@@ -33,7 +33,7 @@ import com.avaje.ebeaninternal.api.SpiQuery.Type;
import com.avaje.ebeaninternal.api.SpiSqlQuery;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.api.TransactionEventTable;
-import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
+import com.avaje.ebeaninternal.server.autofetch.AutoTuneService;
import com.avaje.ebean.dbmigration.DdlGenerator;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
@@ -43,7 +43,6 @@ import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery;
import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.el.ElFilter;
-import com.avaje.ebeaninternal.server.jmx.MAdminAutofetch;
import com.avaje.ebeaninternal.server.lib.ShutdownManager;
import com.avaje.ebeaninternal.server.query.CQuery;
import com.avaje.ebeaninternal.server.query.CQueryEngine;
@@ -69,9 +68,6 @@ import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.management.InstanceAlreadyExistsException;
-import javax.management.MBeanServer;
-import javax.management.ObjectName;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import java.util.ArrayList;
@@ -103,8 +99,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private final DatabasePlatform databasePlatform;
- private final AdminAutofetch adminAutofetch;
-
private final TransactionManager transactionManager;
private final TransactionScopeManager transactionScopeManager;
@@ -132,7 +126,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private final DiffHelp diffHelp;
- private final AutoFetchManager autoFetchManager;
+ private final AutoTuneService autoTuneService;
private final CQueryEngine cqueryEngine;
@@ -152,21 +146,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private final MetaInfoManager metaInfoManager;
- /**
- * The MBean name used to register Ebean.
- */
- private String mbeanName;
-
/**
* The default PersistenceContextScope used if it is not explicitly set on a query.
*/
private final PersistenceContextScope defaultPersistenceContextScope;
- /**
- * The MBeanServer Ebean is registered with.
- */
- private MBeanServer mbeanServer;
-
/**
* Flag set when the server has shutdown.
*/
@@ -235,8 +219,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
this.queryEngine = config.createOrmQueryEngine();
this.relationalQueryEngine = config.createRelationalQueryEngine();
- this.autoFetchManager = config.createAutoFetchManager(this);
- this.adminAutofetch = new MAdminAutofetch(autoFetchManager);
+ this.autoTuneService = config.createAutoFetchManager(this);
this.beanLoader = new DefaultBeanLoader(this);
this.jsonContext = config.createJsonContext(this);
@@ -346,11 +329,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
public AdminAutofetch getAdminAutofetch() {
- return adminAutofetch;
- }
-
- public AutoFetchManager getAutoFetchManager() {
- return autoFetchManager;
+ return autoTuneService;
}
/**
@@ -373,41 +352,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
public void start() {
}
- public void registerMBeans(MBeanServer mbeanServer, int uniqueServerId) {
-
- this.mbeanServer = mbeanServer;
- this.mbeanName = "Ebean:server=" + serverName + uniqueServerId;
-
- ObjectName autofetchName;
- try {
- autofetchName = new ObjectName(mbeanName + ",key=AutoFetch");
- } catch (Exception e) {
- String msg = "Failed to register the JMX beans for Ebean server [" + serverName + "].";
- logger.error(msg, e);
- return;
- }
-
- try {
- mbeanServer.registerMBean(adminAutofetch, autofetchName);
-
- } catch (InstanceAlreadyExistsException e) {
- // tomcat webapp reloading
- String msg = "JMX beans for Ebean server [" + serverName + "] already registered. Will try unregister/register" + e.getMessage();
- logger.warn(msg);
- try {
- mbeanServer.unregisterMBean(autofetchName);
- mbeanServer.registerMBean(adminAutofetch, autofetchName);
-
- } catch (Exception ae) {
- String amsg = "Unable to unregister/register the JMX beans for Ebean server [" + serverName + "].";
- logger.error(amsg, ae);
- }
- } catch (Exception e) {
- String msg = "Error registering MBean[" + mbeanName + "]";
- logger.error(msg, e);
- }
- }
-
/**
* Shutting down via JVM Shutdown hook.
*/
@@ -439,16 +383,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return;
}
shutdownPlugins();
- try {
- if (mbeanServer != null) {
- mbeanServer.unregisterMBean(new ObjectName(mbeanName + ",key=AutoFetch"));
- }
- } catch (Exception e) {
- logger.error("Error unregistering Ebean " + mbeanName, e);
- }
// shutdown autofetch profile collection
- autoFetchManager.shutdown();
+ autoTuneService.shutdown();
// shutdown background threads
backgroundExecutor.shutdown();
// shutdown DataSource (if its an Ebean one)
@@ -1100,7 +1037,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
public SpiOrmQueryRequest createQueryRequest(BeanDescriptor desc, SpiQuery query, Transaction t) {
- if (desc.isAutoFetchTunable() && !query.isSqlSelect() && !autoFetchManager.tuneQuery(query)) {
+ if (desc.isAutoFetchTunable() && !query.isSqlSelect() && !autoTuneService.tuneQuery(query)) {
// use deployment FetchType.LAZY/EAGER annotations
// to define the 'default' select clause
query.setDefaultSelectClause();
diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java
index f0b0e0b30..c1148d0cf 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java
@@ -13,8 +13,8 @@ import com.avaje.ebean.plugin.SpiServerPlugin;
import com.avaje.ebean.text.json.JsonContext;
import com.avaje.ebeaninternal.api.SpiBackgroundExecutor;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
-import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
-import com.avaje.ebeaninternal.server.autofetch.AutoFetchManagerFactory;
+import com.avaje.ebeaninternal.server.autofetch.AutoTuneService;
+import com.avaje.ebeaninternal.server.autofetch.service.AutoTuneServiceFactory;
import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogPrepare;
import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogRegister;
import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogListener;
@@ -235,8 +235,8 @@ public class InternalConfiguration {
return xmlConfig;
}
- public AutoFetchManager createAutoFetchManager(SpiEbeanServer server) {
- return AutoFetchManagerFactory.create(server, serverConfig, resourceManager);
+ public AutoTuneService createAutoFetchManager(SpiEbeanServer server) {
+ return AutoTuneServiceFactory.create(server, serverConfig, resourceManager);
}
public RelationalQueryEngine createRelationalQueryEngine() {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminAutofetch.java b/src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminAutofetch.java
deleted file mode 100644
index a31100d6d..000000000
--- a/src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminAutofetch.java
+++ /dev/null
@@ -1,121 +0,0 @@
-package com.avaje.ebeaninternal.server.jmx;
-
-import com.avaje.ebean.AdminAutofetch;
-import com.avaje.ebean.EbeanServer;
-import com.avaje.ebean.config.AutofetchMode;
-import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Implementation of the AutoFetchControl.
- *
- * This is accessible via {@link EbeanServer#getAdminAutofetch()} or via JMX
- * MBeans.
- *
- */
-public class MAdminAutofetch implements MAdminAutofetchMBean, AdminAutofetch {
-
- private static final Logger logger = LoggerFactory.getLogger(MAdminAutofetch.class);
-
- final AutoFetchManager autoFetchManager;
-
- final String modeOptions;
-
- public MAdminAutofetch(AutoFetchManager autoFetchListener) {
- this.autoFetchManager = autoFetchListener;
- this.modeOptions = AutofetchMode.DEFAULT_OFF + ", "
- + AutofetchMode.DEFAULT_ON + ", "
- + AutofetchMode.DEFAULT_ONIFEMPTY;
- }
-
- public boolean isQueryTuning() {
- return autoFetchManager.isQueryTuning();
- }
-
- public void setQueryTuning(boolean enable) {
- autoFetchManager.setQueryTuning(enable);
- }
-
- public boolean isProfiling() {
- return autoFetchManager.isProfiling();
- }
-
- public void setProfiling(boolean enable) {
- autoFetchManager.setProfiling(enable);
- }
-
- public String getModeOptions() {
- return modeOptions;
- }
-
- public String getMode() {
- return autoFetchManager.getMode().name();
- }
-
- public void setMode(String implicitMode) {
- try {
- AutofetchMode mode = AutofetchMode.valueOf(implicitMode);
- autoFetchManager.setMode(mode);
- } catch (Exception e) {
- logger.info("Invalid implicit mode attempted "+e.getMessage());
- }
- }
-
- public String collectUsageViaGC() {
- return autoFetchManager.collectUsageViaGC(-1);
- }
-
- public double getProfilingRate() {
- return autoFetchManager.getProfilingRate();
- }
-
- public void setProfilingRate(double rate) {
- autoFetchManager.setProfilingRate(rate);
- }
-
- public int getProfilingMin() {
- return autoFetchManager.getProfilingMin();
- }
-
- public int getProfilingBase() {
- return autoFetchManager.getProfilingBase();
- }
-
- public void setProfilingMin(int profilingMin) {
- autoFetchManager.setProfilingMin(profilingMin);
- }
-
- public void setProfilingBase(int profilingMax) {
- autoFetchManager.setProfilingBase(profilingMax);
- }
-
- public String updateTunedQueryInfo() {
- return autoFetchManager.updateTunedQueryInfo();
- }
-
- public int clearProfilingInfo() {
- return autoFetchManager.clearProfilingInfo();
- }
-
- public int clearTunedQueryInfo() {
- return autoFetchManager.clearTunedQueryInfo();
- }
-
- public void clearQueryStatistics() {
- autoFetchManager.clearQueryStatistics();
- }
-
- public int getTotalProfileSize() {
- return autoFetchManager.getTotalProfileSize();
- }
-
- public int getTotalTunedQueryCount() {
- return autoFetchManager.getTotalTunedQueryCount();
- }
-
- public int getTotalTunedQuerySize() {
- return autoFetchManager.getTotalTunedQuerySize();
- }
-
-}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminAutofetchMBean.java b/src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminAutofetchMBean.java
deleted file mode 100644
index ee6e10e78..000000000
--- a/src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminAutofetchMBean.java
+++ /dev/null
@@ -1,145 +0,0 @@
-package com.avaje.ebeaninternal.server.jmx;
-
-import com.avaje.ebean.Query;
-
-public interface MAdminAutofetchMBean {
-
- /**
- * Return true if profiling is enabled.
- */
- boolean isProfiling();
-
- /**
- * Set to true to enable profiling.
- */
- void setProfiling(boolean enable);
-
- /**
- * Return true if autoFetch is enabled.
- */
- boolean isQueryTuning();
-
- /**
- * Set to true to enable autoFetch.
- */
- 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)}.
- */
- String getMode();
-
- /**
- * List of the valid implicit modes that can be used.
- */
- String getModeOptions();
-
- /**
- * Set the auto fetch mode used when a query has not had {@link Query#setAutofetch(boolean)}.
- */
- void setMode(String mode);
-
- /**
- * 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 profilingMaxThreshold);
-
- /**
- * Returns the rate which profiling is collected.
- * This is an int between 0 and 100.
- */
- double getProfilingRate();
-
- /**
- * Set the rate at which profiling is collected after the base.
- *
- * @param rate a int between 0 and 100.
- */
- void setProfilingRate(double rate);
-
- /**
- * 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.
- *
- */
- 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();
-
- /**
- * 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/jmx/package-info.java b/src/main/java/com/avaje/ebeaninternal/server/jmx/package-info.java
deleted file mode 100644
index edaa2ad21..000000000
--- a/src/main/java/com/avaje/ebeaninternal/server/jmx/package-info.java
+++ /dev/null
@@ -1 +0,0 @@
-package com.avaje.ebeaninternal.server.jmx;
\ No newline at end of file
diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java
index b1f7adada..d7eb2d86b 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java
@@ -58,7 +58,7 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
if (queryProps != null) {
queryProps.configureBeanQuery(query);
}
- if (parent.isUseAutofetchManager()) {
+ if (parent.isUseAutoTune()) {
query.setAutofetch(true);
}
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java
index f9f1c1870..9a6b3fb3d 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java
@@ -47,7 +47,7 @@ public class DLoadContext implements LoadContext {
*/
private final String relativePath;
private final ObjectGraphOrigin origin;
- private final boolean useAutofetchManager;
+ private final boolean useProfiling;
private final Map nodePathMap = new HashMap();
@@ -67,7 +67,7 @@ public class DLoadContext implements LoadContext {
this.readOnly = query.isReadOnly();
this.disableLazyLoading = query.isDisableLazyLoading();
this.excludeBeanCache = Boolean.FALSE.equals(query.isUseBeanCache());
- this.useAutofetchManager = query.getAutoFetchManager() != null;
+ this.useProfiling = query.getProfilingListener() != null;
ObjectGraphNode parentNode = query.getParentNode();
if (parentNode != null) {
@@ -197,8 +197,8 @@ public class DLoadContext implements LoadContext {
return new ObjectGraphNode(origin, path);
}
- public boolean isUseAutofetchManager() {
- return useAutofetchManager;
+ public boolean isUseAutoTune() {
+ return useProfiling;
}
protected String getFullPath(String path) {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java
index 2e726ee94..fc3927b31 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java
@@ -70,7 +70,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
queryProps.configureBeanQuery(query);
}
- if (parent.isUseAutofetchManager()) {
+ if (parent.isUseAutoTune()) {
query.setAutofetch(true);
}
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java
index 1f0518c1c..923d4ebed 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java
@@ -6,7 +6,7 @@ import com.avaje.ebean.bean.*;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
import com.avaje.ebeaninternal.api.SpiTransaction;
-import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
+import com.avaje.ebeaninternal.server.autofetch.ProfilingListener;
import com.avaje.ebeaninternal.server.core.Message;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest;
@@ -149,13 +149,13 @@ public class CQuery implements DbReadContext, CancelableQuery {
private final Mode queryMode;
- private final boolean autoFetchProfiling;
+ private final boolean autoTuneProfiling;
private final ObjectGraphNode objectGraphNode;
- private final AutoFetchManager autoFetchManager;
+ private final ProfilingListener profilingListener;
- private final WeakReference autoFetchManagerRef;
+ private final WeakReference profilingListenerRef;
private final Boolean readOnly;
@@ -176,11 +176,10 @@ public class CQuery implements DbReadContext, CancelableQuery {
this.readOnly = request.isReadOnly();
- this.autoFetchManager = query.getAutoFetchManager();
- this.autoFetchProfiling = autoFetchManager != null;
this.objectGraphNode = query.getParentNode();
- this.autoFetchManagerRef = autoFetchProfiling ? new WeakReference(
- autoFetchManager) : null;
+ this.profilingListener = query.getProfilingListener();
+ this.autoTuneProfiling = profilingListener != null;
+ this.profilingListenerRef = autoTuneProfiling ? new WeakReference(profilingListener) : null;
// set the generated sql back to the query
// so its available to the user...
@@ -518,8 +517,8 @@ public class CQuery implements DbReadContext, CancelableQuery {
long exeNano = System.nanoTime() - startNano;
executionTimeMicros = TimeUnit.NANOSECONDS.toMicros(exeNano);
- if (autoFetchProfiling) {
- autoFetchManager.collectQueryInfo(objectGraphNode, loadedBeanCount, executionTimeMicros);
+ if (autoTuneProfiling) {
+ profilingListener.collectQueryInfo(objectGraphNode, loadedBeanCount, executionTimeMicros);
}
queryPlan.executionTime(loadedBeanCount, executionTimeMicros, objectGraphNode);
@@ -649,7 +648,7 @@ public class CQuery implements DbReadContext, CancelableQuery {
// need query.isProfiling() because we just take the data
// from the lazy loaded or refreshed beans and put it into the already
// existing beans which are already collecting usage information
- return autoFetchProfiling && query.isUsageProfiling();
+ return autoTuneProfiling && query.isUsageProfiling();
}
private String getPath(String propertyName) {
@@ -671,8 +670,7 @@ public class CQuery implements DbReadContext, CancelableQuery {
public void profileBean(EntityBeanIntercept ebi, String prefix) {
ObjectGraphNode node = request.getGraphContext().getObjectGraphNode(prefix);
-
- ebi.setNodeUsageCollector(new NodeUsageCollector(node, autoFetchManagerRef));
+ ebi.setNodeUsageCollector(new NodeUsageCollector(node, profilingListenerRef));
}
public void setCurrentPrefix(String currentPrefix, Map currentPathMap) {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
index db2dfb342..b8543b0e1 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
@@ -1,14 +1,5 @@
package com.avaje.ebeaninternal.server.querydefn;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import javax.persistence.PersistenceException;
-
import com.avaje.ebean.*;
import com.avaje.ebean.OrderBy.Property;
import com.avaje.ebean.bean.BeanCollectionTouched;
@@ -26,7 +17,7 @@ import com.avaje.ebeaninternal.api.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionList;
import com.avaje.ebeaninternal.api.SpiQuery;
-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.DRawSqlSelect;
@@ -36,6 +27,14 @@ import com.avaje.ebeaninternal.server.expression.SimpleExpression;
import com.avaje.ebeaninternal.server.query.CancelableQuery;
import com.avaje.ebeaninternal.util.DefaultExpressionList;
+import javax.persistence.PersistenceException;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
/**
* Default implementation of an Object Relational query.
*/
@@ -57,7 +56,7 @@ public class DefaultOrmQuery implements SpiQuery {
*/
private transient TableJoin includeTableJoin;
- private transient AutoFetchManager autoFetchManager;
+ private transient ProfilingListener profilingListener;
private transient BeanDescriptor> beanDescriptor;
@@ -481,7 +480,7 @@ public class DefaultOrmQuery implements SpiQuery {
DefaultOrmQuery copy = new DefaultOrmQuery(beanType, server, expressionFactory, (String) null);
copy.name = name;
copy.includeTableJoin = includeTableJoin;
- copy.autoFetchManager = autoFetchManager;
+ copy.profilingListener = profilingListener;
copy.query = query;
copy.additionalWhere = additionalWhere;
@@ -616,19 +615,23 @@ public class DefaultOrmQuery implements SpiQuery {
return this;
}
+ @Override
public DefaultOrmQuery setForUpdate(boolean forUpdate) {
this.forUpdate = forUpdate;
return this;
}
- public AutoFetchManager getAutoFetchManager() {
- return autoFetchManager;
+ @Override
+ public ProfilingListener getProfilingListener() {
+ return profilingListener;
}
- public void setAutoFetchManager(AutoFetchManager autoFetchManager) {
- this.autoFetchManager = autoFetchManager;
+ @Override
+ public void setProfilingListener(ProfilingListener profilingListener) {
+ this.profilingListener = profilingListener;
}
+ @Override
public Mode getMode() {
return mode;
}
diff --git a/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java b/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java
index 9304cf8ec..adbcc84d8 100644
--- a/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java
+++ b/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java
@@ -13,7 +13,6 @@ import com.avaje.ebean.meta.MetaInfoManager;
import com.avaje.ebean.plugin.SpiServer;
import com.avaje.ebean.text.csv.CsvReader;
import com.avaje.ebean.text.json.JsonContext;
-import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.query.CQuery;
@@ -86,11 +85,6 @@ public class TDSpiEbeanServer implements SpiEbeanServer {
return null;
}
- @Override
- public AutoFetchManager getAutoFetchManager() {
- return null;
- }
-
@Override
public void clearQueryStatistics() {
diff --git a/src/test/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfoTest.java b/src/test/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfoTest.java
index b66df26fb..eaf1ec8be 100644
--- a/src/test/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfoTest.java
+++ b/src/test/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfoTest.java
@@ -15,7 +15,7 @@ import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebeaninternal.api.SpiQuery;
-import com.avaje.ebeaninternal.server.autofetch.TunedQueryInfo;
+import com.avaje.ebeaninternal.server.autofetch.service.TunedQueryInfo;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
import com.avaje.tests.model.basic.Order;
import com.avaje.tests.model.basic.ResetBasicData;
diff --git a/src/test/java/com/avaje/tests/autofetch/AutofetchEmbeddedTest.java b/src/test/java/com/avaje/tests/autofetch/AutofetchEmbeddedTest.java
index 2a201f256..ba59ae6cb 100644
--- a/src/test/java/com/avaje/tests/autofetch/AutofetchEmbeddedTest.java
+++ b/src/test/java/com/avaje/tests/autofetch/AutofetchEmbeddedTest.java
@@ -37,30 +37,30 @@ public class AutofetchEmbeddedTest extends BaseTestCase {
@Test
public void testEmbeddedBeanQueryTuning() {
- Ebean.getServer(null).getAdminAutofetch().setProfiling(true);
- Ebean.getServer(null).getAdminAutofetch().setQueryTuning(true);
- Ebean.getServer(null).getAdminAutofetch().setProfilingBase(1);
-
- EMain testBean = new EMain();
- testBean.setName("test");
- testBean.getEmbeddable().setDescription("test description");
- Ebean.save(testBean);
-
- //This should not throw an exception
- for (int i = 0; i < 5; i++) {
- Ebean.beginTransaction();
- try {
- List result = Ebean.find(EMain.class).setAutofetch(true).findList();
- for (EMain e : result) {
- e.getEmbeddable().setDescription("Test" + i);
- Ebean.save(e);
- }
- Ebean.commitTransaction();
- } finally {
- Ebean.endTransaction();
- logger.debug(Ebean.getServer(null).getAdminAutofetch().collectUsageViaGC());
- }
- }
+// Ebean.getServer(null).getAdminAutofetch().setProfiling(true);
+// Ebean.getServer(null).getAdminAutofetch().setQueryTuning(true);
+// Ebean.getServer(null).getAdminAutofetch().setProfilingBase(1);
+//
+// EMain testBean = new EMain();
+// testBean.setName("test");
+// testBean.getEmbeddable().setDescription("test description");
+// Ebean.save(testBean);
+//
+// //This should not throw an exception
+// for (int i = 0; i < 5; i++) {
+// Ebean.beginTransaction();
+// try {
+// List result = Ebean.find(EMain.class).setAutofetch(true).findList();
+// for (EMain e : result) {
+// e.getEmbeddable().setDescription("Test" + i);
+// Ebean.save(e);
+// }
+// Ebean.commitTransaction();
+// } finally {
+// Ebean.endTransaction();
+// logger.debug(Ebean.getServer(null).getAdminAutofetch().collectUsageViaGC());
+// }
+// }
}
@Test
diff --git a/src/test/java/com/avaje/tests/cache/TestL2CacheWithSharedBean.java b/src/test/java/com/avaje/tests/cache/TestL2CacheWithSharedBean.java
index 381da8236..a31184917 100644
--- a/src/test/java/com/avaje/tests/cache/TestL2CacheWithSharedBean.java
+++ b/src/test/java/com/avaje/tests/cache/TestL2CacheWithSharedBean.java
@@ -8,7 +8,7 @@ import com.avaje.ebean.Ebean;
import com.avaje.ebean.Query;
import com.avaje.ebean.cache.ServerCache;
import com.avaje.ebeaninternal.api.SpiQuery;
-import com.avaje.ebeaninternal.server.autofetch.TunedQueryInfo;
+import com.avaje.ebeaninternal.server.autofetch.service.TunedQueryInfo;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
import com.avaje.tests.model.basic.FeatureDescription;
diff --git a/src/test/java/com/avaje/tests/query/TestAutofetchTuneWithJoin.java b/src/test/java/com/avaje/tests/query/TestAutofetchTuneWithJoin.java
index 0693038fe..f9fcfe399 100644
--- a/src/test/java/com/avaje/tests/query/TestAutofetchTuneWithJoin.java
+++ b/src/test/java/com/avaje/tests/query/TestAutofetchTuneWithJoin.java
@@ -28,8 +28,11 @@ public class TestAutofetchTuneWithJoin extends BaseTestCase {
ResetBasicData.reset();
- Query q = Ebean.find(Order.class).setAutofetch(true).fetch("customer")
- .fetch("customer.contacts").where().lt("id", 3).query();
+ Query q = Ebean.find(Order.class)
+ .setAutofetch(true)
+ .fetch("customer")
+ .fetch("customer.contacts")
+ .where().lt("id", 3).query();
List list = q.findList();
diff --git a/src/test/resources/ebean.properties b/src/test/resources/ebean.properties
index 86562c599..10323b913 100644
--- a/src/test/resources/ebean.properties
+++ b/src/test/resources/ebean.properties
@@ -11,14 +11,15 @@
ebean.encryptKeyManager=com.avaje.tests.basic.encrypt.BasicEncyptKeyManager
-ebean.autofetch.querytuning=false
-ebean.autofetch.profiling=false
+ebean.autofetch.querytuning=true
+ebean.autofetch.profiling=true
ebean.autofetch.implicitmode=default_off
#ebean.autofetch.implicitmode=default_onifempty
ebean.autofetch.profiling.min=1
ebean.autofetch.profiling.base=10
#ebean.autofetch.profiling.rate=0.05
-ebean.autofetch.traceUsageCollection=false
+ebean.autofetch.garbageCollectionOnShutdown=true
+ebean.autofetch.traceUsageCollection=true
ebean.ddl.generate=true
diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml
index b52efc14d..39dcc78bd 100644
--- a/src/test/resources/logback-test.xml
+++ b/src/test/resources/logback-test.xml
@@ -42,6 +42,8 @@
+
+