diff --git a/src/main/java/com/avaje/ebean/bean/CallStack.java b/src/main/java/com/avaje/ebean/bean/CallStack.java index 229809e97..3d0f61f86 100644 --- a/src/main/java/com/avaje/ebean/bean/CallStack.java +++ b/src/main/java/com/avaje/ebean/bean/CallStack.java @@ -26,16 +26,12 @@ public final class CallStack implements Serializable { private final StackTraceElement[] callStack; - public CallStack(StackTraceElement[] callStack) { + public CallStack(StackTraceElement[] callStack, int zeroHash, int pathHash) { this.callStack = callStack; - this.zeroHash = enc(callStack[0].hashCode()); - int hc = 0; - for (int i = 1; i < callStack.length; i++) { - hc = 31 * hc + callStack[i].hashCode(); - } - this.pathHash = enc(hc); + this.zeroHash = enc(zeroHash); + this.pathHash = enc(pathHash); } - + public int hashCode() { int hc = 0; for (int i = 0; i < callStack.length; i++) { @@ -99,7 +95,7 @@ public final class CallStack implements Serializable { } public String getOriginKey(int queryHash) { - return zeroHash + "." + enc(queryHash) + "." + pathHash; + return enc(queryHash)+ "." + zeroHash + "." + pathHash; } private static final int radix = 1 << 6; diff --git a/src/main/java/com/avaje/ebeaninternal/server/autotune/model/Autotune.java b/src/main/java/com/avaje/ebeaninternal/server/autotune/model/Autotune.java index 80f648fcd..68bd3c7fd 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autotune/model/Autotune.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autotune/model/Autotune.java @@ -22,6 +22,7 @@ import javax.xml.bind.annotation.XmlType; * <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"/> + * <element ref="{http://ebean-orm.github.io/xml/ns/autotune}profileEmpty" minOccurs="0"/> * </sequence> * </restriction> * </complexContent> @@ -34,7 +35,8 @@ import javax.xml.bind.annotation.XmlType; @XmlType(name = "", propOrder = { "origin", "profileDiff", - "profileNew" + "profileNew", + "profileEmpty" }) @XmlRootElement(name = "autotune") public class Autotune { @@ -42,6 +44,7 @@ public class Autotune { protected List origin; protected ProfileDiff profileDiff; protected ProfileNew profileNew; + protected ProfileEmpty profileEmpty; /** * Gets the value of the origin property. @@ -120,4 +123,28 @@ public class Autotune { this.profileNew = value; } + /** + * Gets the value of the profileEmpty property. + * + * @return + * possible object is + * {@link ProfileEmpty } + * + */ + public ProfileEmpty getProfileEmpty() { + return profileEmpty; + } + + /** + * Sets the value of the profileEmpty property. + * + * @param value + * allowed object is + * {@link ProfileEmpty } + * + */ + public void setProfileEmpty(ProfileEmpty value) { + this.profileEmpty = value; + } + } diff --git a/src/main/java/com/avaje/ebeaninternal/server/autotune/model/ObjectFactory.java b/src/main/java/com/avaje/ebeaninternal/server/autotune/model/ObjectFactory.java index 0c72b0a81..effa06fff 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autotune/model/ObjectFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autotune/model/ObjectFactory.java @@ -7,7 +7,7 @@ import javax.xml.bind.annotation.XmlRegistry; /** * This object contains factory methods for each * Java content interface and Java element interface - * generated in the com.avaje.ebeaninternal.server.autotune.model package. + * generated in the com.avaje.ebeaninternal.server.autotune.model package. *

An ObjectFactory allows you to programatically * construct new instances of the Java representation * for XML content. The Java representation of XML @@ -45,6 +45,14 @@ public class ObjectFactory { return new Origin(); } + /** + * Create an instance of {@link ProfileEmpty } + * + */ + public ProfileEmpty createProfileEmpty() { + return new ProfileEmpty(); + } + /** * Create an instance of {@link Autotune } * diff --git a/src/main/java/com/avaje/ebeaninternal/server/autotune/model/ProfileEmpty.java b/src/main/java/com/avaje/ebeaninternal/server/autotune/model/ProfileEmpty.java new file mode 100644 index 000000000..0b0dc7eb1 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/autotune/model/ProfileEmpty.java @@ -0,0 +1,69 @@ + +package com.avaje.ebeaninternal.server.autotune.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; + + +/** + *

Java 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"/>
+ *       </sequence>
+ *     </restriction>
+ *   </complexContent>
+ * </complexType>
+ * 
+ * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "origin" +}) +@XmlRootElement(name = "profileEmpty") +public class ProfileEmpty { + + protected List origin; + + /** + * Gets the value of the origin property. + * + *

+ * 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 getOrigin() { + if (origin == null) { + origin = new ArrayList(); + } + return this.origin; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autotune/service/BaseQueryTuner.java b/src/main/java/com/avaje/ebeaninternal/server/autotune/service/BaseQueryTuner.java index 340058a92..0097fa5fa 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autotune/service/BaseQueryTuner.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autotune/service/BaseQueryTuner.java @@ -11,6 +11,7 @@ import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; import javax.persistence.PersistenceException; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** @@ -167,4 +168,11 @@ public class BaseQueryTuner { } } } + + /** + * Return the keys as a set. + */ + public Set keySet() { + return tunedQueryInfoMap.keySet(); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/autotune/service/DefaultAutoTuneService.java b/src/main/java/com/avaje/ebeaninternal/server/autotune/service/DefaultAutoTuneService.java index b93fbe44a..3fca222f2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autotune/service/DefaultAutoTuneService.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autotune/service/DefaultAutoTuneService.java @@ -10,6 +10,7 @@ import com.avaje.ebeaninternal.server.autotune.AutoTuneService; import com.avaje.ebeaninternal.server.autotune.model.Autotune; import com.avaje.ebeaninternal.server.autotune.model.Origin; import com.avaje.ebeaninternal.server.autotune.model.ProfileDiff; +import com.avaje.ebeaninternal.server.autotune.model.ProfileEmpty; import com.avaje.ebeaninternal.server.autotune.model.ProfileNew; import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetailParser; @@ -22,7 +23,9 @@ import java.text.SimpleDateFormat; import java.util.Collections; import java.util.Comparator; import java.util.Date; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; /** @@ -105,14 +108,31 @@ public class DefaultAutoTuneService implements AutoTuneService { AtomicInteger newCounter = new AtomicInteger(); AtomicInteger diffCounter = new AtomicInteger(); + Set profileKeys = new HashSet(); for (AutoTuneCollection.Entry entry : entries) { saveProfilingEntry(document, entry, newCounter, diffCounter); + profileKeys.add(entry.getOrigin().getKey()); + } + + // report the origin keys that we didn't collect any profiling on + Set tunerKeys = queryTuner.keySet(); + for (String tuneKey : tunerKeys) { + if (!profileKeys.contains(tuneKey)) { + ProfileEmpty profileEmpty = document.getProfileEmpty(); + if (profileEmpty == null) { + profileEmpty = new ProfileEmpty(); + document.setProfileEmpty(profileEmpty); + } + Origin emptyOrigin = new Origin(); + emptyOrigin.setKey(tuneKey); + profileEmpty.getOrigin().add(emptyOrigin); + } } int totalNew = newCounter.get(); int totalDiff = diffCounter.get(); if (totalNew == 0 && totalDiff == 0) { - logger.debug("No new or diff entries for profiling server:{}", serverName); + logger.info("No new or diff entries for profiling server:{}", serverName); } else { sortDocument(document); @@ -125,7 +145,7 @@ public class DefaultAutoTuneService implements AutoTuneService { AutoTuneXmlWriter writer = new AutoTuneXmlWriter(); writer.write(document, file); - logger.debug("writing new:{} diff:{} profiling entries for server:{}", totalNew, totalDiff, serverName); + logger.info("writing new:{} diff:{} profiling entries for server:{}", totalNew, totalDiff, serverName); } } @@ -142,6 +162,10 @@ public class DefaultAutoTuneService implements AutoTuneService { if (profileNew != null) { Collections.sort(profileNew.getOrigin(), new OriginNameKeySort()); } + ProfileEmpty profileEmpty = document.getProfileEmpty(); + if (profileEmpty != null) { + Collections.sort(profileEmpty.getOrigin(), new OriginKeySort()); + } } /** @@ -159,6 +183,17 @@ public class DefaultAutoTuneService implements AutoTuneService { } } + /** + * Comparator sort by bean type then key. + */ + class OriginKeySort implements Comparator { + + @Override + public int compare(Origin o1, Origin o2) { + return o1.getKey().compareTo(o2.getKey()); + } + } + private void saveProfilingEntry(Autotune document, AutoTuneCollection.Entry entry, AtomicInteger newCount, AtomicInteger diffCount) { ObjectGraphOrigin point = entry.getOrigin(); diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/CallStackFactory.java b/src/main/java/com/avaje/ebeaninternal/server/core/CallStackFactory.java new file mode 100644 index 000000000..f5b4b4f1a --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/core/CallStackFactory.java @@ -0,0 +1,14 @@ +package com.avaje.ebeaninternal.server.core; + +import com.avaje.ebean.bean.CallStack; + +/** + * Creates CallStack based on the stack trace. + */ +public interface CallStackFactory { + + /** + * Create and return the CallStack given the stack trace elements. + */ + CallStack createCallStack(StackTraceElement[] finalTrace); +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultCallStackFactory.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultCallStackFactory.java new file mode 100644 index 000000000..869be6a1f --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultCallStackFactory.java @@ -0,0 +1,27 @@ +package com.avaje.ebeaninternal.server.core; + +import com.avaje.ebean.bean.CallStack; + +/** + * Default CallStackFactory where the Hash function for StackTraceElement includes the line number. + */ +public class DefaultCallStackFactory implements CallStackFactory { + + @Override + public CallStack createCallStack(StackTraceElement[] finalTrace) { + return new CallStack(finalTrace, finalTrace[0].hashCode(), pathHash(finalTrace)); + } + + /** + * Return the hash code for the path excluding the first element. + */ + private int pathHash(StackTraceElement[] callStack) { + + int hc = 0; + for (int i = 1; i < callStack.length; i++) { + hc = 31 * hc + callStack[i].hashCode(); + } + return hc; + } + +} 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 548a1e0ec..c09c5a6fc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java @@ -93,8 +93,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { private static final int IGNORE_LEADING_ELEMENTS = 5; - private static final String AVAJE_EBEAN = Ebean.class.getName().substring(0, 15); - + private static final String COM_AVAJE_EBEAN = "com.avaje.ebean"; + + private static final String ORG_AVAJE_EBEAN = "org.avaje.ebean"; + private final ServerConfig serverConfig; private final String serverName; @@ -105,6 +107,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { private final TransactionScopeManager transactionScopeManager; + private final CallStackFactory callStackFactory = new DefaultCallStackFactory(); + private final int maxCallStack; /** @@ -2030,7 +2034,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { // find the first non-avaje stackElement for (; startIndex < stackTrace.length; startIndex++) { - if (!stackTrace[startIndex].getClassName().startsWith(AVAJE_EBEAN)) { + if (!stackTrace[startIndex].getClassName().startsWith(COM_AVAJE_EBEAN)) { + break; + } + } + for (; startIndex < stackTrace.length; startIndex++) { + if (!stackTrace[startIndex].getClassName().startsWith(ORG_AVAJE_EBEAN)) { break; } } @@ -2050,10 +2059,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { throw new RuntimeException("StackTraceElement size 0? stack: " + Arrays.toString(stackTrace)); } - return new CallStack(finalTrace); + return callStackFactory.createCallStack(finalTrace); } - @Override public JsonContext json() { // immutable thread safe so return shared instance diff --git a/src/main/resources/ebean-autotune-1.0.xsd b/src/main/resources/ebean-autotune-1.0.xsd index fd4fc3ad2..de6246cfa 100644 --- a/src/main/resources/ebean-autotune-1.0.xsd +++ b/src/main/resources/ebean-autotune-1.0.xsd @@ -11,6 +11,7 @@ + @@ -43,4 +44,12 @@ + + + + + + + +