jamonapi/0000755000175000017500000000000010731215566012620 5ustar twernertwernerjamonapi/md5sum.txt0000666000175000017500000000022510674447250014601 0ustar twernertwernere2def5717be33022490d8d11086e48bd *jamon-2.7.jar 02af5a408462e116805b1a0df9257331 *jamon.war 21deedc024d91556e5ba08ac8ffed54c *jamontomcat-2.7.jar jamonapi/src/0000777000175000017500000000000010674451660013416 5ustar twernertwernerjamonapi/src/java/0000777000175000017500000000000010674450732014336 5ustar twernertwernerjamonapi/src/java/com/0000777000175000017500000000000010674450736015120 5ustar twernertwernerjamonapi/src/java/com/jamonapi/0000777000175000017500000000000010674450734016714 5ustar twernertwernerjamonapi/src/java/com/jamonapi/ActivityStats.java0000666000175000017500000000247510601470110022356 0ustar twernertwerner package com.jamonapi; /** * Class used to track the number of active monitors (including global/primary/this). * It allows you to see how many monitors are concurrently running at any given time. * * @author ssouza */ final class ActivityStats { final Counter allActive; // the number of monitors that are now running final Counter primaryActive; // the number of monitors marked primary that are now running final Counter thisActive; // the number of monitors of this type that are running /** Creates a new instance of Counters */ ActivityStats(Counter thisActive, Counter primaryActive, Counter allActive) { this.thisActive=thisActive; this.primaryActive=primaryActive; this.allActive=allActive; } ActivityStats() { this(new Counter(),new Counter(),new Counter()); } /** The number of all active monitors running */ public double getGlobalActive() { return allActive.getCount(); } /** The number of primary monitors running */ public double getPrimaryActive() { return primaryActive.getCount(); } /** The number monitors of this type that are running */ public double getActive() { return thisActive.getCount(); } } jamonapi/src/java/com/jamonapi/aop/0000777000175000017500000000000010674450734017473 5ustar twernertwernerjamonapi/src/java/com/jamonapi/aop/JAMonEJBInterceptor.java0000666000175000017500000000331610674051434024037 0ustar twernertwernerpackage com.jamonapi.aop; import javax.interceptor.AroundInvoke; import javax.interceptor.InvocationContext; import com.jamonapi.MonitorFactory; import com.jamonapi.Monitor; import com.jamonapi.MonKeyImp; import com.jamonapi.utils.Misc; public class JAMonEJBInterceptor { private static final String EXCEPTION_STR="JAMonEJBInterceptor.EJBException"; private static final int EXCEPTION=1; @AroundInvoke public Object intercept(InvocationContext ctx) throws Exception { Object[] details=null; Monitor mon=null; String label=null; try { label=new StringBuffer("JAMonEJBInterceptor: ").append(ctx.getTarget().getClass().getName()).append(".").append(ctx.getMethod().getName()).toString(); details=new Object[] {label, ""}; mon=MonitorFactory.start(new MonKeyImp(label, details, "ms.")); return ctx.proceed(); } catch (Exception e) { details[EXCEPTION]=Misc.getExceptionTrace(e); MonitorFactory.add(new MonKeyImp(EXCEPTION_STR, details, "Exception"), 1); MonitorFactory.add(new MonKeyImp(MonitorFactory.EXCEPTIONS_LABEL, details, "Exception"), 1); throw e; } finally { mon.stop(); } } public String delme(String hello) throws Exception { return "hello"; } public static void main(String[] args) throws Exception { JAMonEJBInterceptor o=new JAMonEJBInterceptor(); System.out.println(o.getClass().getMethod("delme", new Class[]{String.class})); } } jamonapi/src/java/com/jamonapi/BasicTimingMonitor.java0000666000175000017500000000211310367776310023315 0ustar twernertwernerpackage com.jamonapi; /** The most basic of timing Monitors. It is very close in performance to making straight calls to System.currentTimeMillis(). * This class is used primarily for comparison to the best case performance numbers for all of the other Monitors, * however it may also be used to make timing measurements. * * BasicTimingMonitors are not thread safe. Monitors returned via MonitorFactory are. * * Sample call: * BasicTimingMonitor mon=new BasicTimingMonitor(); * mon.start(); * ...code being monitored... * mon.stop(); **/ final public class BasicTimingMonitor { // Note. this class does not implement the Monitor interface and is not used in the // rest of the monitor framework. However it can be used if performance comparable // to simple times to currentTimeMillis() are required. private long startTime; public void start() { startTime=System.currentTimeMillis(); } public long stop() { return System.currentTimeMillis()-startTime; } } jamonapi/src/java/com/jamonapi/CompositeListener.java0000666000175000017500000001312510671013542023216 0ustar twernertwernerpackage com.jamonapi; import java.util.*; import com.jamonapi.utils.*; /** A class that can contain other listeners that can listen to jamon events of interest. * These classes will all implement the JAMonListener interface too. This is an example of the * Gang of 4 Composite design pattern. * @author steve souza * */ public class CompositeListener implements JAMonListener, DetailData { // A variable that will hold the list of Listeners private List listenerList=new ArrayList(); // The name of the composite listener private String name; /** Uses the CompositeListener name */ public CompositeListener() { this("CompositeJAMonListener"); } /** Pass in a Listener name that allows you to differentiate this listener from others */ public CompositeListener(String name) { this.name=name; } /** Add a listener to the composite and return this object */ public CompositeListener addListener(JAMonListener listener) { listenerList.add(listener); return this; } /* Return the listener associated with the passed in name */ public JAMonListener getListener(String listenerName) { int rows=getNumListeners(); for (int i=0;i0) range=new RangeBase(rangeHolder); rangeFactory.setRangeDefault(key, range ); } public String[] getRangeHeader() { return rangeFactory.getHeader(); } public Object[][] getRangeNames() { return rangeFactory.getData(); } public void remove(MonKey key) { map.remove(key); } public void remove(String label, String units) { map.remove(new MonKeyImp(label, units)); } public boolean exists(MonKey key) { return map.containsKey(key); } public boolean exists(String label, String units) { return map.containsKey(new MonKeyImp(label, units)); } public int getNumRows() { return map.size(); } // MonitorComposite methods /** getComposite("AllMonitors") is the same as getRootMonitor() */ public MonitorComposite getRootMonitor() { return new MonitorComposite(getMonitors()); } /** Pass in the units (or range type) and return all monitors of that * type. 'AllMonitors' is a special argument returns a composite of surprise surprise all monitors *getComposite("AllMonitors") is the same as getRootMonitor() ; **/ public MonitorComposite getComposite(String units) { return new MonitorComposite(getMonitors(units)); } public String getVersion() { return VERSION; } // INTERNALS/PRIVATES FOLLOW private MonitorImp getTimeMonitor(MonKey key, boolean isPrimary) { return getMonitor(key, isPrimary, TIME_MONITOR); } /***This was changed 11/24/06 to fix syncronization problems reported as bug */ private synchronized MonitorImp getMonitor(MonKey key, boolean isPrimary, boolean isTimeMonitor) { // note using MonKey over String concatenation doubled the speed // of the code, and was only slightly slower than using just the label // as the key MonitorImp mon=getExistingMonitor(key); // chance of 2 threads going into the next code simultaneously if (mon==null) { mon=createMon(key, isPrimary, isTimeMonitor); putMon(key, mon); } if (mon.isEnabled()) { mon = (isTimeMonitor) ? new TimeMon(key, mon.getMonInternals()) : new DecoMon(key, mon.getMonInternals()); } return mon; } private MonitorImp createMon(MonKey key, boolean isPrimary, boolean isTimeMonitor) { ActivityStats activityStats=new ActivityStats(new Counter(), primaryActive, allActive); // get default range for this type and assign it to the monitor RangeImp range=rangeFactory.getRangeDefault(key.getRangeKey(), activityStats); MonitorImp mon=new MonitorImp(key, range, activityStats, isTimeMonitor); mon.setPrimary(isPrimary); return mon; } private MonitorImp[] getMonitors(String units) { MonitorImp[] monitors=getMonitors(); if (monitors==null || units==null) return null; else if ("AllMonitors".equalsIgnoreCase(units)) return monitors; List rows=new ArrayList(500); int size=monitors.length; for (int i=0;i representative values returned from response methods *
response.getBufferSize()=8192
response.getCharacterEncoding()=utf-8
response.getContentCount()=985
response.getContentType()=text/html;charset=utf-8
response.getLocale()=en_US

representative values returned from request methods
request.getAuthType()=null
request.getCharacterEncoding()=null
request.getContentLength()=-1
request.getCharacterEncoding()=null
request.getContentType()=null
request.getContextPath()=/jamon
request.getLocalAddr()=127.0.0.1
request.getLocale()=en_US
request.getLocalName()=localhost
request.getLocalPort()=8080
request.getMethod()=GET
request.getPathInfo()=null
request.getPathTranslated()=null
request.getProtocol()=HTTP/1.1
request.getQueryString()=name=steve%20souza&id=9898
request.getRemoteAddr()=127.0.0.1
request.getRemoteHost()=127.0.0.1
request.getRemotePort()=1454
request.getRemoteUser()=null
request.getRequestedSessionId()=670BFE2B4A7C7C77D9825EFA753D2058
request.getRequestURI()=/jamon/ZZZZ
request.getRequestURL()=http://localhost:8080/jamon/ZZZZ
request.getScheme()=http
request.getServerName()=localhost
request.getServerPort()=8080
request.getServletPath()=/ZZZZ
request.getUserPrincipal()=null
request.isRequestedSessionIdFromCookie()=true
request.isRequestedSessionIdFromURL()=false
request.isRequestedSessionIdValid()=false
request.isSecure()=false * * @author steve souza */ public class HttpMonFactory implements HttpMonManage { private static final String DEFAULT_SUMMARY="request.getRequestURI().ms as allPages, request.getRequestURI().value.ms as page"; private String jamonSummaryLabels="default";// will do the above monitors if the word default is used in this variable. private Collection httpMonItemsHolder=new ArrayList();// Holds HttpMonItems private boolean ignoreHttpParams=true;// ignore http params if getRequestURI, or getRequestURL are called. This done to primarily to prevent // jsessionid from becoming part of a jamon label. By default params are removed (i.e. true) private String labelPrefix; // prefix used for jamon labels private boolean enabled=true; //Enable/Disable httpMonitoring. By default it is on private int numTimeMons=0;// The Number of monitors that are time based ones. // The size value will not allow any more http stats to be put into jamon if the total number of jamon entries exceeds 5000 entries. // This value may be changed to anything. Note jamon entries can still be added via standard jamon calls (might want to add this feature // there too). This is to prevent a buffer overflow should someone keep submitting invalid pages when a record is created for each page. private int size=5000; private static final HttpMon NULL_HTTP_MON=new HttpMonNull();// used when monitoring is disabled. /** Create an HttpMonFactory by passing in text that occurs at the beginning of all jamon labels. ex com.jamonapi.http.JAMonTomcatValve */ public HttpMonFactory(String labelPrefix) { this.labelPrefix=labelPrefix; } /** Pass a series of things (HttpServletRequest/HttpServletResponse methods) to monitor. If the word 'default' is passed then the default * values will be used. If 'demo' is passed you will see a list of possibilities (This should not be done in production as too much data would be * generated). You can add to the default by doing passing "default, request.getStatus().httpStatus". Each time this method is called any previously * set items that were being monitored will not longer be monitored. See http://www.jamonapi.com for more examples. */ public void setSummaryLabels(String jamonSummaryLabels) { this.jamonSummaryLabels=""; this.httpMonItemsHolder=new ArrayList(); this.numTimeMons=0; if (jamonSummaryLabels==null) return; // replace the word 'demo' with a good sampling of possibilities jamonSummaryLabels=jamonSummaryLabels.replaceAll("(?i)demo", getDemoLabels()); // replace string 'default' with the actual default values jamonSummaryLabels=replaceDefault(jamonSummaryLabels, DEFAULT_SUMMARY); // tokenize the string and add each of the HttpMonItems String[] summaryLabelsArr=split(jamonSummaryLabels); for (int i=0;i 0) then don't create a monitor. // if the monitor already exists then you can proceed as no more monitors will be created. return MonitorFactory.getNumRows()>httpMonBase.getSize() && httpMonBase.getSize()>0 && !MonitorFactory.exists(getMonKey(httpMonBase)); } // Track anytime the monitor threshold has been exceeded. private void createSizeThresholdExceededMon(HttpMonRequest httpMonBase) { String label=new StringBuffer(httpMonBase.getLabelPrefix()).append(".HttpMonFactory.sizeExceeded.").append(httpMonBase.getSize()).toString(); String detailLabel=new StringBuffer(getLabel(httpMonBase)).append(", ").append(httpMonBase.getRequestURI()).append(", ").append(getUnits()).toString(); MonitorFactory.add(new MonKeyImp(label, detailLabel, "Count"), httpMonBase.getSize()); } // return key. note we are passing in details that can be used in the detail buffer. Details consist of the uri as well as a stack trace // if one occured. MonKey getMonKey(HttpMonRequest httpMonBase) { return new MonKeyImp(getLabel(httpMonBase), httpMonBase.getDetails(), getUnits()); } /** If a value label is to be used then append it to the end of the label, else just return the label. The results will be used to create * a jamon record. Ex: request.getMethod() or request.getMethod().post * @return */ String getLabel(HttpMonRequest httpMonBase) { if ("".equals(additionToLabel)) return label; // add value and/or url if they are required in the label. StringBuffer sb=new StringBuffer(label); if ( !(label.charAt(label.length()-1)=='.'))// in some cases if this isn't done then the label has no dots, or 2 consecutive ones. sb.append("."); for (int i=0;i=1)// i.e. both u and v are specified sb.append(", "); if (additionToLabel.charAt(i)=='v')//value sb.append("value: ").append(getValueLabel(httpMonBase)); else if (additionToLabel.charAt(i)=='u')//url sb.append("url: ").append(httpMonBase.getKeyReadyURI()); else if (additionToLabel.charAt(i)=='p')//path sb.append("contextpath: ").append(httpMonBase.getContextPath()); } return sb.toString(); } /** Used to create a jamon label like request.getMethod().post */ Object getValueLabel(HttpMonRequest httpMonBase) { Object valLabel=executeMethod(httpMonBase); if (removeHttpParams) valLabel=removeHttpParams(valLabel); return valLabel; } /* Return value to be added to jamon. Note this is not called when it is a time monitor. If the object returned is a number * then add that value to jamon else just add the number 1. */ double getValueToAdd(HttpMonRequest httpMonBase) { Object obj=executeMethod(httpMonBase); if (obj instanceof Number) return ((Number)obj).doubleValue(); else return 1.0; } // Execute the request or responses method. private Object executeMethod(HttpMonRequest httpMonBase) { Object retValue=null; try { retValue = getMethod(httpMonBase).invoke(getObjectToExecute(httpMonBase), (Object[])null);// null is noargs. } catch (Throwable e) {// note I don't want the program to abort due to monitoring so the exception is not being passed upstream MonitorFactory.add(new MonKeyImp(httpMonBase.getLabelPrefix()+".monError", new Object[]{this, Misc.getExceptionTrace(e)}, "Exception"),1); } return retValue; } // return method that we are getting monitoring info from. Method getMethod(HttpMonRequest httpMonBase) { try { if (method==null) method = getObjectToExecute(httpMonBase).getClass().getMethod(methodName, (Class[])null); } catch (NoSuchMethodException e) { } return method; } // get the object we are calling the monitoring method from Object getObjectToExecute(HttpMonRequest httpMonBase) { if (isResponse()) return httpMonBase.getResponse(); else return httpMonBase.getRequest(); } public String toString() { return new StringBuffer("label=").append(label).append(", units=").append(units).append(", methodName=").append(methodName).toString(); } // get alias portion of 'request.getRequestURI() AS myAlias. // this is used to put a more descriptive name in jamon. private static String colAlias(String str) { if (str==null) return null; String[] arr=str.split(" [aA][sS] "); if (arr.length==2) // returns pageHits when 'request.getRequestURI() as pageHits' is passed return arr[1].trim(); else return null; // returns request.getRequestURI() (no alias was used) } // return nonalias portion of string. private static String nonAlias(String str) { if (str==null) return null; String[] arr=str.split(" [aA][sS] "); return arr[0].trim(); // returns request.getRequestURI() (no alias was used) } // passing this: http://www.mypage.com:8080/page;jsessionid=5akalsdjfflj;other=9s0f0udsf?pageName=my.jsp // would return this: http://www.mypage.com:8080/page // used to remove jsessionid from page hits. private static String removeHttpParams(Object url) { if (url==null) return null; String urlStr=url.toString(); int paramIndex = urlStr.indexOf(";"); if (paramIndex == -1)// ; isn't in string return urlStr; else return urlStr.substring(0, paramIndex); } public static void main(String[] args) { System.out.println(colAlias("request.getMethod().units.value as MyLabel")); System.out.println(colAlias("request.getMethod().units.value")); // null no alias System.out.println(colAlias("request.getMethod()"));// null no alias System.out.println(nonAlias("request.getMethod().units.value as MyLabel")); System.out.println(removeHttpParams("http://www.mypage.com:8080/page;jsessionid=5akalsdjfflj;other=9s0f0udsf?pageName=my.jsp")); System.out.println(removeHttpParams("http://www.mypage.com:8080/page?pageName=my.jsp")); } } jamonapi/src/java/com/jamonapi/http/HttpMonManage.java0000666000175000017500000000473710673566576023266 0ustar twernertwernerpackage com.jamonapi.http; /** Interface for controlling what http request/response info that should be monitored */ interface HttpMonManage { public void setSummaryLabels(String jamonSummaryLabels); public String getSummaryLabels(); public void addSummaryLabel(String jamonSummaryLabel); /** * Containers (tomcat/jetty etc) put jessionid (and other params) as part of what is returned by HttpServletRequest.getRequestURI, and HttpServletRequest.getRequestURL. * This can make many pages not unique enough to benefit from jamon, so by default this part of the url is removed from the monitoring label. * Example this: /myapp/mypage.jsp;jsessionid=320sljsdofou * becomes this in the jamon label: /myapp/mypage.jsp * * getIgnoreHttpParams() - return if this is enabled or disabled (true means the params will be removed/ignored. This is the default behaviour) * setIgnoreHttpParams(boolean httpIgnoreParams) - set whether it is enabled or disabled (true means the params will be removed/ignored. This is the default behaviour) * */ public boolean getIgnoreHttpParams(); /** * Containers (tomcat/jetty etc) put jessionid (and other params) as part of what is returned by HttpServletRequest.getRequestURI, and HttpServletRequest.getRequestURL. * This can make many pages not unique enough to benefit from jamon, so by default this part of the url is removed from the monitoring label. * Example this: /myapp/mypage.jsp;jsessionid=320sljsdofou * becomes this in the jamon label: /myapp/mypage.jsp * * getIgnoreHttpParams() - return if this is enabled or disabled (true means the params will be removed/ignored. This is the default behaviour) * setIgnoreHttpParams(boolean httpIgnoreParams) - set whether it is enabled or disabled (true means the params will be removed/ignored. This is the default behaviour) * */ public void setIgnoreHttpParams(boolean ignoreHttpParams); /** Set maximum number of rows that can be in jamon before no more records are added. This will prevent jamon from growing unbounded */ public void setSize(int size); public int getSize(); /** enable/disable monitoring. Would be better to name them enable and isEnabled, but as far as I could tell tomcat can only initialize * getter and setter methods.*/ public void setEnabled(boolean enable); public boolean getEnabled(); } jamonapi/src/java/com/jamonapi/http/HttpMonNull.java0000666000175000017500000000172110670462754022764 0ustar twernertwernerpackage com.jamonapi.http; import java.io.IOException; import javax.servlet.ServletException; /** * HttpMon that is used instead of HttpMonRequest when monitoring is disabled. * Essentially this class will be used as a singleton with noop methods. * * @author steve souza * */ class HttpMonNull implements HttpMon { HttpMonNull() { } public HttpMon start() { return this; } public void stop() { } public String getDetailLabel() { return ""; } public Throwable getException() { return null; } public void setException(Throwable t) { } public void throwException(Throwable t) throws IOException, ServletException { if (t instanceof ServletException) throw (ServletException)t; else if (t instanceof IOException) throw (IOException)t; else throw new ServletException(t); } } jamonapi/src/java/com/jamonapi/http/HttpMonRequest.java0000666000175000017500000002146010674051434023475 0ustar twernertwernerpackage com.jamonapi.http; /** * Generic HttpMon object that monitors http request, and http response objects. * Includes any dynamic data needed by HttpMonItem such as url, request and response. This allows HttpMonItems to not be * created with each request. This object is constructed via HttpMonFactory and represents * all monitors for one page request. */ import java.util.Iterator; import javax.servlet.ServletException; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import com.jamonapi.MonKeyImp; import com.jamonapi.Monitor; import com.jamonapi.MonitorFactory; import com.jamonapi.utils.Misc; final class HttpMonRequest implements HttpMon { private static final String EXCEPTION_ATTR="javax.servlet.error.exception";// seems to be standarad exception property for tocmat, and jetty // request.getAttribute("javax.servlet.error.exception") private final Object request;//HttpServletRequest or child of it private final Object response;//HttpServletResponse or a child of it private final HttpMonFactory httpMonFactory; private Monitor[] timeMons;// an array that is created for each time monitor private int timeMonIndex=0;// index used in start/stop methods to keep track of the current time monitor private String keyReadyURI;// uri that has params removed such as jsessionid. This can be used as a jamon key. private String[] details=new String[]{null,""};// URI,EXCEPTION private static final int URI=0; private static final int EXCEPTION=1; private Throwable requestException; HttpMonRequest(Object request, Object response, HttpMonFactory httpMonFactory) { this.request=request; this.response=response; this.httpMonFactory=httpMonFactory; this.timeMons=(httpMonFactory.getNumTimeMons()>0) ? new Monitor[httpMonFactory.getNumTimeMons()] : null; details[URI]=getRequestURI(); } /* * Method called to start monitoring http requests, and responses. Loop through all * HttpMonItems starting each of them. Note state is passed into the stateless HttpMonItem * instances via 'this' instance. */ public HttpMon start() { timeMonIndex=0;// Index that is incremented for time monitors only Iterator iter=iter(); while (iter.hasNext()) { HttpMonItem monItem=(HttpMonItem) iter.next(); monItem.start(this); } return this; } /** Method called to stop any active http monitoring requests, and responses */ public void stop() { timeMonIndex=0; setException(); Iterator iter=iter(); while (iter.hasNext()) { HttpMonItem monItem=(HttpMonItem) iter.next(); monItem.stop(this); } // page hits do a stop } // Iterator over httpMonItems owned by the httpMonFactory private Iterator iter() { return httpMonFactory.iter(); } public Object[] getDetails() { return details; } /** Detail label used in jamon. It will always include the requestURI, and will also include * a stack trace if one occurred. */ public String getDetailLabel() { if ("".equalsIgnoreCase(details[EXCEPTION])) return getRequestURI(); else return new StringBuffer(getRequestURI()).append("\n").append(details[EXCEPTION]).toString(); } public Throwable getException() { return requestException; } public void setException(Throwable t) { this.requestException=t; } private void setException() { if (requestException==null && request instanceof HttpServletRequest) { Object exc=((HttpServletRequest)request).getAttribute(EXCEPTION_ATTR);// defined by all containers if (exc instanceof Throwable) setException((Throwable)exc);//sets requestException } // if an exception has occurred make neccesarry jamon records. if (requestException!=null) { StringBuffer trace=new StringBuffer("stackTrace=").append(Misc.getExceptionTrace(requestException)); if (requestException instanceof ServletException && ((ServletException)requestException).getRootCause()!=null) trace.append("\nrootCause=").append(Misc.getExceptionTrace(((ServletException)requestException).getRootCause())); setStackTrace(trace.toString()); String label=getLabelPrefix()+".ServletException"; MonitorFactory.add(new MonKeyImp(label, getDetails(), "Exception"), 1); MonitorFactory.add(new MonKeyImp(MonitorFactory.EXCEPTIONS_LABEL, getDetails(), "Exception"), 1); } } public void throwException(Throwable t) throws IOException, ServletException { setException(); if (t instanceof ServletException) throw (ServletException)t; else if (t instanceof IOException) throw (IOException)t; else throw new ServletException(t); } // Return the URI/URL of this request. As this is called often enough it is cached after the first call. String getRequestURI() { if (details[URI]!=null) return details[URI]; else if (request instanceof HttpServletRequest) { return ((HttpServletRequest)request).getRequestURI(); } else return ""; } // remove httpPrams from requestURI so it can be used as a jamon key without worry // of creating too many keys. String getKeyReadyURI() { if (keyReadyURI==null) keyReadyURI=removeHttpParams(getRequestURI()); return keyReadyURI; } String getContextPath() { if (request instanceof HttpServletRequest) { return ((HttpServletRequest)request).getContextPath(); } else return ""; } // used to cache Exception string as it would have to be generated once per monitor if it wasn't cached, and would also consume memory // for each creation. void setStackTrace(String stackTrace) { details[EXCEPTION]=stackTrace; } // Get the prefix used in the monitor label such as: com.jamonapi.http String getLabelPrefix() { return httpMonFactory.getLabelPrefix(); } // Ignore jsessionid's and other prams as part of the request. This is done to make requests more unique (i.e. /jamon/mypage.jsp?jessionid=ls883dds) // gets converted to /jamon/mypage.jsp boolean getIgnoreHttpParams() { return httpMonFactory.getIgnoreHttpParams(); } // Is monitoring enabled? boolean isEnabled() { return httpMonFactory.getEnabled(); } // Get the max possible size of the buffer before it fills. This is to avoid buffer overflow problems. int getSize() { return httpMonFactory.getSize(); } // httpservletrequest Object getRequest() { return request; } // httpservletresponse Object getResponse() { return response; } // If it is a timeMonitor then set the current monitor into the time array. This is called from httpMonItem // i.e. it is a callback method called from httpMonItem start at the appropriate time and it is a time monitor // Note strictly speaking the 'if' checks aren't required. However, my thought is that no app should ever // risk an exception being thrown due to monitoring void setTimeMon(Monitor mon) { if (timeMons!=null && timeMonIndex jamon JAMonServletFilter com.jamonapi.http.JAMonServletFilter JAMonServletFilter * */ public class JAMonServletFilter extends HttpServlet implements HttpMonManage, Filter { private static final long serialVersionUID = 27L; private static final String PREFIX="com.jamonapi.http.JAMonServletFilter"; private HttpMonFactory httpMonFactory=new HttpMonFactory(PREFIX); private final String jamonSummaryLabels="request.getRequestURI().ms as allPages, request.getRequestURI().value.ms as page, request.contextpath.ms"; public JAMonServletFilter() { setSummaryLabels(jamonSummaryLabels); } /** Servlet filter method that does the monitoring */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { HttpMon httpMon=null; try { httpMon=httpMonFactory.start(request, response); filterChain.doFilter(request, response); } catch (Throwable e) { httpMon.throwException(e); } finally { httpMon.stop(); } } public void setSummaryLabels(String jamonSummaryLabels) { httpMonFactory.setSummaryLabels(jamonSummaryLabels); } public String getSummaryLabels() { return httpMonFactory.getSummaryLabels(); } public void addSummaryLabel(String jamonSummaryLabel) { httpMonFactory.addSummaryLabel(jamonSummaryLabel); } public boolean getIgnoreHttpParams() { return httpMonFactory.getIgnoreHttpParams(); } public void setIgnoreHttpParams(boolean ignoreHttpParams) { httpMonFactory.setIgnoreHttpParams(ignoreHttpParams); } public void setEnabled(boolean enable) { httpMonFactory.setEnabled(enable); } public int getSize() { return httpMonFactory.getSize(); } public boolean getEnabled() { return httpMonFactory.getEnabled(); } public void setSize(int size) { httpMonFactory.setSize(size); } public void init(FilterConfig arg0) throws ServletException { } } jamonapi/src/java/com/jamonapi/http/JAMonTomcatValve.java0000666000175000017500000001106210674051434023642 0ustar twernertwernerpackage com.jamonapi.http; /** This valve works in tomcat 6 and jboss tomcat 5.5. An alternative approach is to use the jamontomcat-2.7.jar and this approach is required for * tomcat 4/5. The Valve architecture and signatures were changed between release 5 and 5.5. For tomcat 5.5 * this class should work in tomcats version of 5.5 but doesn't due to classloader issues. Instead put com.jamontomcatvalve.http.JAMonTomcat55Valve * in your server/classes/com/jamontomcat/http directory and put jamon's jar in common/lib. This approach should also work in tomcat 6 * though I didn't try it. This is a wrapper class for the true monitoring class of HttpMonFactory. * * Note *
*

*
*
* @author steve souza * */ import javax.servlet.*; import org.apache.catalina.Valve; import java.io.IOException; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; public class JAMonTomcatValve extends org.apache.catalina.valves.ValveBase implements HttpMonManage { private static final String PREFIX="com.jamonapi.http.JAMonTomcatValve"; private static final String DEFAULT_SUMMARY="default, response.getContentCount().bytes, response.getStatus().value.httpStatus, request.contextpath.ms"; private HttpMonFactory httpMonFactory=new HttpMonFactory(PREFIX); private final String jamonSummaryLabels="default"; public JAMonTomcatValve() { setSummaryLabels(jamonSummaryLabels); } /** * Extract the desired request property, and pass it (along with the * specified request and response objects) to the protected * process() method to perform the actual filtering. * This method must be implemented by a concrete subclass. * * @param request The servlet request to be processed * @param response The servlet response to be created * * @exception IOException if an input/output error occurs * @exception ServletException if a servlet error occurs * http://www.jdocs.com/tomcat/5.5.17/org/apache/catalina/valves/RequestFilterValve.html * * log response, request to see what they do. * debug mode? * test xml - read property */ public void invoke(Request request, Response response) throws IOException, ServletException { HttpMon httpMon=null; try { httpMon=httpMonFactory.start(request, response); Valve nextValve=getNext(); if (nextValve!=null) nextValve.invoke(request, response); } catch (Throwable e) { httpMon.throwException(e); } finally { httpMon.stop(); } } public void setSummaryLabels(String jamonSummaryLabels) { httpMonFactory.setSummaryLabels(jamonSummaryLabels, DEFAULT_SUMMARY); } public String getSummaryLabels() { return httpMonFactory.getSummaryLabels(); } public void addSummaryLabel(String jamonSummaryLabel) { httpMonFactory.addSummaryLabel(jamonSummaryLabel); } public boolean getIgnoreHttpParams() { return httpMonFactory.getIgnoreHttpParams(); } public void setIgnoreHttpParams(boolean ignoreHttpParams) { httpMonFactory.setIgnoreHttpParams(ignoreHttpParams); } public void setEnabled(boolean enable) { httpMonFactory.setEnabled(enable); } public int getSize() { return httpMonFactory.getSize(); } public boolean getEnabled() { return httpMonFactory.getEnabled(); } public void setSize(int size) { httpMonFactory.setSize(size); } public String getInfo() { return PREFIX; } } jamonapi/src/java/com/jamonapi/http/JettyHttpMonFactory.java0000666000175000017500000000072510673330600024467 0ustar twernertwernerpackage com.jamonapi.http; /** * Factory used in the JAMonJettyHandler. Usually this need not be used directly, but via JAMonJettyHandler. * @author steve souza * */ public class JettyHttpMonFactory extends HttpMonFactory { public JettyHttpMonFactory(String labelPrefix) { super(labelPrefix); } HttpMonItem createHttpMonItem(String label) { return new JettyHttpMonItem(label, this); } } jamonapi/src/java/com/jamonapi/http/JettyHttpMonItem.java0000666000175000017500000000310410674051434023756 0ustar twernertwernerpackage com.jamonapi.http; /** * used to monitor jetty requests via the JAMonJettyHandler. */ import com.jamonapi.Monitor; import com.jamonapi.MonitorFactory; import org.mortbay.jetty.Request; class JettyHttpMonItem extends HttpMonItem { JettyHttpMonItem() { } JettyHttpMonItem(String label, HttpMonFactory httpMonFactory) { super(label, httpMonFactory); } /** Jetty Handlers does not let jamon start/stop time them. It seems the request is done by the time jamon gets it. To overcome this use the jetty api * to get the time of a request for a page. If it isn't a jetty request then call the parent. */ // Only called if this is a time monitor i.e units are 'ms.' Monitor startTimeMon(HttpMonRequest httpMonBase) { if (httpMonBase.getRequest() instanceof Request) return MonitorFactory.getMonitor(getMonKey(httpMonBase)).start(); else return super.startTimeMon(httpMonBase); } // Only called if this is a time monitor i.e units are 'ms.' void stopTimeMon(HttpMonRequest httpMonBase) { if (httpMonBase.getRequest() instanceof Request) { Request request=(Request)httpMonBase.getRequest(); Monitor mon=httpMonBase.getNextTimeMon(); if (mon!=null) { mon.add(System.currentTimeMillis()-request.getTimeStamp()).stop();// figure elapsed time and then decrement active. } } else super.stopTimeMon(httpMonBase); } } jamonapi/src/java/com/jamonapi/JAMonArrayBufferListener.java0000666000175000017500000000276310673330600024356 0ustar twernertwernerpackage com.jamonapi; import com.jamonapi.utils.BufferList; public class JAMonArrayBufferListener extends JAMonBufferListener { /** * Constructor that creaates this object with its default name (the class * name) */ public JAMonArrayBufferListener() { super("JAMonArrayBufferListener"); } /** Pass in the jamonListener name */ public JAMonArrayBufferListener(String name) { super(name); } /** Name the listener and pass in the jamon BufferList to use */ public JAMonArrayBufferListener(String name, BufferList list) { super(name, list); } /** * When this event is fired the monitor will be added to the rolling buffer. * If it is a log4j monitor the buffer will be specific to log4j fields * (i.e.LoggingEvent info such as threadname, formattedmessage, exception * stack trace and a few others. If it is not then the super class's * processEvent is called. * */ public void processEvent(Monitor mon) { JAMonDetailValue jamDetail=mon.getJAMonDetailRow(); jamDetail.setKeyToString(false); addRow(jamDetail); } /** Makes a usable copy of this BufferListener */ public JAMonListener copy() { return new JAMonArrayBufferListener(getName(), getBufferList().copy()); } } jamonapi/src/java/com/jamonapi/JAMonBufferListener.java0000666000175000017500000000501710673330600023352 0ustar twernertwernerpackage com.jamonapi; import com.jamonapi.utils.*; /** JAMonListener that puts jamon data into a buffer that allows you to display the last N configurble * detail events. The buffer will have the detail label, value and invocation date for the monitor that * was fired. * * @author steve souza * */ //public class JAMonBufferListener implements JAMonListener, DetailData, CopyJAMonListener { public class JAMonBufferListener implements JAMonListener, CopyJAMonListener { private BufferList list; private String name; static final String[] DEFAULT_HEADER=new String[] {"Label","LastValue","Active","Date"}; static final int VALUE_COL=1; static final int DATE_COL=3; public JAMonBufferListener() { this("JAMonBufferListener"); } /** Pass in the jamonListener name */ public JAMonBufferListener(String name){ this(name, new BufferList(DEFAULT_HEADER,50)); } /** Name the listener and pass in the jamon BufferList to use */ public JAMonBufferListener(String name, BufferList list) { this.name=name; this.list=list; } /** When this event is fired the monitor will be added to the rolling buffer */ public void processEvent(Monitor mon) { list.addRow(mon.getJAMonDetailRow()); } /** Add a row to the buffer */ public void addRow(ToArray row) { list.addRow(row); } /** Add a row to the buffer */ public void addRow(Object[] row) { list.addRow(row); } /** get the underlying bufferList which can then be used to display its contents */ public BufferList getBufferList() { return list; } public String getName() { return name; } public void setName(String name) { this.name=name; } public JAMonListener copy() { return new JAMonBufferListener(getName(), list.copy()); } public DetailData getDetailData() { return new BufferListDetailData(list); } // /** // * Use getDetailData() instead. // * @deprecated // * // */ // public Object[][] getData() { // return list.getData(); // } // // // /** // * Use getDetailData() instead. // * @deprecated // * // */ // public String[] getHeader() { // return list.getHeader(); // } public int getRowCount() { return list.getRowCount(); } public boolean hasData() { return list.hasData(); } public boolean isEmpty() { return list.isEmpty(); } } jamonapi/src/java/com/jamonapi/JAMonDetailValue.java0000666000175000017500000000334710672050612022637 0ustar twernertwernerpackage com.jamonapi; import java.util.Date; import java.util.*; import com.jamonapi.utils.ToArray; import com.jamonapi.utils.Misc; /** Class used to add the label, value and time invoked for the associated monitor. Used in the * jamonBufferListener class. * * @author steve souza * */ public final class JAMonDetailValue implements ToArray { // private final String label; // monitor name private final MonKey key; private final double value; // monitors lastValue private final long time; // invocation time private final double active; private boolean keyToString=true; private Object[] row; static JAMonDetailValue NULL_VALUE=new JAMonDetailValue(new MonKeyImp("Null JAMonDetails Object","Null JAMonDetails Object",""),0,0,0); public JAMonDetailValue(MonKey key, double value, double active, long time) { // this.label=label; this.key=key; this.value=value; this.active=active; this.time=time; } /** Returns label, value, time as an Object[] of 3 values. */ public Object[] toArray() { if (row==null) { if (keyToString) row = new Object[]{Misc.getAsString(key.getDetails()),new Double(value), new Double(active), new Date(time)}; else { List list=new ArrayList(); Misc.addTo(list, key.getDetails()); list.add(new Double(value)); list.add(new Double(active)); list.add(new Date(time)); row=list.toArray(); } } return row; } public void setKeyToString(boolean keyToString) { this.keyToString=keyToString; } public boolean isKeyToString() { return keyToString; } } jamonapi/src/java/com/jamonapi/JAMonFilter.java0000666000175000017500000000433310627223522021663 0ustar twernertwerner package com.jamonapi; /** * * The following should be put in the Web Application's web.xml file to enable servlet monitoring. * This servlet filter will enable any file access to html, jpg, jsp, servlet or any other file * resource that is part of the web application. You can change the filter-mapping element below * in the web.xml file in include/not include different files from monitoring. Use JAMonAdmin.jsp * to display any collected data. * fdsapi JAMonFilter com.jamonapi.JAMonFilter JAMonFilter /* */ import java.io.IOException; import javax.servlet.*; import javax.servlet.http.*; public class JAMonFilter extends HttpServlet implements Filter { private static final long serialVersionUID = 1L; public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { Monitor allPages = MonitorFactory.start(new MonKeyImp("com.jamonapi.allPages",getURI(request),"ms.")); Monitor monitor = MonitorFactory.start(getURI(request)); try { filterChain.doFilter(request, response); } finally { monitor.stop(); allPages.stop(); } } protected String getURI(ServletRequest request) { if (request instanceof HttpServletRequest) { return ((HttpServletRequest) request).getRequestURI(); } else { return "Not an HttpServletRequest"; } } private FilterConfig filterConfig = null; public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; } public void destroy() { this.filterConfig = null; } } jamonapi/src/java/com/jamonapi/JAMonListener.java0000666000175000017500000000100010610740562022207 0ustar twernertwernerpackage com.jamonapi; import java.util.EventListener; /** Interface that can be implemented if you want to code something to listen for JAMon events * such as a new max/min/max active have occured, or even if the monitor has fired. It also implements * the java EventListener tag interface. * * @author steve souza * */ public interface JAMonListener extends EventListener { public String getName(); public void setName(String name); public void processEvent(Monitor mon); } jamonapi/src/java/com/jamonapi/JAMonListenerFactory.java0000666000175000017500000003631710673330600023557 0ustar twernertwernerpackage com.jamonapi; import java.util.*; import com.jamonapi.utils.*; /**

Factory used to hold JAMonListeners. Developers may put any listeners that implement * JAMonBufferListeners.

* *

Any listener may be retrieved by passing in the JAMonListener name. At this time JAMon ships * with the following listeners that can be referenced by name. Every buffer has a shared counterpart * that allows different montiors to share the same buffer. In every other way they are * the same as their similarly named counterparts. *

* *

* FIFOBuffer - Holds most recent objects in the buffer
* NLargestValueBuffer - Keeps the largest values in the buffer
* NSmallestValueBuffer - Keeps the smallest values in the buffer
* NLargestValueBuffer7Days - When buffer is full the oldest data in buffer that is over 7 days is removed. If no data * is older than 7 days then the smallest is removed.
* NLargestValueBuffer24Hrs - When buffer is full the oldest data in buffer that is over 24 hours is removed. If no data * is older than 7 days then the smallest is removed.
* NSmallestValueBuffer7Days - When buffer is full the oldest data in buffer that is over 7 days is removed. If no data * is older than 7 days then the largest is removed.
* NSmallestValueBuffer24Hrs - When buffer is full the oldest data in buffer that is over 24 hours is removed. If no data * is older than 7 days then the largest is removed.
* SharedFIFOBuffer - Holds most recent objects in the buffer
* SharedNLargestValueBuffer - Keeps the largest values in the buffer
* SharedNSmallestValueBuffer - Keeps the smallest values in the buffer
* SharedNLargestValueBuffer7Days - When buffer is full the oldest data in buffer that is over 7 days is removed. If no data * is older than 7 days then the smallest is removed.
* SharedNLargestValueBuffer24Hrs - When buffer is full the oldest data in buffer that is over 24 hours is removed. If no data * is older than 7 days then the smallest is removed.
* SharedNSmallestValueBuffer7Days - When buffer is full the oldest data in buffer that is over 7 days is removed. If no data * is older than 7 days then the largest is removed.
* SharedNSmallestValueBuffer24Hrs - When buffer is full the oldest data in buffer that is over 24 hours is removed. If no data * is older than 7 days then the largest is removed.
* HTTPBufferListener - Buffer that holds data specific to http requests.
* ExceptionBufferListener - Buffer that holds data specific to monitors that track exceptions in the detail buffer
* *

* @author steve souza * */ public class JAMonListenerFactory { private static final boolean NATURAL_ORDER=true; private static final boolean REVERSE_ORDER=false; private static String[] HEADER={"ListenerName", "Listener"}; private static Map map=Misc.createCaseInsensitiveMap(); static { // put factory instances into the JAMonListenerFactory put(getFIFO()); put(getNLargest()); put(getNSmallest()); put(getNLargest7Days()); put(getNLargest24Hrs()); put(getNSmallest7Days()); put(getNSmallest24Hrs()); // allows sharing buffers between monitors! put(getSharedFIFO()); put(getSharedNLargest()); put(getSharedNSmallest()); put(getSharedNLargest7Days()); put(getSharedNLargest24Hrs()); put(getSharedNSmallest7Days()); put(getSharedNSmallest24Hrs()); put(getExceptionBufferListener()); put(getHTTPBufferListener()); } /** Developers may register their own listeners to be made available for use in JAMon */ public static void put(JAMonListener jamonListener) { map.put(jamonListener.getName(), jamonListener); } /** Returns an array of all registered JAMonListeners in the format: key, JamonListener factory instance */ public static Object[][] getData() { Iterator iter=map.entrySet().iterator(); Object[][] data=new Object[map.size()][]; int i=0; while (iter.hasNext()) { data[i]=new Object[2]; Map.Entry entry=(Map.Entry) iter.next(); data[i][0]=entry.getKey(); data[i][1]=entry.getValue(); i++; } return data; } /** Returns the header for display of JAMonListeners */ public static String[] getHeader() { return HEADER; } /** Get an instance of the named factory instance. If the Liistener implements * CopyJAMonListener then copy will be called. If not then the default constructor will * be called. * * @param listenerName * @return */ public static JAMonListener get(String listenerName) { try { JAMonListener factoryInstance =(JAMonListener)map.get(listenerName); if (factoryInstance instanceof CopyJAMonListener) { return ((CopyJAMonListener)factoryInstance).copy(); } else { JAMonListener newInst = (JAMonListener)factoryInstance.getClass().newInstance(); newInst.setName(factoryInstance.getName()); return newInst; } } catch (Exception e) { throw new RuntimeException("Error getting listener from factory: "+listenerName+", "+e); } } // Various factory methods used to populate the JAMonListenerFactory with default // JAMonBufferListeners private static BufferHolder getBufferHolderNLargest7Days() { // Keeps only the largest values within 7 days. JAMonArrayComparator jac=new JAMonArrayComparator(); DateMathComparator dmc=new DateMathComparator(Calendar.DAY_OF_YEAR, -7); jac.addCompareCol(JAMonBufferListener.DATE_COL, dmc); jac.addCompareCol(JAMonBufferListener.VALUE_COL, NATURAL_ORDER); BufferHolder bufferHolder=new NExtremeArrayBufferHolder(jac); return bufferHolder; } private static BufferHolder getBufferHolderNLargest24Hrs() { // Keeps only the largest values within 24 hrs. JAMonArrayComparator jac=new JAMonArrayComparator(); DateMathComparator dmc=new DateMathComparator(Calendar.HOUR_OF_DAY, -24); jac.addCompareCol(JAMonBufferListener.DATE_COL, dmc); jac.addCompareCol(JAMonBufferListener.VALUE_COL, NATURAL_ORDER); BufferHolder bufferHolder=new NExtremeArrayBufferHolder(jac); return bufferHolder; } private static BufferHolder getBufferHolderNSmallest7Days() { // Keeps only the smallest values within 7 days. JAMonArrayComparator jac=new JAMonArrayComparator(); DateMathComparator dmc=new DateMathComparator(Calendar.DAY_OF_YEAR, -7); jac.addCompareCol(JAMonBufferListener.DATE_COL, dmc); jac.addCompareCol(JAMonBufferListener.VALUE_COL, REVERSE_ORDER); BufferHolder bufferHolder=new NExtremeArrayBufferHolder(jac); return bufferHolder; } private static BufferHolder getBufferHolderNSmallest24Hrs() { // Keeps only the smallest values within 24 hrs. JAMonArrayComparator jac=new JAMonArrayComparator(); DateMathComparator dmc=new DateMathComparator(Calendar.HOUR_OF_DAY, -24); jac.addCompareCol(JAMonBufferListener.DATE_COL, dmc); jac.addCompareCol(JAMonBufferListener.VALUE_COL, REVERSE_ORDER); BufferHolder bufferHolder=new NExtremeArrayBufferHolder(jac); return bufferHolder; } private static JAMonBufferListener getFIFO() { BufferHolder bufferHolder=new FIFOBufferHolder(); BufferList bufferList=new BufferList(JAMonBufferListener.DEFAULT_HEADER,bufferHolder); return new JAMonBufferListener("FIFOBuffer", bufferList); } private static JAMonBufferListener getNLargest() { BufferHolder bufferHolder=new NExtremeArrayBufferHolder(NATURAL_ORDER, JAMonBufferListener.VALUE_COL); BufferList bufferList=new BufferList(JAMonBufferListener.DEFAULT_HEADER, bufferHolder); return new JAMonBufferListener("NLargestValueBuffer", bufferList); } private static JAMonBufferListener getNSmallest() { BufferHolder bufferHolder=new NExtremeArrayBufferHolder(REVERSE_ORDER, JAMonBufferListener.VALUE_COL); BufferList bufferList=new BufferList(JAMonBufferListener.DEFAULT_HEADER, bufferHolder); return new JAMonBufferListener("NSmallestValueBuffer", bufferList); } private static JAMonBufferListener getNLargest7Days() { BufferList bufferList=new BufferList(JAMonBufferListener.DEFAULT_HEADER, getBufferHolderNLargest7Days()); return new JAMonBufferListener("NLargestValueBuffer7Days", bufferList); } private static JAMonBufferListener getNLargest24Hrs() { BufferList bufferList=new BufferList(JAMonBufferListener.DEFAULT_HEADER, getBufferHolderNLargest24Hrs()); return new JAMonBufferListener("NLargestValueBuffer24Hrs", bufferList); } private static JAMonBufferListener getNSmallest7Days() { BufferList bufferList=new BufferList(JAMonBufferListener.DEFAULT_HEADER, getBufferHolderNSmallest7Days()); return new JAMonBufferListener("NSmallestValueBuffer7Days", bufferList); } private static JAMonBufferListener getNSmallest24Hrs() { BufferList bufferList=new BufferList(JAMonBufferListener.DEFAULT_HEADER, getBufferHolderNSmallest24Hrs()); return new JAMonBufferListener("NSmallestValueBuffer24Hrs", bufferList); } private static JAMonBufferListener getSharedFIFO() { BufferHolder bufferHolder=new FIFOBufferHolder(); BufferList bufferList=new BufferList(JAMonBufferListener.DEFAULT_HEADER,bufferHolder); return new SharedJAMonBufferListener("SharedFIFOBuffer", bufferList); } private static JAMonBufferListener getSharedNSmallest() { BufferHolder bufferHolder=new NExtremeArrayBufferHolder(REVERSE_ORDER, JAMonBufferListener.VALUE_COL); BufferList bufferList=new BufferList(JAMonBufferListener.DEFAULT_HEADER, bufferHolder); return new SharedJAMonBufferListener("SharedNSmallestValueBuffer", bufferList); } private static JAMonBufferListener getSharedNLargest() { BufferHolder bufferHolder=new NExtremeArrayBufferHolder(NATURAL_ORDER, JAMonBufferListener.VALUE_COL); BufferList bufferList=new BufferList(JAMonBufferListener.DEFAULT_HEADER, bufferHolder); return new SharedJAMonBufferListener("SharedNLargestValueBuffer", bufferList); } private static JAMonBufferListener getSharedNLargest7Days() { BufferList bufferList=new BufferList(JAMonBufferListener.DEFAULT_HEADER, getBufferHolderNLargest7Days()); return new SharedJAMonBufferListener("SharedNLargestValueBuffer7Days", bufferList); } private static JAMonBufferListener getSharedNLargest24Hrs() { BufferList bufferList=new BufferList(JAMonBufferListener.DEFAULT_HEADER, getBufferHolderNLargest24Hrs()); return new SharedJAMonBufferListener("SharedNLargestValueBuffer24Hrs", bufferList); } private static JAMonBufferListener getSharedNSmallest7Days() { BufferList bufferList=new BufferList(JAMonBufferListener.DEFAULT_HEADER, getBufferHolderNSmallest7Days()); return new SharedJAMonBufferListener("SharedNSmallestValueBuffer7Days", bufferList); } private static JAMonBufferListener getSharedNSmallest24Hrs() { BufferList bufferList=new BufferList(JAMonBufferListener.DEFAULT_HEADER, getBufferHolderNSmallest24Hrs()); return new SharedJAMonBufferListener("SharedNSmallestValueBuffer24Hrs", bufferList); } private static JAMonBufferListener getExceptionBufferListener() { String[] header=new String[] {"Label","Exception", "LastValue","Active","Date"}; BufferHolder bufferHolder=new FIFOBufferHolder(); BufferList bufferList=new BufferList(header,bufferHolder); return new JAMonArrayBufferListener("ExceptionBufferListener", bufferList); } private static JAMonBufferListener getHTTPBufferListener() { String[] header=new String[] {"Label","Exception", "LastValue","Active","Date"}; BufferHolder bufferHolder=new FIFOBufferHolder(); BufferList bufferList=new BufferList(header,bufferHolder); return new JAMonArrayBufferListener("HTTPBufferListener", bufferList); } // called from main method for testing purposes private static void testArray(String label, int dateToAdd, boolean increase) { System.out.print("\n\n****"+label); JAMonBufferListener jbl=(JAMonBufferListener)get(label); BufferList bl=jbl.getBufferList(); Calendar cal=new GregorianCalendar(); if (increase) { for (int i=1,j=-50;i<=100;i++,j++) { cal.setTime(new Date()); cal.add(dateToAdd, j); bl.addRow(new Object[]{"label"+i,new Integer(i),"Active"+i,cal.getTime()}); } } else { for (int i=100,j=50;i>=1;i--,j--) { cal.setTime(new Date()); cal.add(dateToAdd, j); bl.addRow(new Object[]{"label"+i,new Integer(i),"Active"+i,cal.getTime()}); } } // firstVal will be under date threshold, and secondVal will exceed it. int firstVal=-5; int secondVal=-10; if (dateToAdd==Calendar.HOUR_OF_DAY) { firstVal=-12; secondVal=-36; } cal.setTime(new Date()); cal.add(dateToAdd, firstVal); bl.addRow(new Object[]{"label",new Integer(1000),"Active",cal.getTime()}); cal.setTime(new Date()); cal.add(dateToAdd, firstVal); bl.addRow(new Object[]{"label",new Integer(-1000),"Active",cal.getTime()}); cal.setTime(new Date()); cal.add(dateToAdd, secondVal); bl.addRow(new Object[]{"label",new Integer(1000),"Active",cal.getTime()}); cal.setTime(new Date()); cal.add(dateToAdd, secondVal); bl.addRow(new Object[]{"label",new Integer(-1000),"Active",cal.getTime()}); Misc.disp(bl.getData()); } public static void main(String[] args) { System.out.println("/n*****Testing JAMonListenerFactory.main()"); put(new CompositeListener("tester")); put(new JAMonBufferListener("helloListener", new BufferList(new String[]{"hey"},200))); String[] header=getHeader(); Object[][] data=getData(); for (int i=0;i * Title: JAMonAppender *

*

* Description: log4j Appender to that allows you to summarize log4j stats via jamon and view * the tail of the log in realtime in a jamon web page. Click here for more info on how to use the JAMonAppender. *

* *

* Copyright: Copyright (c) 2007 *

* * @author Ed Desrosiers, Steve Souza */ public class JAMonAppender extends AppenderSkeleton { /* Prefix for this classes jamon monitor labels */ private final String PREFIX = "com.jamonapi.log4j.JAMonAppender."; // any of these poperties can be overridden via log4j configurators. private int bufferSize = 100; private String units = "log4j"; // units in jamon montiors // indicates whether or not log4j LoggingEvent info is placed in buffer. // This could potentially be slower though I didn't test it, and I // wouldn't be overly concerned about it. private boolean enableListenerDetails = true; // Enable monitoring of the various log4j levels in jamon. private boolean enableLevelMonitoring = true; private boolean generalize = false; private Generalizer generalizer = new DefaultGeneralizer(); static { // Register this object to be available for use in the // JAMonListenerFactory. JAMonListenerFactory.put(new Log4jBufferListener()); } public JAMonAppender() { } /** * If the appender is enabled then start and stop a JAMon entry. Depending * on how this object is configured it may also put details into a * JAMonBufferLister and generalize the logging message * (logger.error(message) etc) and put it in jamon too. By default it will * only do jamon records for each of the log4j Levels. * * @param event */ protected void append(LoggingEvent event) { String message = (getLayout() == null) ? event.getRenderedMessage() : getLayout().format( event); if (getEnableLevelMonitoring()) { // monitor that counts all calls to log4j logging methods MonitorFactory.add(createKey(PREFIX + "TOTAL", message, event), 1); // monitor that counts calls to log4j at each level (DEBUG/WARN/...) MonitorFactory.add(createKey(PREFIX + event.getLevel(), message, event), 1); } // if the object was configured to generalize the message then do as // such. This will create a jamon record with the generalized method // so it is important for the developer to ensure that the generalized // message is unique enough not to grow jamon unbounded. if (getGeneralize()) { MonitorFactory.add(createKey(generalize(message), message, event), 1); } } // Return a key that will put LoggingEvent info in a bufferlistenr if // enableListenerDetails has been enabled, // else simply use the standard jamon MonKeyImp private MonKey createKey(String summaryLabel, String detailLabel, LoggingEvent event) { if (enableListenerDetails) // put array in details buffer return new Log4jMonKey(summaryLabel, detailLabel, units, event); else return new MonKeyImp(summaryLabel, detailLabel, units); } /** * Required log4j method. Currently a no-op. */ public void close() { } /** *

* JAMonAppender doesn't have to have a layount because it is acceptable to * default to using the raw message. Not providing a layout will return a * log4j error that looks like the following, however it can safely be * ignored. Providing any layout for the JAMonAppender will make the error * go away. Unfortunately log4j doesn't have a way to specify an optional * layout. *

* *

* log4j:ERROR Could not find value for key * log4j.appender.jamonAppender.layout *

*/ public boolean requiresLayout() { return true; } /** * @return Returns the units. By default this is 'log4j' though it can be * changed. This is used as part of the jamon key. */ public String getUnits() { return units; } /** * @param units * The units to set. */ public void setUnits(String units) { this.units = units; } /** * Specifies whether or not LoggingEvent info will be used in the attached * Log4jBufferListener. By default this is enabled. */ public boolean getEnableListenerDetails() { return enableListenerDetails; } /** * Specify whether or not LoggingEvent info will be used in the attached * Log4jBufferListener */ public void setEnableListenerDetails(boolean arrayDetails) { this.enableListenerDetails = arrayDetails; } /** * Specifies whether or not there will be a JAMon record for each log4j * Level (DEBUG/WARN/...), and another one that corresponds to all calls to * log4j logging methods. It is identified by the label TOTAL. By default * this is enabled. */ public void setEnableLevelMonitoring(boolean enableLevelMonitoring) { this.enableLevelMonitoring = enableLevelMonitoring; } /** Returns whether or not LevelMonitoring is enabled or not. */ public boolean getEnableLevelMonitoring() { return enableLevelMonitoring; } /** * Note this is primarily used by the log4j configurator. Valid values are * the various log4j levels: DEBUG/ERROR/WARN/INFO/ERROR/FATAL, as well as * TOTAL (A listener that gets called for all levels), BASIC (same as * calling TOTAL/ERROR/FATAL), and ALL (same as calling * ERROR/WARN/INFO/ERROR/FATAL/TOTAL). Values are not case sensitive. . * * @param enableListeners */ public void setEnableListeners(String level) { if (Level.DEBUG.toString().equalsIgnoreCase(level.toUpperCase())) addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.DEBUG, units)); else if (Level.INFO.toString().equalsIgnoreCase(level.toUpperCase())) addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.INFO, units)); else if (Level.WARN.toString().equalsIgnoreCase(level.toUpperCase())) addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.WARN, units)); else if (Level.ERROR.toString().equalsIgnoreCase(level.toUpperCase())) addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.ERROR, units)); else if (Level.FATAL.toString().equalsIgnoreCase(level.toUpperCase())) addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.FATAL, units)); else if ("TOTAL".toString().equalsIgnoreCase(level.toUpperCase())) addDefaultListener(MonitorFactory.getMonitor(PREFIX + "TOTAL", units)); else if (Level.ALL.toString().equalsIgnoreCase(level.toUpperCase())) { addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.DEBUG, units)); addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.INFO, units)); addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.WARN, units)); addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.ERROR, units)); addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.FATAL, units)); addDefaultListener(MonitorFactory.getMonitor(PREFIX + "TOTAL", units)); } else if ("BASIC".toString().equalsIgnoreCase(level.toUpperCase())) { addDefaultListener(MonitorFactory.getMonitor(PREFIX + "TOTAL", units)); addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.ERROR, units)); addDefaultListener(MonitorFactory.getMonitor(PREFIX + Level.FATAL, units)); } } // Add a Log4jBufferListener to the passed in Monitor private void addDefaultListener(Monitor mon) { if (!mon.hasListeners()) { Log4jBufferListener listener = new Log4jBufferListener(); listener.getBufferList().setBufferSize(bufferSize); mon.getListenerType("value").addListener(listener); } } /** * For defaultBufferSize to take hold it must be called before the first * call to setDefaultListeners. By default the buffer size is 100. * * @param bufferSize */ public void setListenerBufferSize(int bufferSize) { this.bufferSize = bufferSize; } /** Indicate whether or not a jamon record should be created from the passed in message. * Note you can use the DefaultGeneralizer, your own Generalizer. It is very important that * you ensure the String returned by the generalizer is unique enough that JAMon doesn't grow unbounded. * For example by choosing to use no Generalizer you must pass in a relatively unique log4j string. * @param generalize */ public void setGeneralize(boolean generalize) { this.generalize = generalize; } /** Return whether or not generalization will occur */ public boolean getGeneralize() { return generalize; } /** generalize the passed in String if a Genaralizer is set */ private String generalize(String detailedMessage) { return (generalizer != null) ? generalizer.generalize(detailedMessage) : detailedMessage; } /** Enable the use of the DefaultGeneralizer. As a side effect setGeneralize(true) is called * telling this class to generalize. * @param enableDefaultGeneralizer */ public void setEnableDefaultGeneralizer(boolean enableDefaultGeneralizer) { if (enableDefaultGeneralizer) { this.generalizer = new DefaultGeneralizer(); setGeneralize(true); } else this.generalizer = null; } /** Indicates whether or not a Generalizer has been set */ public boolean hasGeneralizer() { return (generalizer != null); } /** * Default generalizer based on com.jamonapi.utils.SQLDeArger. It * generalizes by replacing numbers and strings in single or double quotes * with '?'. i.e. select * from table where name = 'steve' and id=50 becomes * select * from table where name = ? and id=?. Developers can provide their * own Generalizer if this is not the desired behaviour. Although the * example uses a query the code works equally well with any String. The * generalizer is used to create a record appropriate for jamon from a * detail String that goes to log4j. */ public void setGeneralizerClass(Generalizer generalizer) { this.generalizer = generalizer; } /** Pass in a string class name and this generalizer will be constructed an used. For example com.jamonapi.utils.DefaultGeneralizer could be passed in * * @param generalizerClassStr * @throws InstantiationException * @throws IllegalAccessException * @throws ClassNotFoundException */ public void setGeneralizerDynamic(String generalizerClassStr) throws InstantiationException, IllegalAccessException, ClassNotFoundException { this.generalizer = (Generalizer) Class.forName(generalizerClassStr).newInstance(); } } jamonapi/src/java/com/jamonapi/log4j/Log4jBufferListener.java0000666000175000017500000001040410672050612024401 0ustar twernertwernerpackage com.jamonapi.log4j; import org.apache.log4j.spi.LoggingEvent; import com.jamonapi.JAMonBufferListener; import com.jamonapi.JAMonListener; import com.jamonapi.Monitor; import com.jamonapi.MonKey; import com.jamonapi.utils.BufferList; import com.jamonapi.utils.Misc; /** *

* This class can act as a standard JAMonBufferListener/FIFOBuffer or more * interestingly if used with log4j it will put in the Buffer data that although * designed to work as a Buffer that displays details unique to log4j, if the * monitor does not have a Log4jMonKey, it will fallback to Standard * JAMonBufferListener behaviour. This makes it so no problems exist if someone * inadvertently assigns a Log4jBufferListener to a non log4j entity. *

* * * @author steve souza * */ public class Log4jBufferListener extends JAMonBufferListener { private boolean isLog4jMonKey = false;// looks at data to determine if a // key is log4j. if so // header used to display details in the JAMonBufferListener. private static final String[] LOG4J_HEADER = new String[] { "LoggerName", "Level", "ThreadName", "FormattedMessage", "Date", "Exception" }; //{"Label","LastValue","Active","Date"};more data /** * Constructor that creaates this object with its default name (the class * name) */ public Log4jBufferListener() { super("Log4jBufferListener"); } /** Pass in the jamonListener name */ public Log4jBufferListener(String name) { super(name); } /** Name the listener and pass in the jamon BufferList to use */ public Log4jBufferListener(String name, BufferList list) { super(name, list); } private Log4jBufferListener(String name, BufferList list, boolean isLog4jMonKey) { this(name, list); this.isLog4jMonKey = isLog4jMonKey; } /** * When this event is fired the monitor will be added to the rolling buffer. * If it is a log4j monitor the buffer will be specific to log4j fields * (i.e.LoggingEvent info such as threadname, formattedmessage, exception * stack trace and a few others. If it is not then the super class's * processEvent is called. * */ public void processEvent(Monitor mon) { MonKey monKey = mon.getMonKey(); // If the key is a log4j key it has the LoggingEvent in it and we can // put that data into the buffer. If the first record is passed and it is a Log4JMonKey then use // more specific log4j detail buffer array. Note this variable is also used in the header method. if (monKey instanceof Log4jMonKey && !isLog4jMonKey) isLog4jMonKey = true; if (isLog4jMonKey) { Log4jMonKey key = (Log4jMonKey) monKey; getBufferList().addRow(toArray(key.getLoggingEvent(), mon)); } else super.processEvent(mon); } /** * method that returns an array to use in the Buffer. It can return any * sortable objects as long as they match what is returned in the * getHeader() method. * * @param event * @param mon * @return */ protected Object[] toArray(LoggingEvent event, Monitor mon) { return new Object[] { event.getLoggerName(), event.getLevel().toString(), event.getThreadName(), mon.getMonKey().getDetails(), mon.getLastAccess(), (event.getThrowableInformation() == null || event.getThrowableInformation().getThrowable() == null) ? "" : Misc.getExceptionTrace(event.getThrowableInformation().getThrowable()) }; } protected String[] getDefaultHeader() { return LOG4J_HEADER; } /** Makes a usable copy of this BufferListener */ public JAMonListener copy() { return new Log4jBufferListener(getName(), getBufferList().copy(), isLog4jMonKey); } /** Returns the valid header for display of this buffer */ public String[] getHeader() { if (isLog4jMonKey) return getDefaultHeader(); else return getBufferList().getHeader(); } } jamonapi/src/java/com/jamonapi/log4j/Log4jMonKey.java0000666000175000017500000000270610664052302022671 0ustar twernertwernerpackage com.jamonapi.log4j; import org.apache.log4j.spi.LoggingEvent; import com.jamonapi.*; /** * MonKey used to put log4j records into jamon hashmap. It is the same as * MonKeyImp except it also carries with it a log4j LoggingEvent. This is not * used as part of the key, but is used to display log4j info in any * BufferListeners this object has. To take maximum advantage of the data in the * LoggingEvent of this key use a Log4jBufferListener for log4j JAMon monitors. * A regular FIFO buffer can also be used, however all info in the LoggingEvent * won't be used in this case. * * @author steve souza * */ public class Log4jMonKey extends MonKeyImp { /** Constructor for building jamon key for log4j */ public Log4jMonKey(String summaryLabel, String detailLabel, String units, LoggingEvent event) { super(summaryLabel, detailLabel, units); setParam(event); } /** Return the log4j LoggingEvent object that is part of this key */ public LoggingEvent getLoggingEvent() { return (LoggingEvent) getParam(); } /** * Returns any object that has a named key. For this object 'label' and * 'units', and 'LoggingEvent' are valid. It is case insenstive. */ public Object getValue(String key) { if ("LoggingEvent".equalsIgnoreCase(key)) return getParam(); else return super.getValue(key); } } jamonapi/src/java/com/jamonapi/log4j/Log4jTester.java0000666000175000017500000000467010630526410022736 0ustar twernertwernerpackage com.jamonapi.log4j; import java.util.Properties; import org.apache.log4j.PropertyConfigurator; import org.apache.log4j.Logger; import com.jamonapi.MonitorFactory; import com.jamonapi.utils.Misc; public class Log4jTester { private static Properties getDefaultProps() { // # Set root logger level to DEBUG and its only appender to A1. Properties properties = new Properties(); properties.put("log4j.logger.com.jamonapi.log4j", "DEBUG, A1, jamonAppender"); // # A1 is set to be a ConsoleAppender, and A2 uses JAMonAppender. properties.put("log4j.appender.A1", "org.apache.log4j.ConsoleAppender"); properties.put("log4j.appender.jamonAppender", "com.jamonapi.log4j.JAMonAppender"); properties.put("log4j.appender.jamonAppender.units", "testlog4jUnits"); properties.put("log4j.appender.jamonAppender.enableDefaultGeneralizer", "true"); properties.put("log4j.appender.jamonAppender.EnableListeners", "BASIC"); properties.put("log4j.appender.jamonAppender.EnableListenerDetails", "true"); properties.put("log4j.appender.jamonAppender.EnableLevelMonitoring", "true"); properties.put("log4j.appender.jamonAppender.ListenerBufferSize", "200"); // # jamonAppender uses PatternLayout. properties.put("log4j.appender.A1.layout", "org.apache.log4j.PatternLayout"); properties.put("log4j.appender.A1.layout.ConversionPattern", "%-4r steve [%t] %-5p %c %x - %m%n"); // # A1 uses PatternLayout. properties.put("log4j.appender.jamonAppender.layout", "org.apache.log4j.PatternLayout"); properties.put("log4j.appender.jamonAppender.layout.ConversionPattern", "%p.%c.%m"); return properties; } public static void main(String[] args) throws Exception { PropertyConfigurator.configure(getDefaultProps()); Logger log1 = Logger.getLogger("com.jamonapi.log4j"); for (int i = 0; i < 5; i++) { log1.debug("message " + i); log1.error("message " + i); log1.info("message " + i); } Misc.disp(MonitorFactory.getRootMonitor().getData()); for (int i = 0; i < 5; i++) { log1.debug("message " + i); log1.error("message " + i); log1.info("message " + i); } Misc.disp(MonitorFactory.getRootMonitor().getData()); } } jamonapi/src/java/com/jamonapi/MonInternals.java0000666000175000017500000000506110611765154022166 0ustar twernertwernerpackage com.jamonapi; import java.util.Iterator; import com.jamonapi.utils.Misc; /** * Contains the data associated with a monitor. These internals can be passed * around and shared by other monitor instances that are tracking aggregate * stats for the same MonKey. It mostly acts as a Struct with the exception of * the reset() method. * * Created on December 11, 2005, 10:19 PM */ final class MonInternals { /** seed value to ensure that the first value always sets the max */ static final double MAX_DOUBLE = -Double.MAX_VALUE; /** seed value to ensure that the first value always sets the min */ static final double MIN_DOUBLE = Double.MAX_VALUE; MonKey key; /** the total for all values */ double total = 0.0; /** The minimum of all values */ double min = MIN_DOUBLE; /** The maximum of all values */ double max = MAX_DOUBLE; /** The total number of occurrences/calls to this object */ double hits = 0.0; /** Intermediate value used to calculate std dev */ double sumOfSquares = 0.0; /** The most recent value that was passed to this object */ double lastValue = 0.0; /** The first time this object was accessed */ long firstAccess = 0; /** The last time this object was accessed */ long lastAccess = 0; /** Is this a time monitor object? Used for performance optimizations */ boolean isTimeMonitor = false; /** * jamon 2.4 from BaseMon enable/disable */ boolean enabled = true; boolean trackActivity = false; String name = "";// for regular monitors empty. For range monitors // "Range1_" String displayHeader = "";// for regular monitors empty. rangeholder name // for ranges (i.e. 0_20ms) /** * added for jamon 2.4 from Mon */ double maxActive = 0.0; double totalActive = 0.0; boolean isPrimary = false; boolean startHasBeenCalled = true; ActivityStats activityStats; // from MonitorImp RangeImp range; double allActiveTotal; // used to calculate the average active total // monitors for this distribution double primaryActiveTotal; double thisActiveTotal; Listeners listeners; MonInternals() { listeners=new Listeners(this); } public void reset() { hits = total = sumOfSquares = lastValue = 0.0; firstAccess = lastAccess = 0; min = MIN_DOUBLE; max = MAX_DOUBLE; // added from mon class maxActive = totalActive = 0.0; activityStats.thisActive.setCount(0); // added from frequencydistbase allActiveTotal = primaryActiveTotal = thisActiveTotal = 0; if (range != null) range.reset(); } } jamonapi/src/java/com/jamonapi/Monitor.java0000666000175000017500000003117410672050612021201 0ustar twernertwernerpackage com.jamonapi; /** * Used to interact with monitor objects. I would have preferred to make this an *interface, but didn't do that as jamon 1.0 code would have broken. Live and learn * * Created on December 11, 2005, 10:19 PM */ import java.util.Date; import com.jamonapi.utils.ToArray; // Note this was done as an empty abstract class so a recompile isn't needed // to go to jamon 2.0. I had originally tried to make Monitor an interface. // public abstract class Monitor extends BaseStatsImp implements MonitorInt { public abstract class Monitor implements MonitorInt { /** Used in call to addListener(...). i.e. addListener(Monitor.MAX, ...) */ public static final String VALUE = "value"; public static final String MAX = "max"; public static final String MIN = "min"; public static final String MAXACTIVE = "maxactive"; // Internal data passed from monitor to monitor. MonInternals monData; Monitor(MonInternals monData) { this.monData = monData; } Monitor() { this(new MonInternals()); } final MonInternals getMonInternals() { return monData; } public MonKey getMonKey() { return monData.key; } /** Returns the label for the monitor */ public String getLabel() { return (String) getMonKey().getValue(MonKey.LABEL_HEADER); } /** Returns the units for the monitor */ public String getUnits() { return (String) getMonKey().getValue(MonKey.UNITS_HEADER); } public void setAccessStats(long now) { if (monData.enabled) { synchronized (monData) { // set the first and last access times. if (monData.firstAccess == 0) monData.firstAccess = now; monData.lastAccess = now; } } } public void reset() { if (monData.enabled) { synchronized (monData) { monData.reset(); } } } public double getTotal() { if (monData.enabled) { synchronized (monData) { return monData.total; } } else return 0; } public void setTotal(double value) { if (monData.enabled) { synchronized (monData) { monData.total = value; } } } public double getAvg() { if (monData.enabled) return avg(monData.total); else return 0; } public double getMin() { if (monData.enabled) { synchronized (monData) { return monData.min; } } else return 0; } public void setMin(double value) { if (monData.enabled) { synchronized (monData) { monData.min = value; } } } public double getMax() { if (monData.enabled) { synchronized (monData) { return monData.max; } } else return 0; } public void setMax(double value) { if (monData.enabled) { synchronized (monData) { monData.max = value; } } } public double getHits() { if (monData.enabled) { synchronized (monData) { return monData.hits; } } else return 0; } public void setHits(double value) { if (monData.enabled) { synchronized (monData) { monData.hits = value; } } } public double getStdDev() { if (monData.enabled) { synchronized (monData) { double stdDeviation = 0; if (monData.hits != 0) { double sumOfX = monData.total; double n = monData.hits; double nMinus1 = (n <= 1) ? 1 : n - 1; // avoid 0 divides; double numerator = monData.sumOfSquares - ((sumOfX * sumOfX) / n); stdDeviation = java.lang.Math.sqrt(numerator / nMinus1); } return stdDeviation; } } else return 0; } public void setFirstAccess(Date date) { if (monData.enabled) { synchronized (monData) { monData.firstAccess = date.getTime(); } } } private static final Date NULL_DATE = new Date(0); public Date getFirstAccess() { if (monData.enabled) { synchronized (monData) { return new Date(monData.firstAccess); } } else return NULL_DATE; } public void setLastAccess(Date date) { if (monData.enabled) { synchronized (monData) { monData.lastAccess = date.getTime(); } } } public Date getLastAccess() { if (monData.enabled) { synchronized (monData) { return new Date(monData.lastAccess); } } else return NULL_DATE; } public double getLastValue() { if (monData.enabled) { synchronized (monData) { return monData.lastValue; } } else return 0; } public void setLastValue(double value) { if (monData.enabled) { synchronized (monData) { monData.lastValue = value; } } } // new methods. not sure about them public void disable() { monData.enabled = false; } public void enable() { monData.enabled = true; } public boolean isEnabled() { return monData.enabled; } Listeners getListeners() { return monData.listeners; } /** new jamon 2.4 stuff */ /** * Add a listener that receives notification every time this monitors add * method is called. If null is passed all associated Listeners will be * detached. */ // Some jamon 2.4 introduced methods. Mostly listener related. public ListenerType getListenerType(String listenerType) { return getListeners().getListenerType(listenerType); } public Monitor start() { if (monData.enabled) { synchronized (monData) { monData.activityStats.allActive.increment(); if (monData.isPrimary) { monData.activityStats.primaryActive.increment(); } // tracking current active/avg active/max active for this // instance double active = monData.activityStats.thisActive .incrementAndReturn(); monData.totalActive += active;// allows us to track the // average active for THIS // instance. if (active >= monData.maxActive) { monData.maxActive = active; if (monData.listeners.listenerArray[Listeners.MAXACTIVE_LISTENER_INDEX].listener!=null && active>1) monData.listeners.listenerArray[Listeners.MAXACTIVE_LISTENER_INDEX].listener.processEvent(this); } // The only way activity tracking need be done is if start has // been entered. if (!monData.startHasBeenCalled) { monData.startHasBeenCalled = true; if (monData.range != null) monData.range.setActivityTracking(true); } } // end synchronized } // end enabled return this; } public Monitor stop() { if (monData.enabled) { synchronized (monData) { monData.activityStats.thisActive.decrement(); if (monData.isPrimary) { monData.activityStats.primaryActive.decrement(); } monData.activityStats.allActive.decrement(); } } return this; } public Monitor add(double value) { if (monData.enabled) { synchronized (monData) { // Being as TimeMonitors already have the current time and are // passing it in // the value (casted as long) for last access need not be // recalculated. // Using this admittedly ugly approach saved about 20% // performance // overhead on timing monitors. if (!monData.isTimeMonitor) setAccessStats(System.currentTimeMillis()); // most recent value monData.lastValue = value; // calculate hits i.e. n monData.hits++; // calculate total i.e. sumofX's monData.total += value; // used in std deviation monData.sumOfSquares += value * value; // tracking activity is only done if start was called on the // monitor // there is no need to synchronize and perform activity tracking // if this // monitor doesn't have a start and stop called. if (monData.trackActivity) { // total of this monitors active monData.thisActiveTotal += monData.activityStats.thisActive.getCount(); // total of primary actives monData.primaryActiveTotal += monData.activityStats.primaryActive.getCount(); // total of all monitors actives monData.allActiveTotal += monData.activityStats.allActive.getCount(); } // calculate min. note saving min if it is a tie // so listener will be called and checking to see if the new // min was less than the current min seemed to cost // more. Same for max below if (value <= monData.min) { monData.min = value; if (monData.listeners.listenerArray[Listeners.MIN_LISTENER_INDEX].listener!=null) monData.listeners.listenerArray[Listeners.MIN_LISTENER_INDEX].listener.processEvent(this); } // calculate max if (value >= monData.max) { monData.max = value; if (monData.listeners.listenerArray[Listeners.MAX_LISTENER_INDEX].listener!=null) monData.listeners.listenerArray[Listeners.MAX_LISTENER_INDEX].listener.processEvent(this); } if (monData.listeners.listenerArray[Listeners.VALUE_LISTENER_INDEX].listener!=null) monData.listeners.listenerArray[Listeners.VALUE_LISTENER_INDEX].listener.processEvent(this); if (monData.range != null) monData.range.processEvent(this); } } return this; } public Range getRange() { return monData.range; } public double getActive() { if (monData.enabled) { synchronized (monData) { return monData.activityStats.thisActive.getCount(); } } else return 0; } public void setActive(double value) { if (monData.enabled) { synchronized (monData) { monData.activityStats.thisActive.setCount(value); } } } public double getMaxActive() { if (monData.enabled) { synchronized (monData) { return monData.maxActive; } } else return 0; } public void setMaxActive(double value) { if (monData.enabled) { synchronized (monData) { monData.maxActive = value; } } } /** Neeed to reset this to 0.0 to remove avg active numbers */ public void setTotalActive(double value) { if (monData.enabled) { synchronized (monData) { monData.totalActive = value; } } } public boolean isPrimary() { return monData.isPrimary; } public void setPrimary(boolean isPrimary) { if (monData.enabled) { this.monData.isPrimary = isPrimary; } } public boolean hasListeners() { synchronized (monData) { return monData.listeners.hasListeners(); } } public String toString() { if (monData.enabled) { // This character string is about 275 characters now, but made // the default a little bigger, so the JVM doesn't have to grow // the StringBuffer should I add more info. StringBuffer b = new StringBuffer(400); b.append(getMonKey() + ": ("); b.append("LastValue="); b.append(getLastValue()); b.append(", Hits="); b.append(getHits()); b.append(", Avg="); b.append(getAvg()); b.append(", Total="); b.append(getTotal()); b.append(", Min="); b.append(getMin()); b.append(", Max="); b.append(getMax()); b.append(", Active="); b.append(getActive()); b.append(", Avg Active="); b.append(getAvgActive()); b.append(", Max Active="); b.append(getMaxActive()); b.append(", First Access="); b.append(getFirstAccess()); b.append(", Last Access="); b.append(getLastAccess()); b.append(")"); return b.toString(); } else return ""; } /** FROM frequencydistimp */ public void setActivityTracking(boolean trackActivity) { this.monData.trackActivity = trackActivity; } public boolean isActivityTracking() { return monData.trackActivity; } private double avg(double value) { synchronized (monData) { if (monData.hits == 0) return 0; else return value / monData.hits; } } public double getAvgActive() { if (monData.enabled) { // can be two ways to get active. For ranges // thisActiveTotal is used and for nonranges // totalActive is used. if (monData.trackActivity) { return avg(monData.thisActiveTotal); } else return avg(monData.totalActive); } else return 0; } public double getAvgGlobalActive() { return avg(monData.allActiveTotal); } public double getAvgPrimaryActive() { return avg(monData.primaryActiveTotal); } public JAMonDetailValue getJAMonDetailRow() { if (monData.enabled) { synchronized (monData) { return new JAMonDetailValue(getMonKey(), monData.lastValue, monData.activityStats.thisActive.getCount(), monData.lastAccess); // return new JAMonDetailValue(getMonKey().getDetailLabel(), // monData.lastValue, monData.activityStats.thisActive.getCount(), monData.lastAccess); } } else return JAMonDetailValue.NULL_VALUE; } } jamonapi/src/java/com/jamonapi/MonitorComposite.java0000666000175000017500000003736510672050612023074 0ustar twernertwerner/** * Treats groups of monitors the same way you treat one monitor. i.e. you can enable/disable/reset * etc a group of monitors. */ package com.jamonapi; import java.util.*; import com.jamonapi.utils.*; public class MonitorComposite extends Monitor implements DetailData { private final Monitor[] monitors;// the monitors in the composite private final int numRows; // rows in the composite private final static int TYPICAL_NUM_CHILDREN=200;// hopefully makes it so the monitor need not grow all the time /** Creates a new instance of MonitorComposite */ public MonitorComposite(Monitor[] monitors) { this.monitors=monitors; numRows=(monitors==null) ? 0 : monitors.length; } MonitorComposite() { this(null); } public Monitor[] getMonitors() { return monitors; } /** Pass in an array with col1=lables, and col2=units and then call methods */ public static MonitorComposite getMonitors(String[][] labels) { int numRows=(labels==null) ? 0 : labels.length; int numCols=(labels[0]==null) ? 0 : labels[0].length; Monitor[] monArray=new Monitor[numRows]; for (int i=0; i\n"); for (int i=0;i"+header[i]+""); html.append(""+header[0]+"");//repeat first header html.append("\n"); for (int i=0;i"); for (int j=0;j"+data[i][j]+""); } html.append(""+data[i][0]+"");// repeat first column html.append("\n"); } html.append(""); return html.toString(); } /** Does this have data? */ public boolean hasData() { return (getNumRows()==0) ? false : true; } // Various get row data methods (for full row, basic row, and display row private Object[] getRowData(MonitorImp mon) { List row=new ArrayList(TYPICAL_NUM_CHILDREN); mon.getRowData(row); return row.toArray(); } private Object[] getBasicRowData(MonitorImp mon) { List row=new ArrayList(); mon.getBasicRowData(row); return row.toArray(); } private Object[] getRowDisplayData(MonitorImp mon) { List row=new ArrayList(TYPICAL_NUM_CHILDREN); mon.getRowDisplayData(row); return row.toArray(); } public void reset() { for (int i=0;i0) // thisDate>lastAccess lastAccess=thisDate; } return lastAccess; } public double getLastValue() { Date date=getLastAccess(); for (int i=0;i=0) // date>=getLastAccess) return monitors[i].getLastValue(); } return 0; } public double getMax() { double max=MonInternals.MAX_DOUBLE; for (int i=0;imax) max=thisMax; } return max; } public double getMaxActive() { double max=MonInternals.MAX_DOUBLE; for (int i=0;imax) max=thisMax; } return max; } public double getMin() { double min=MonInternals.MIN_DOUBLE; for (int i=0;iAggregate the passed in value with the monitor associated with the label, and the units. The aggregation tracks * hits, avg, total, min, max and more. Note the monitor returned is threadsafe. However, it is best to get a monitor * vi this method and not reuse the handle as TimeMonitors are not thread safe (see the getTimeMonitor method.

* * Sample call:
*
	 * Monitor mon=MonitorFactory.add("bytes.sent","MB", 1024);
	 *


* */ public static Monitor add(String label, String units, double value) { return factory.add(label, units, value); } /** Used when you want to create your own key for the monitor. This works similarly to a group by clause where the key is * any columns used after the group by clause. */ public static Monitor add(MonKey key, double value) { return factory.add(key, value); } /**

Return a timing monitor with units in milliseconds. stop() should be called on the returned monitor to indicate the time * that the process took. Note time monitors keep the starttime as an instance variable and so every time you want to use a TimeMonitor * you should get a new instance.

* * Sample call:
*
	 *  Monitor mon=MonitorFactory.start("pageHits");
...code being timed...
mon.stop(); * *


* */ public static Monitor start(String label) { return factory.start(label); } /**

Return a timing monitor with units in milliseconds, that is not aggregated into the jamon stats. stop() should be called on the returned monitor to indicate the time * that the process took. Note time monitors keep the starttime as an instance variable and so every time you want to use a TimeMonitor * you should get a new instance.

* * Sample call:
*
	 *  Monitor mon=MonitorFactory.start();
...code being timed...
mon.stop(); * *


* */ public static Monitor start() { return factory.start(); } public static Monitor getMonitor() { return factory.getMonitor(); } /**

Return a timing monitor with units in milliseconds, that is not aggregated into the jamon stats. The concept of primary allows you to correlate performance of all monitors with the most resource intensive things the app does which helps you determine scalability.

* * Sample call:
*
	 *  Monitor mon=MonitorFactory.startPrimary("myPrimaryMonitor");
...code being timed...
mon.stop(); * *


* */ public static Monitor startPrimary(String label) { return factory.startPrimary(label); } /** Start a monitor with the specified key and mark it as primary */ public static Monitor startPrimary(MonKey key) { return factory.startPrimary(key); } /** Start using the passed in key. Note activity stats are incremented */ public static Monitor start(MonKey key) { return factory.start(key); } /**

Return the monitor associated with the label, and units. All statistics associated with the monitor can then be accessed such as hits, total, avg, min, and max. If the monitor does not exist it will be created.

* * Sample call:
*
	 *  Monitor mon=MonitorFactory.getMonitor("myPrimaryMonitor");
* *


* */ public static Monitor getMonitor(String label, String units) { return factory.getMonitor(label, units); } /** Get the monitor associated with the passed in key. It will be created if it doesn't exist */ public static Monitor getMonitor(MonKey key) { return factory.getMonitor(key); } /**

Return the time monitor associated with the label. All statistics associated with the monitor can then be accessed such as hits, total, avg, min, and max. If the monitor does not exist it will be created.

* * Sample call:
*
	 *  Monitor mon=MonitorFactory.getTimeMonitor("myPrimaryMonitor");
* *


* */ public static Monitor getTimeMonitor(String label) { return factory.getTimeMonitor(label); } /** Get the time monitor associated with the passed in key. It will be created if it doesn't exist. The units * are in ms.*/ public static Monitor getTimeMonitor(MonKey key) { return factory.getTimeMonitor(key); } /**

Determine if the monitor associated with the label, and the units currently exists.

* * Sample call:
*
	 *  Monitor mon=MonitorFactory.getTimeMonitor("myPrimaryMonitor");
* *


* */ public static boolean exists(String label, String units) { return factory.exists(label, units); } /** Return true if the monitor associated with the passed in key exists */ public static boolean exists(MonKey key) { return factory.exists(key); } /**

Return the composite monitor (a collection of monitors) associated with the passed in units. Note in JAMon 1.0 this * method would take a lable and would return all monitors that matched that criterion. This ability is now better performed * using ArraySQL from the FormattedDataSet API. See JAMonAdmin.jsp for an example.

* * Sample call:
*
	 *  Monitor mon=MonitorFactory.getComposite("ms.");
* mon=MonitorFactory.getComposite("allMonitors");
* *


* */ public static MonitorComposite getComposite(String units) { return factory.getComposite(units); } /** * This returns the number of monitors in this factory. */ public static int getNumRows() { return factory.getNumRows(); } /** * Return the header for displaying what ranges are available. * */ public static String[] getRangeHeader() { return factory.getRangeHeader(); } /** * Return the ranges in this factory. * */ public static Object[][] getRangeNames() { return factory.getRangeNames(); } /** Return the composite monitor of all monitors for this factory */ public static MonitorComposite getRootMonitor() { return factory.getRootMonitor(); } /** Return the version of JAMon */ public static String getVersion() { return factory.getVersion(); } /** Remove/delete the specified monitor */ public static void remove(String label, String units) { factory.remove(label, units); } /** Remove the monitor associated with the passed in key */ public static void remove(MonKey key) { factory.remove(key); } /** * Use the specified map to hold the monitors. This map should be * threadsafe. This allows for the use of a faster map than * * the default synchronzied HashMap() * */ public static void setMap(Map map) { factory.setMap(map); } /** * Associate a range with a key/unit. Any monitor with the given unit will * have this range. Any monitor with * * no range associated with its unit will have no range. * */ public static void setRangeDefault(String key, RangeHolder rangeHolder) { factory.setRangeDefault(key, rangeHolder); } /** * Enable/Disable MonitorFactory. When enabled (true) the factory returns * monitors that store aggregate stats. When disabled (false) * * null/noop monitors are returned. enable()/disable() can also be used to * perform the same function * * * */ public static void setEnabled(boolean enable) { if (enable) factory = enabledFactory; else factory = disabledFactory; } /** * * Enable or disable the debug factory. The debug factory can be * enabled/disabled at runtime. Calling this method with a false * * also disables calls to MonitorFactory.getDebugFactory(int * debugPriorityLevel) * * * * Sample Call: * * MonitorFactory.setDebugEnabled(false); * * MonitorFactory.getDebugFactory().start(); // no stats are gathered. * */ public static void setDebugEnabled(boolean enable) { if (enable) debugFactory = enabledFactory; else debugFactory = disabledFactory; } /** * Enable MonitorFactory. When enabled the factory returns monitors that * store aggregate stats. This method has the same * * effect as calling MonitorFactor.setEnabled(true). * */ public static void enable() { setEnabled(true); } /** * Disable MonitorFactory. When disabled the factory returns null/noop * monitors. This method has the same * * effect as calling MonitorFactor.setEnabled(true). * */ public static void disable() { setEnabled(false); } /** * Is the MonitorFactory currently enabled? * */ public static boolean isEnabled() { return (factory == enabledFactory) ? true : false; } /** * Is the Debug Monitor Factory currently enabled? * */ public static boolean isDebugEnabled() { return (debugFactory == enabledFactory) ? true : false; } public static boolean isGlobalActiveEnabled() { return factory.isGlobalActiveEnabled(); } public static void enableGlobalActive(boolean enable) { factory.enableGlobalActive(enable); } /** * Reset/remove all monitors. If the factory is disabled this method has no * action. */ public static void reset() { if (isEnabled()) init(); } private static void init() { // enable the factory by default. factory = debugFactory = enabledFactory = new FactoryEnabled(); disabledFactory = new FactoryDisabled(enabledFactory); } /** * This returns the header for basic data with no range info in the header. * This method is deprecated. use the methods associated * * with the CompositeMonitor. The various getXXXHeader() methods of * CompositeMonitors can return this information and more. * * * * */ public static String[] getHeader() { return factory.getRootMonitor().getBasicHeader(); } /** * This returns the data for basic data with no range info. * * The various getXXXData() methods of CompositeMonitors can return this * information and more. * * * * * */ public static Object[][] getData() { return factory.getRootMonitor().getBasicData(); } /** * This returns an HTML report for basic data with no range info in the * header. * */ public static String getReport() { return factory.getRootMonitor().getReport(); } /** * This returns an HTML report for basic data with no range info in the * header for the past in units. * * This method will be removed in the next release. */ public static String getReport(String units) { return getComposite(units).getReport(); } /** Iterator that contains Monitor's that are in this factory */ public Iterator iterator() { return factory.iterator(); } /** return a test range holder */ private static RangeHolder getTestHolder() { RangeHolder rh = new RangeHolder(); rh.add("10_display", 10); rh.add("20_display", 20); rh.add("30_display", 30); rh.add("40_display", 40); rh.add("50_display", 50); rh.add("60_display", 60); rh.add("70_display", 70); rh.add("80_display", 80); rh.add("90_display", 90); rh.add("100_display", 100); rh.add("110_display", 110); rh.add("120_display", 120); rh.add("130_display", 130); rh.add("140_display", 140); rh.add("150_display", 150); // note last range is always called lastRange and is added automatically return rh; } public static void main(String[] args) { Monitor timer = MonitorFactory.start("totaltime"); for (int i = 1; i <= 10; i++) { System.out.println(MonitorFactory.add("NIC.bytes.sent", "bytes", i * 1000)); System.out.println(MonitorFactory.add("negativetest", "neg", -1000.0 * i)); } System.out.println(""); Monitor m = null; m = MonitorFactory.start("purchasesTimeTestNoRange"); for (int i = 1; i <= 1000000; i++) MonitorFactory.add("purchasesNoRange", "dollars", 1000.0); System.out.println("purchasesTimeTestNoRange=" + m.stop().getTotal()); m = MonitorFactory.start("testTimerTimeTest"); for (int i = 1; i <= 1000000; i++) MonitorFactory.start("testTimer").stop(); System.out.println("testTimerTimeTest=" + m.stop().getTotal()); for (int i = -5; i <= 20; i++) { MonitorFactory.add("purchases", "dollars", i * 50); } System.out.println(""); System.out.println(MonitorFactory.add("NIC.bytes.received", "bytes", 250.0)); System.out.println(MonitorFactory.add("NIC.bytes.received", "bytes", 250.0)); timer.stop(); for (int i = -5; i < 25; i++) MonitorFactory.add("timetest", "ms.", i * 5); System.out.println(MonitorFactory.getMonitor("purchases", "dollars").getHits()); System.out.println(MonitorFactory.getTimeMonitor("testTimer").getHits()); // case sensitive so won't print System.out.println(MonitorFactory.getTimeMonitor("testtimer").getHits()); System.out.println("Total time=" + timer.getTotal()); MonitorFactory.reset(); MonitorFactory.setRangeDefault("dollars", getTestHolder()); m = MonitorFactory.start("purchasesTimeTestRange"); for (int i = 1; i <= 1000000; i++) MonitorFactory.add("purchasesRange", "dollars", 1000.0); System.out.println("purchasesTimeTestRange=" + m.stop().getTotal()); Object[][] data = null; MonitorFactory.setRangeDefault("bytes", getTestHolder()); MonitorFactory.setRangeDefault("cents", getTestHolder()); MonitorFactory.setRangeDefault("minutes", getTestHolder()); MonitorFactory.setRangeDefault("MB", getTestHolder()); MonitorFactory.setRangeDefault("KB", getTestHolder()); MonitorFactory.setRangeDefault("points", getTestHolder()); String[] header = MonitorFactory.getComposite("ms.").getHeader(); data = MonitorFactory.getComposite("ms.").getData(); header = MonitorFactory.getComposite("ms.").getBasicHeader(); data = MonitorFactory.getComposite("ms.").getBasicData(); header = MonitorFactory.getComposite("ms.").getDisplayHeader(); data = MonitorFactory.getComposite("ms.").getDisplayData(); MonitorFactory.getComposite("ms.").disable(); header = MonitorFactory.getComposite("ms.").getHeader(); data = MonitorFactory.getComposite("ms.").getData(); System.out.println("header length="+header.length+", data length="+data.length); System.out.println("JAMon Version=" + MonitorFactory.getVersion()); } } jamonapi/src/java/com/jamonapi/MonitorFactoryInterface.java0000666000175000017500000001103410671013542024343 0ustar twernertwernerpackage com.jamonapi; /** * Interface used to create Monitors. It is implemented by both FactoryEnabled and FactoryDisabled * which allows for enabling/disabling monitors at run time. A * Factory is a design concept that is described in the Gang of 4's design patterns. * * Note the factory will create a monitor if it doesn't exist and use an existing one if it * does. * Created on January 29, 2006, 10:31 PM */ import java.util.*; public interface MonitorFactoryInterface { /** Return a monitor with the given label and units. Note label has an effect on what range is used. If no range is * associated with units then it will use the null range (i.e. no range) * * Sample Call: factory.add("com.fdsapi.MyException", "error", 1); */ public Monitor add(String label, String units, double value); /** Used when you want to create your own key for the monitor. This works similarly to a group by clause where the key is * any columns used after the group by clause. */ public Monitor add(MonKey key, double value); /** Return a time monitor (the units are implied and are ms. Note activity stats are incremented*/ public Monitor start(String label); /** Start using the passed in key. Note activity stats are incremented */ public Monitor start(MonKey key); /** Returns a TimeMonitor that won't update the jamon factory. */ public Monitor start(); /** Returns a non-TimeMonitor that won't update the jamon factory. */ public Monitor getMonitor(); /** Start a time monitor and mark it as primary */ public Monitor startPrimary(String label); /** Start a monitor with the specified key and mark it as primary */ public Monitor startPrimary(MonKey key); /** Get the monitor associated with the passed in key. It will be created if it doesn't exist */ public Monitor getMonitor(MonKey key); /** Get the monitor with the passed in label, and units. It will be created if it doesn't exist */ public Monitor getMonitor(String label, String units); /** Get the time monitor associated with the passed in label. It will be created if it doesn't exist. The units * are in ms.*/ public Monitor getTimeMonitor(String label); /** Get the time monitor associated with the passed in key. It will be created if it doesn't exist. The units * are in ms.*/ public Monitor getTimeMonitor(MonKey key); /** Remove the monitor associated with the passed in label and units */ public void remove(String label, String units); /** Remove the monitor associated with the passed in key */ public void remove(MonKey key); /** Return true if the monitor associated with the passed in label and units exists */ public boolean exists (String label, String units); /** Return true if the monitor associated with the passed in key exists */ public boolean exists(MonKey key); /** Associate a Range mapping to any monitor that has a unit/key name that matches what is passed to key */ public void setRangeDefault(String key, RangeHolder rangeHolder); /** Return the header associated with range names */ public String[] getRangeHeader(); /** Retun an array of range names. This is dynamic based on what was passed to setRangeDefault */ public Object[][] getRangeNames(); /** Get the number of monitors in this factory */ public int getNumRows(); /** Get the root composite monitor that contains all monitors in this factory */ public MonitorComposite getRootMonitor(); /* Retun the composite monitor associated with the passed unit. Note this method changed from jamon 1.0. * Previously it took a regular expression that * was matched on the label column and now it looks for any monitors that have the given range/unit key */ public MonitorComposite getComposite(String units); /** Get JAMon's version. Example: 2.0 */ public String getVersion(); /** Set the map that holds the monitors. This could be used to aid jamon performance by passing in a high performance * Thread safe map such as open source projects and jdk 1.5 have */ public void setMap(Map map); /** Reset jamon stats for this factory. Like recreating the factory */ public void reset(); public boolean isGlobalActiveEnabled(); public void enableGlobalActive(boolean enable); public Iterator iterator(); } jamonapi/src/java/com/jamonapi/MonitorImp.java0000666000175000017500000001140510672050612021642 0ustar twernertwernerpackage com.jamonapi; /** * Main workhorse for monitors. For display gets info from key, monitor, and range. * * Created on January 21, 2006, 6:08 PM */ import java.util.List; class MonitorImp extends Monitor implements RowData { MonitorImp(MonInternals monData) { super(monData); } private static final MonKey NULL_MON_KEY=new NullMonKey(); MonitorImp() { this(NULL_MON_KEY,null,null,false); } // inherited by FrequencyDist MonitorImp(MonKey key, RangeImp range, ActivityStats activityStats, boolean isTimeMonitor) { this.monData.key = key; this.monData.range = range; this.monData.activityStats = activityStats; this.monData.isTimeMonitor = isTimeMonitor; } public List getBasicHeader(List header) { monData.key.getBasicHeader(header); getThisData(header); return header; } private List getThisData(List header) { header.add(monData.name+"Hits"); header.add(monData.name+"Avg"); header.add(monData.name+"Total"); header.add(monData.name+"StdDev"); header.add(monData.name+"LastValue"); header.add(monData.name+"Min"); header.add(monData.name+"Max"); header.add(monData.name+"Active"); header.add(monData.name+"AvgActive"); header.add(monData.name+"MaxActive"); header.add(monData.name+"FirstAccess"); header.add(monData.name+"LastAccess"); header.add(monData.name+"Enabled"); header.add(monData.name+"Primary"); header.add(monData.name+"HasListeners"); return header; } public List getHeader(List header) { monData.key.getHeader(header); getThisData(header); if (monData.range!=null) monData.range.getHeader(header); return header; } public List getDisplayHeader(List header) { monData.key.getDisplayHeader(header); getThisData(header); if (monData.range!=null) monData.range.getDisplayHeader(header); return header; } public List getBasicRowData(List rowData) { monData.key.getBasicRowData(rowData); return getThisRowData(rowData); } private List getThisRowData(List rowData) { rowData.add(new Double(getHits())); rowData.add(new Double(getAvg())); rowData.add(new Double(getTotal())); rowData.add(new Double(getStdDev())); rowData.add(new Double(getLastValue())); rowData.add(new Double(getMin())); rowData.add(new Double(getMax())); rowData.add(new Double(getActive())); rowData.add(new Double(getAvgActive())); rowData.add(new Double(getMaxActive())); rowData.add(getFirstAccess()); rowData.add(getLastAccess()); rowData.add(new Boolean(isEnabled())); rowData.add(new Boolean(isPrimary())); rowData.add(new Boolean(hasListeners())); return rowData; } public List getRowData(List rowData) { monData.key.getRowData(rowData); getThisRowData(rowData); if (monData.range!=null) monData.range.getRowData(rowData); return rowData; } public List getRowDisplayData(List rowData) { monData.key.getRowDisplayData(rowData); getThisRowData(rowData); if (monData.range!=null) monData.range.getRowDisplayData(rowData); return rowData; } public RangeHolder getRangeHolder() { RangeImp r=(RangeImp) getRange(); if (r!=null) return r.getRangeHolder(); else return null; } void setActivityStats(ActivityStats activityStats) { this.monData.activityStats=activityStats; } private static class NullMonKey implements MonKey { public Object getDetails() { return ""; } public String getRangeKey() { return ""; } public Object getValue(String primaryKey) { return ""; } public List getBasicHeader(List header) { return header; } public List getBasicRowData(List rowData) { return rowData; } public List getDisplayHeader(List header) { return header; } public List getHeader(List header) { return header; } public List getRowData(List rowData) { return rowData; } public List getRowDisplayData(List rowData) { return rowData; } public void setDetails(Object details) { } } } jamonapi/src/java/com/jamonapi/MonitorInt.java0000666000175000017500000000517210672050612021653 0ustar twernertwernerpackage com.jamonapi; import java.util.Date; import com.jamonapi.utils.ToArray; /** * MonitorInt.java * * Created on February 2, 2006, 7:54 PM */ interface MonitorInt { public double getTotal(); public void setTotal(double value); public double getAvg(); public double getMin(); public void setMin(double value); public double getMax(); public void setMax(double value); public double getHits(); public void setHits(double value); public double getStdDev(); public void setFirstAccess(Date date); public Date getFirstAccess(); public void setLastAccess(Date date); public Date getLastAccess(); public double getLastValue(); public void setLastValue(double value); /** Start a monitor. This increments the active counter by one. Calling start is not required. If it is called stop should be called too. */ public Monitor start(); /** Stop a monitor. The decrements the active counter by one. Calling stop is * required if start is called. */ public Monitor stop(); /** This method adds a value to the monitor (and aggegates statistics on it) */ public Monitor add(double value); /** reset all values in the monitor to their defaults */ public void reset(); /** enable the monitor. If the monitor is enabled all other calls to the monitor * have an action */ public void enable(); /** Disable the monitor. If a monitor is disabled all other calls to the monitor * are noops. **/ public void disable(); /** Is the monitor enabled. */ public boolean isEnabled(); /** Return the Range object associated with this monitor. The range object is a compromise between saving all data or none */ public Range getRange(); /** Return the label associated with this monitor. */ public MonKey getMonKey(); public double getActive(); public void setActive(double value); public double getMaxActive(); public void setMaxActive(double value); public void setTotalActive(double value); public double getAvgActive(); public boolean isPrimary(); /** Indicate that this a primary Monitor. See www.jamonapi.com for an explanation of primary monitors **/ public void setPrimary(boolean isPrimary); // Some jamon 2.4 introduced methods. Mostly listener related. public ListenerType getListenerType(String listenerType); public boolean hasListeners(); public void setActivityTracking(boolean trackActivity); public boolean isActivityTracking(); public JAMonDetailValue getJAMonDetailRow(); } jamonapi/src/java/com/jamonapi/MonKey.java0000666000175000017500000000156210620427352020754 0ustar twernertwernerpackage com.jamonapi; /** key that allows for a monitor to be passed any number of keys used in the equivalent * of a group by clause. Put in hashmap to identify a Monitor. Implementations will need * to implement equals, and hashcode. MonKeys are the way Monitors are identified in the * storing Map */ public interface MonKey extends RowData, MonKeyItem { final String LABEL_HEADER="Label"; final String UNITS_HEADER="Units"; /** return any value associated with the key. * new MonKey(label, units). would return the value associated with label * or units if: getValue("label"), or getValue("units"); */ public Object getValue(String primaryKey); /** Uses this value to look up an associated Range */ public String getRangeKey(); // public String getDetailLabel(); } jamonapi/src/java/com/jamonapi/MonKeyBase.java0000666000175000017500000001666210672050612021554 0ustar twernertwernerpackage com.jamonapi; import java.util.*; /** Class that can be used as a composite key for MonitorFactor.add(compositeKey, 100) method calls * Note the passed in LinkedHashMap is used as a key to another Map that looks up the associated monitor. * Sun java docs says this regarding keys in Maps: "Note: great care must be exercised if mutable objects * are used as map keys. The behavior of a map is not specified if the value of an * object is changed in a manner that affects equals comparisons while the object is a key in the map." * So once a value is placed in the LinkedHashMap is passed to the MonKeyBase * constructor it should not be changed. * */ public class MonKeyBase implements MonKey { /** * A key implmentation for label, and units type monitors. */ private Map keyMap; private String rangeKeyStr; private Object details; /** Calls the other constructor. The keyMap will be used to create the range name */ public MonKeyBase(LinkedHashMap keyMap) { this(null, keyMap); } /** The LinkHashMap will contain key value pairs. For example: fn=steve, ln=souza, age=44. LinkHashMap is used * as the insertion order needs to be retained for displaying header, and data. The rangeKeyString is used * to look up an associated range should one exist */ public MonKeyBase(String rangeKeyStr, LinkedHashMap keyMap) { this.rangeKeyStr=rangeKeyStr; this.keyMap = (keyMap == null) ? new LinkedHashMap() : keyMap; } /** * Returns any object that has a named key. In this keys case 'label' and * 'units' makes sense, but any values are acceptible. */ public Object getValue(String key) { if ("details".equalsIgnoreCase(key)) return getDetails(); else return keyMap.get(key); } /** * This method is called automatically by a HashMap when this class is used * as a HashMap key. A Coordinate is considered equal if its x and y * variables have the same value. */ public boolean equals(Object compareKey) { if (this==compareKey) return true; else if (compareKey instanceof MonKeyBase) { MonKeyBase key=(MonKeyBase) compareKey; return keyMap.equals(key.getMonKeyMap()); } else return false; } /** Used when key is put into a Map to look up the monitor */ public int hashCode() { return keyMap.hashCode(); } /** Return the map underlying this Object */ public Map getMonKeyMap() { return keyMap; } /** Puts the word 'Label' into the list. Used in the basic JAMon report */ public List getBasicHeader(List header) { header.add(LABEL_HEADER); return header; } /** Returns each key in the map as a header element in the list */ public List getDisplayHeader(List header) { return getHeader(header); } /** Returns each key in the map as a header element in the list */ public List getHeader(List header) { Iterator iter=keyMap.keySet().iterator(); while(iter.hasNext()) { header.add(iter.next()); } return header; } /** Returns the values assoicated with the key as a comma delimited entry in the list. * For example if the map contains firstname, lastname, age then something like * steve, souza, 44 would be returned */ public List getBasicRowData(List rowData) { Collection row=keyMap.values(); int currentElement=1; int lastElement=row.size(); Iterator iter=row.iterator(); StringBuffer buff=new StringBuffer(); // loop through elements creating a comma delimeted list of the values while(iter.hasNext()) { buff.append(iter.next()); if (currentElement!=lastElement) buff.append(", "); currentElement++; } if (buff.length()>0) rowData.add(buff.toString()); return rowData; } /** Add each value from the map at an element to the list. */ public List getRowData(List rowData) { Collection row=keyMap.values(); Iterator iter=row.iterator(); // loop through elements creating a comma delimeted list of the values while(iter.hasNext()) { rowData.add(iter.next()); } return rowData; } /** Add each value from the map at an element to the list. */ public List getRowDisplayData(List rowData) { return getRowData(rowData); } /** Returns a string representation of this object: JAMon Key, firstname=steve, lastname=souza, age=44*/ public String toString() { Iterator iter=keyMap.entrySet().iterator(); StringBuffer buff=new StringBuffer("JAMon Key"); while(iter.hasNext()) { Map.Entry entry = (Map.Entry) iter.next(); buff.append(", ").append(entry.getKey()).append("=").append(entry.getValue()); } return buff.toString(); } /** Returns either the passed in range key, or it builds the key from the maps keys concatenated */ public String getRangeKey() { if (rangeKeyStr==null) { StringBuffer buff=new StringBuffer(); Iterator iter=keyMap.keySet().iterator(); while(iter.hasNext()) buff.append(iter.next()).append(" "); rangeKeyStr=buff.toString(); } return rangeKeyStr; } public Object getDetails() { if (details==null) { List list=new ArrayList(); Iterator iter=keyMap.entrySet().iterator(); //StringBuffer buff=new StringBuffer(); while(iter.hasNext()) { Map.Entry entry = (Map.Entry) iter.next(); Object value=entry.getValue(); if (value instanceof MonKeyItem) value=((MonKeyItem)value).getDetails(); list.add(value); // buff.append(value).append(", "); } return list; } else return details; } public void setDetails(Object details) { this.details=details; } private void testDisplay() { System.out.println("\n***"); System.out.println("toString()="+this); System.out.println("getValue calls="+getValue("fn")+", "+getValue("ln")+", "+getValue("age")); System.out.println("getRangeKey()="+getRangeKey()); // Header calls System.out.println("\nHeader calls"); System.out.println("getBasicHeader()="+getBasicHeader(new ArrayList())); System.out.println("getHeader()="+getHeader(new ArrayList())); System.out.println("getDisplayHeader()="+getDisplayHeader(new ArrayList())); // Data Calls System.out.println("\nData calls"); System.out.println("getBasicRowData()="+getBasicRowData(new ArrayList())); System.out.println("getRowData()="+getRowData(new ArrayList())); System.out.println("getRowDisplayData()="+getRowDisplayData(new ArrayList())); System.out.println("getDetailLabel()="+getDetails()); } public static void main(String[] arg) { LinkedHashMap steveMap=new LinkedHashMap(); LinkedHashMap mindyMap=new LinkedHashMap(); steveMap.put("fn",new MonKeyItemBase("steve summary","steve detail")); steveMap.put("ln","souza"); steveMap.put("age", new Long(44)); mindyMap.put("fn","mindy"); mindyMap.put("ln","bobish"); mindyMap.put("age", new Long(33)); MonKey steveMonKey=new MonKeyBase("Steves Range", steveMap); MonKey steveMonKey2=new MonKeyBase(steveMap); MonKey mindyMonKey=new MonKeyBase(mindyMap); System.out.println("do 2 steves equal="+steveMonKey.equals(steveMonKey)); System.out.println("do mindy and steve equal="+steveMonKey.equals(mindyMonKey)); ((MonKeyBase)steveMonKey).testDisplay(); ((MonKeyBase)steveMonKey2).testDisplay(); ((MonKeyBase)mindyMonKey).testDisplay(); } } jamonapi/src/java/com/jamonapi/MonKeyImp.java0000666000175000017500000001246310672050612021422 0ustar twernertwernerpackage com.jamonapi; /** *

A key implmentation for label, and units type monitors. * Note this could also be implemented with the following use of MonKeyBase. This * class predates that one and would not have to use a Map for basic functions * and so MAY be more efficient (this wasn't tested). Using label, and units * is the most common monitor that will be used in most cases.

* *

This could be implemented like the following. * LinkedHashMap lm=new LinkedHashMap();
* lm.put("Label", "mypakcage.myclass");
* lm.put(""Units", "ms.");
* MonKey mk=new MonKeyBase(lm);
* *

*/ //import java.util.Collection; import java.util.List; public class MonKeyImp implements MonKey { private final String summaryLabel; // pageHits for example private Object details; // The actual page name for the detail buffer. pageHits for example private final String units; // ms. for example // private boolean initializeDetail=true; private Object param; public MonKeyImp(String summaryLabel, String units) { this(summaryLabel, summaryLabel, units); } /** Object details can be an Object[], a Collection, or a Java Object. */ public MonKeyImp(String summaryLabel, Object details, String units) { this.summaryLabel = (summaryLabel==null) ? "" : summaryLabel; this.details = details; this.units= (units==null) ? "" : units; } public MonKeyImp(MonKeyItem keyItem, String units) { this.summaryLabel = (keyItem==null) ? "" : keyItem.toString();; this.units= (units==null) ? "" : units; this.details=keyItem.getDetails(); } /** Returns the label for the monitor */ public String getLabel() { return summaryLabel; } /** Returns the units for the monitor */ public String getUnits() { return units; } public Object getDetails() { return details; // return details; // if (initializeDetail) { // initializeDetail=false; // detailLabel+=", "+units; // (detailLabel==null) ? "" : detailLabel // } // // return detailLabel; } // // public List getDetails(List list) { // Misc.addTo(list, details); // return list; // } public void setDetails(Object details) { this.details=details; } /** Returns any object that has a named key. In this keys case * 'label' and 'units' makes sense, but any values are acceptible. */ public Object getValue(String key) { if (LABEL_HEADER.equalsIgnoreCase(key)) return getLabel(); else if (UNITS_HEADER.equalsIgnoreCase(key)) return getUnits(); else if ("param".equalsIgnoreCase(key)) return getParam(); else if ("details".equalsIgnoreCase(key)) return getDetails(); else return null; } /** Used to get any arbitrary Object into the key. It will not be used as part of the key, however it can be retrieved later for example * in the JAMonBufferListener. * @return */ public Object getParam() { return param; } /** Used to set any arbitrary Object into the key. It will not be used as part of the key, however it can be retrieved later for example * in the JAMonBufferListener. * @return */ public void setParam(Object param) { this.param=param; } /** This method is called automatically by a HashMap when this class is used as a HashMap key. A Coordinate is considered equal if its x and y variables have the same value. */ public boolean equals(Object compareKey) { return ( compareKey instanceof MonKeyImp && summaryLabel.equals(((MonKeyImp) compareKey).summaryLabel) && units.equals(((MonKeyImp) compareKey).units) ); } /** Used when key is put into a Map to look up the monitor */ public int hashCode() { return (summaryLabel.hashCode() + units.hashCode()); } public List getBasicHeader(List header) { header.add(LABEL_HEADER); return header; } public List getDisplayHeader(List header) { return getHeader(header); } public List getHeader(List header) { header.add(LABEL_HEADER); header.add(UNITS_HEADER); return header; } public List getBasicRowData(List rowData) { rowData.add(getLabel()+", "+getUnits()); return rowData; } public List getRowData(List rowData) { rowData.add(getLabel()); rowData.add(getUnits()); return rowData; } public List getRowDisplayData(List rowData) { return getRowData(rowData); } public String toString() { return new StringBuffer().append("JAMon Label=").append(getLabel()).append(", Units=").append(getUnits()).toString(); } public String getRangeKey() { return getUnits(); } } jamonapi/src/java/com/jamonapi/MonKeyItem.java0000666000175000017500000000130110672050612021560 0ustar twernertwernerpackage com.jamonapi; /** Used for MonKey to allow jamon to have the generalized form of the key for aggregation, and the * more specific form for writing out details say to a buffer. * * For example the generalized form of a query would be something like: select * from table where * name=?. The detail form would be something like: select * from table where name='steve' * * MonKeyItems can be placed as objects in MonKeyImp, and MonKeyBase * * Note toString() should always return the more generalized form. * @author steve souza * */ import java.util.List; public interface MonKeyItem { public Object getDetails(); public void setDetails(Object details); } jamonapi/src/java/com/jamonapi/MonKeyItemBase.java0000666000175000017500000000322710672050612022364 0ustar twernertwernerpackage com.jamonapi; import java.util.List; import com.jamonapi.utils.*; /** Used in MonKeys. Pass a generalized form to the summaryLabel and a specific form to the * detailLabel. (i.e. summary=myproc ?,?, detail=myproc 'steve','souza'. Make sure you * don't pass the arguments in the wrong order as jamon uses the summary label for jamon aggregate * stats, and you don't want every non-generalized form to become a jamon record. * * @author steve souza * */ public class MonKeyItemBase implements MonKeyItem { private Object summaryLabel; private Object details; public MonKeyItemBase(Object summaryLabel) { this.summaryLabel=(summaryLabel==null) ? "" : summaryLabel; this.details=(summaryLabel==null) ? "" : summaryLabel; } public MonKeyItemBase(Object summaryLabel, Object details) { this.summaryLabel=(summaryLabel==null) ? "" : summaryLabel; this.details=details; } public Object getDetails() { return details; } public void setDetails(Object details) { this.details=details; } // public List getDetails(List list) { // Misc.addTo(list, details); // return list; // // } /** should call getSummaryLabel */ public String toString(){ return summaryLabel.toString(); } public boolean equals(Object obj) { if (this==obj) return true; else if (obj==null) return false; else if (!(obj instanceof MonKeyItemBase)) return false; else { MonKeyItemBase mk=(MonKeyItemBase) obj; return summaryLabel.equals(mk.summaryLabel); } } public int hashCode() { return summaryLabel.hashCode(); } } jamonapi/src/java/com/jamonapi/NExtremeArrayBufferHolder.java0000666000175000017500000000315610622152124024563 0ustar twernertwernerpackage com.jamonapi; import java.util.*; import com.jamonapi.utils.*; /** Buffer used to keep the last N recent array values based on the comparator. Note the Comparator must * be thread safe. It can also stored the last n recent values of ToArray objects. By using various Comparators * you can determine what should stay in the buffer and what should be removed. JAMon comes with a number of buffers * based on this class. * @author steve souza * */ public class NExtremeArrayBufferHolder extends NExtremeBufferHolder { /** Constructor that takes a JAMonArrayComparator that can be used to determine * when values should be removed from and added to the array. This can be used to * decide what to do to values in the buffer based on multiple columns. * @param comparator */ public NExtremeArrayBufferHolder(JAMonArrayComparator comparator) { super(true); setComparator(comparator); } /** Pass true for natural order, and false for reverse order and column number in Object[] to compare starting at 0 */ public NExtremeArrayBufferHolder(boolean naturalOrder, int colToCompare) { super(new JAMonArrayComparator(colToCompare, naturalOrder)); } /** Note the only valid Comparator to be passed is JAMonArrayComparator */ public void setComparator(Comparator comparator) { if (comparator instanceof JAMonArrayComparator) super.setComparator(comparator); } /** Factory method that returns a usable copy of this object */ public BufferHolder copy() { return new NExtremeArrayBufferHolder((JAMonArrayComparator)getComparator()); } } jamonapi/src/java/com/jamonapi/NullMonitor.java0000666000175000017500000000021610667265734022046 0ustar twernertwernerpackage com.jamonapi; public class NullMonitor extends MonitorImp { public NullMonitor() { disable(); } } jamonapi/src/java/com/jamonapi/package.html0000666000175000017500000000044110376452606021173 0ustar twernertwerner This package contains classes and interfaces used to monitor Java applications.

For further information on JAMon go to www.jamonapi.com

Steve Souza - admin@jamonapi.com JAMon

jamonapi/src/java/com/jamonapi/proxy/0000777000175000017500000000000010674450734020075 5ustar twernertwernerjamonapi/src/java/com/jamonapi/proxy/JAMonDataSource.java0000666000175000017500000000441610452521360023650 0ustar twernertwernerpackage com.jamonapi.proxy; import java.io.PrintWriter; import java.io.Serializable; import java.sql.Connection; import java.sql.SQLException; import javax.sql.*; import javax.naming.Referenceable; import javax.naming.Reference; import javax.naming.NamingException; /** The datasource is incomplete. the object factory is not done. It should * be able to wrap an exisiting DataSource however. * @author steve souza * */ public class JAMonDataSource implements DataSource, Referenceable, Serializable { private DataSource realDataSource; public JAMonDataSource(DataSource realDataSource) { this.realDataSource=realDataSource; } public JAMonDataSource() { } /** * Determines if a de-serialized file is compatible with this class. * * Maintainers must change this value if and only if the new version * of this class is not compatible with old versions. See Sun docs * for details. * * Not necessary to include in first version of the class, but * included here as a reminder of its importance. */ private static final long serialVersionUID = 0xABCDABC1; public Connection getConnection() throws SQLException { return MonProxyFactory.monitor(realDataSource.getConnection()); } public Connection getConnection(String userName, String passWord) throws SQLException { return MonProxyFactory.monitor(realDataSource.getConnection(userName, passWord)); } public int getLoginTimeout() throws SQLException { return realDataSource.getLoginTimeout(); } public PrintWriter getLogWriter() throws SQLException { return realDataSource.getLogWriter(); } public void setLoginTimeout(int seconds) throws SQLException { realDataSource.setLoginTimeout(seconds); } public void setLogWriter(PrintWriter output) throws SQLException { realDataSource.setLogWriter(output); } public Reference getReference() throws NamingException { return ((Referenceable)realDataSource).getReference(); } } jamonapi/src/java/com/jamonapi/proxy/JAMonDriver.java0000666000175000017500000002374610672330410023056 0ustar twernertwernerpackage com.jamonapi.proxy; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.sql.*; import com.jamonapi.*; import com.jamonapi.utils.Misc; /** This class will be a proxy for the underlying jdbc driver. * @author steve souza * */ public class JAMonDriver implements Driver { private static final String jamonURL="jdbc:jamon:"; static { try { registerDriver("com.jamonapi.proxy.JAMonDriver"); } catch (Exception e) {/* can't happen */}; } private static Driver registerDriver(String className) throws SQLException { Driver d=null; try { d=(Driver) Class.forName(className).newInstance(); DriverManager.registerDriver(d); } catch (Exception e){ String message="MonProxy-Exception: loading JDBC Driver="+e.getMessage(); MonitorFactory.add(new MonKeyImp(message, new Object[]{message, Misc.getExceptionTrace(e)}, "Exception"),1); // MonitorFactory.add(message, "Exception",1); DriverManager.println(message); throw new SQLException("Can not load real driver ("+className+") from JAMonDriver: "+e.getLocalizedMessage()); } return d; } /** * (non-Javadoc) * @see java.sql.Driver#connect(java.lang.String, java.util.Properties) */ public Connection connect(String url, Properties info) throws SQLException { if (!acceptsURL(url)) return null; // use jamonrealdriver from properties if it is there. URLInfo urlInfo=new URLInfo(url, info); Driver realDriver=getRegisteredDriver(urlInfo.getRealURL()); if (realDriver==null) { // for example: com.sybase.jdbc2.jdbc.SybDriver realDriver=registerDriver(urlInfo.getRealDriverName()); } Monitor mon = MonitorFactory.start("MonProxy-Interface (class=com.jamonapi.proxy.JAMonDriver): public java.sql.Connection com.jamonapi.proxy.JAMonDriver.connect(java.lang.String, java.util.Properties) throws java.sql.SQLException"); Connection conn=null; try { conn=MonProxyFactory.monitor(realDriver.connect(urlInfo.getRealURL(), info)); } catch (SQLException sqlException) { String sqlMessage=",ErrorCode="+sqlException.getErrorCode()+",SQLState="+sqlException.getSQLState(); String label="MonProxy-Exception: Root cause exception="+sqlException.getClass().getName()+sqlMessage; MonKey key=new MonKeyImp(label, new Object[]{label, Misc.getExceptionTrace(sqlException)}, "Exception"); MonitorFactory.add(key,1); // Message for the exception throw sqlException; } finally { mon.stop(); } // call the following method just in case the realDriver returns a proxied connection itself. return conn; } private Driver getRegisteredDriver(String url) { Driver registeredDriver=null; try { registeredDriver = DriverManager.getDriver(url); } catch (Exception e) {/* not an error */} return registeredDriver; } /** Returns true if this driver can respond to the url */ public boolean acceptsURL(String url) throws SQLException { if (url==null) return false; else if (url.toLowerCase().startsWith(jamonURL)) return true; else return false; } /** Returns this drivers properties. Currently the only property is 'jamonrealdriver' */ public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException { return new DriverPropertyInfo[] { getPropertyInfo("jamonrealdriver",null, "The driver to be proxy monitored. Example: jdbc:jamon:sybase:Tds:myserver:1234/mydatabase?LITERAL_PARAMS=true&PACKETSIZE=512&jamonrealdriver=com.sybase.jdbc3.jdbc.SybDriver&HOSTNAME=myhost", true), }; } private static DriverPropertyInfo getPropertyInfo(String name, String value, String description, boolean required) { DriverPropertyInfo prop=new DriverPropertyInfo(name, value); prop.description=description; prop.required=required; return prop; } public int getMajorVersion() { return 2; } public int getMinorVersion() { return 3; } public boolean jdbcCompliant() { return true; } /** Takes a url of the jamon format: jdbc:jamon:sybase:Tds:myserver:1234/mydatabase?jamonrealdriver=com.sybase.jdbc3.jdbc.SybDriver&LITERAL_PARAMS=true&PACKETSIZE=512&HOSTNAME=myhost and returns: com.sybase.jdbc3.jdbc.SybDriver */ public static String getRealDriverName(String url) { return new URLInfo(url).getRealDriverName(); } /** Takes a url of the jamon format: jdbc:jamon:sybase:Tds:myserver:1234/mydatabase?jamonrealdriver=com.sybase.jdbc3.jdbc.SybDriver&LITERAL_PARAMS=true&PACKETSIZE=512&HOSTNAME=myhost and returns the real url associated with the underlying driver: jdbc:sybase:Tds:myserver:1234/mydatabase?LITERAL_PARAMS=true&PACKETSIZE=512&HOSTNAME=myhost */ public static String getRealURL(String url) { return new URLInfo(url).getRealURL(); } // Parses a passed in URL. private static class URLInfo { String realDriverName; String realURL; String jamonURL; // sample url: jdbc:jamon:hsqldb:.:pw=steve;jamonrealDriver=org.hsqldb.jdbcDriver; URLInfo(String jamonURL) { this(jamonURL,null); } // sample url: jdbc:jamon:hsqldb:.:pw=steve;jamonrealDriver=org.hsqldb.jdbcDriver; URLInfo(String jamonURL, Properties info) { this.jamonURL=jamonURL; // .*jamonrealdriver - any characters up to jamonrealdriver // [\\s=]* - any number of optional spaces surrounding the equal sign. // ([\\w\\.]*) - trying to get a package name which can have any number of characters [a-zA-Z_0-9] and '.'. This value is what is needed // [\\W]? - 0 or 1 characters that aren't in a package name. realDriverName = (info==null) ? null : info.getProperty("jamonrealdriver"); if (realDriverName==null) realDriverName=parseURL(".*jamonrealdriver[\\s=]*([\\w\\.]*)[\\W]?", jamonURL); // remove all traces of jamon in the url. this might not strictly be required as drivers // probably ignore anything they don't recognize realURL = jamonURL.replaceAll("jdbc:jamon:", "jdbc:"); realURL = realURL.replaceAll("jamonrealdriver[\\s=\\.\\w]*[\\W]?", ""); } private String parseURL(String pattern, String url) { String result=""; if (url==null) return result; Pattern re = Pattern.compile(pattern); Matcher matcher = re.matcher(url); if (matcher.lookingAt()) result = matcher.group(1); return result.trim(); } String getRealDriverName() { return realDriverName; } String getRealURL() { return realURL; } String getJAMonURL() { return jamonURL; } } public static void main(String[] args) throws Exception { String drivers []= {"jdbc:jamon:sybase:Tds:myserver:1234/mydatabase?jamonrealdriver=com.sybase.jdbc3.jdbc.SybDriver&LITERAL_PARAMS=true&PACKETSIZE=512&HOSTNAME=myhost" ,"jdbc:jamon:sybase:Tds:myserver:1234/mydatabase?LITERAL_PARAMS=true&PACKETSIZE=512&HOSTNAME=myhost&jamonrealdriver=com.sybase.jdbc3.jdbc.SybDriver2" ,"jdbc:jamon:sybase:Tds:myserver:1234/mydatabase?LITERAL_PARAMS=true&PACKETSIZE=512&jamonrealdriver=com.sybase.jdbc3.jdbc.SybDriver&HOSTNAME=myhost" ,"jdbc:jamon:sybase:Tds:myserver:1234/mydatabase?LITERAL_PARAMS=true&PACKETSIZE=512&jamonrealdriver=com.sybase.jdbc3.jdbc.SybDriver&HOSTNAME=myhost" ,"jdbc:jamon:hsqldb:.jamonrealdriver = org.hsqldb.jdbcDriver;" ,"jdbc:jamon:hsqldb:.jamonrealdriver = org.hsqldb.jdbcDriver:" ,"jdbc:jamon:hsqldb:.jamonrealdriver = org.hsqldb.jdbcDriver" ,"jdbc:jamon:microsoft:sqlserver://localhost:1433;jamonrealdriver=com.microsoft.jdbc.sqlserver.SQLServerDriver" ,"jdbc:jamon:microsoft:sqlserver:sybase:Tds:127.0.0.1:5000/dbname;jamonrealdriver=com.sybase.jdbc3.jdbc.SybDriver" ,"jdbc:jamon:informix-sqli://161.144.202.206:3000:INFORMIXSERVER=stars:jamonrealdriver= com.informix.jdbc.IfxDriver:"}; for (int i=0;i=1); } private boolean monitorOtherJDBC(Object value) { return (value instanceof Statement || value instanceof Connection); } private boolean monitorResultSet(Object value) { return (value instanceof ResultSet && params.isResultSetEnabled && params.isInterfaceEnabled); } private Object monitorJDBC(Object returnValue) { if (returnValue instanceof ResultSet) return MonProxyFactory.monitor((ResultSet) returnValue); else if (returnValue instanceof Statement) return MonProxyFactory.monitor((Statement) returnValue); else if (returnValue instanceof Connection) return MonProxyFactory.monitor((Connection) returnValue); else return returnValue; } // This will return true for CallableStatements too. private boolean isPreparedStatement(Object value) { return value instanceof PreparedStatement; } private String getSQL(Object sql) { return (sql==null) ? "null sql" : sql.toString(); } /** * This class associates queries with the PreparedStatement and tracks accesses to it. It also removes values from a Statements sql and * replaces it with '?'. For example: select * from table where key='steve', becomes select * from table where key=? * @author steve souza * */ private static class SQLDeArgMon { private int accessCounter=0; private String sql; private int monRows; private static final int LABEL_IND=0; private MonitorComposite monitors=null; private String[][] data=null; //The constructor creates monitors for 1) the first keyword (i.e. select/delete/...), 2) the sql for the PreparedStatement/Statement // 3) Any keyword matches passed in (such as table names) SQLDeArgMon(String statementType, String sql, List matchStrings) { SQLDeArger sqld=new SQLDeArger(sql, matchStrings); this.sql=sqld.getParsedSQL(); data=sqld.getAll(); // contains 3 cols: summary sql dearged, detail sql, ms. (units) monRows=data.length; for (int i=0;i0)// i.e. a match found matchesList.add(matchStr); } matches=(String[]) matchesList.toArray(new String[0]);// convert matches into an array } } /** Note matchStrings should contain Strings. If it doesn't toString() will be called on the objects */ void setMatchStrings(List matchStrings) { this.matchStrings=matchStrings; } /** SQL types are the first word that is in a sql statement. Examples are * insert, delete, update, and select. However, any word that you add by calling this * method will be detected as a sql type. Note the JDBCMonProxy uses this info * to add a monitor for whenever a select, insert etc are executed. This gives the number * of times and performances of the sql types. A list of all the default * sql types follows: select, update, delete, insert, truncate, exec, create, drop, alter * commit, rollback, grant, revoke, save. Any value that isn't on the list will return * 'other'. The getSQLType method returns the SQL type value in the sql statement passed to the * constructor. * * * @param type */ public static void putSQLType(String type) { sqlTypes.put(type,type); } private void setSQLType(String type) { sqlType = (String)sqlTypes.get(type); if (sqlType==null) sqlType="other"; } // returns true as long as there are characters to process private boolean hasTokens() { return (currentCharNum0) return sqlToParse.substring(start,end); else return null; } // determine if the character is punctuation or not. private boolean isPunctuation(char ch) { return (!isInString() && !Character.isLetterOrDigit(ch)); } private boolean isWordBoundary(char currentCh, char nextCh) { // word boundaries are special puncutation, when not in a string. // select * from table where key=100 // first part of conditional would be triggered with convert(200. if currentchar was '(' // 2nd part: select *, convert(char(20)... from. if characther is 't' of select or 't' // of 'convert' return (isPunctuation(currentCh) && Character.isLetterOrDigit(nextCh)) || (!isInString() && (Character.isWhitespace(nextCh) || !Character.isLetterOrDigit(nextCh))); } // Indicates if the character is embedded in a where clause string surrounded by ' or ". // i.e. this would return true for any characters in between double quotes "steve's" private boolean isInString() { return isInString; } private void setIsInString(boolean isInString) { this.isInString=isInString; } // can either be ' or " private void setStringDelim(char delim) { this.delim=delim; } private char getStringDelim() { return delim; } // Get the current character of processing private char getCurrentChar() { return sqlChars[currentCharNum]; } // get next charater of processing. private char getNextChar() { return sqlChars[currentCharNum+1]; } // Determine if the delimeter is " or ' private void setStringDelim() { // if first delimiter in a string like 'steve' then prepare for a string to be // processed // if in the string count delimeters to know when to end. boolean isDelim=(isInString() && getCurrentChar()==getStringDelim()); if (isDelim) totalDelims++; if (!isInString() && (getCurrentChar()=='\'' || getCurrentChar()=='"')) { delimCounter++; totalDelims++; } // else it is the last ' or " in 'steve' , 'mindy''s''' else if (isInString() && getCurrentChar()==getStringDelim() && getNextChar()!=getStringDelim() && totalDelims%2==0) { delimCounter--; } if (!isInString() && delimCounter==1) { setStringDelim(getCurrentChar()); setIsInString(true); } else if (isInString() && delimCounter==0) setIsInString(false); } // Any token that starts with a 0 returns true. i.e. 1000 0xFF, 10E9, would return true. private boolean isFloatString(String str){ // if null string or the first character is not a digit then this is not a number if (str==null || !Character.isDigit(str.charAt(0))) return false; else // else it is a number return true; } private boolean isQuotedString(String str) { if (str==null || "".equals(str.trim())) return false; char first=str.charAt(0); char last=str.charAt(str.length()-1); // either 'souza' or "souza" is a good quoted string. if ((first=='\'' || first=='"') && first==last) return true; else return false; } private static void printDebugInfo(String sql) { List matches=new ArrayList(); matches.add("employees"); matches.add("dependents"); matches.add("orders"); SQLDeArger s=new SQLDeArger(sql, matches); System.out.println("\nSQL="+sql); System.out.println("ParsedSQL="+s.getParsedSQL()); System.out.println("SQLType="+s.getSQLType()); if (s.hasMatches()) { System.out.println("String Matches="); String[][] m=s.getAll(); for (int i=0;i=100000 or salary<=200000 or name like 'steve%' and name in ('mindy','steve') and short= 20 or sand='no sand' or short=30 and sand='no sand' and sand='no sand' or salary in (select max(salary/2),100 from emps group by name having count(*)>5)"); printDebugInfo("select ?,*,? from table where name like ?"); printDebugInfo("select 'mindy', \"mindy\" from table where name like 'mindy'"); printDebugInfo(" delete from table where key name like 'mindy%' "); printDebugInfo("myproc"); printDebugInfo("exec myproc"); printDebugInfo("select * from employees as e, customers as c, dependents as d where e.id=c.id and c.id=d.id and e.name in (select * from favorite where name like 'j%' and salary > 50000)"); printDebugInfo("select * from employees as e, customers as c, dependents as d where e.id=c.id and c.id=d.id and e.name not in ('steve','souza','jeff','beck') and salary in (100000,20000, 50000) and age!=50"); printDebugInfo("select 10_name_10, name_10_name,* from employees where age<50 and age!=10.0 and age!=-50.0 and age < 0xFF and age < 0x0123456789aAbBcCdDeEfF and age<+10E09"); // valid java variables can begin with a letter, $, or _ printDebugInfo("select 10/22/06, date102206, m10/d22/y06, 10name, $10_name, _10_name, 10_name_10, name_10_na1010me,* from employees where age<50 and age!=10.0 and age!=-50.0 and age < 0xFF and age < 0x0123456789aAbBcCdDeEfF and age<+10E09 and age<1.72E3F"); printDebugInfo("pageHits 'ssouza' jamon mb 100.5:pageHits jamon:pagehits.ssouza"); } } jamonapi/src/java/com/jamonapi/Range.java0000666000175000017500000000142010603753240020576 0ustar twernertwernerpackage com.jamonapi; /** Class that contains buckets of time that are tracked within a monitor. */ public interface Range extends JAMonListener { /**Return a FrequencyDist associated with the passed in value. */ public FrequencyDist getFrequencyDist(double value); /** Return the array of all FrequnceDists in this range*/ public FrequencyDist[] getFrequencyDists(); /** Add a value to the FrequencyDist associated with the value that is passed */ public void add(double value); /** Reset all FrequencyDists associated with this Range */ public void reset(); /** Get the logical operator associated with the top end point. Possible values *are < and <= **/ public String getLogicalOperator(); } jamonapi/src/java/com/jamonapi/RangeBase.java0000666000175000017500000000544510610740562021405 0ustar twernertwernerpackage com.jamonapi; /** Base class for ranges. * Range.java * * Created on November 6, 2005, 3:40 PM */ /** *12/16/05 - Added the ability to copy ranges from the factory so each monitor will have a unique range */ final class RangeBase extends RangeImp { private double[] rangeValues; /** The first range will catcth anything less than that value. */ RangeBase(RangeHolder rangeHolder) { this.rangeHolder=rangeHolder; this.isLessThan=rangeHolder.isLessThan(); this.rangeValues=rangeHolder.getEndPoints(); int len=rangeValues.length; // add one to cover values less than first range frequencyDist=new FrequencyDistBase[len+1]; for (int i=0;iMonitor that tracks execution time in milliseconds.

* *

Note due to the fact that when start is called it resets the startTime instance * variable and different threads can call start() before one of the threads calls * stop this object when used BY ITSELF would not be thread safe. However, when * not reused i.e. when TimeMon's are always taken from MonitorFactory it is threadsafe.

* *

I didn't attempt to make this thread safe as even if it was having two threads * subsequently call start, start before a stop would reset the startTime and so * make one of the callers time off.

* *

Note this class is a thin wrapper that adds time capabilities to the basic Monitor * class

*/ class TimeMon extends DecoMon { private long startTime; public TimeMon(MonKey key, MonInternals monData) { super(key, monData); } public Monitor start() { super.start(); startTime=System.currentTimeMillis(); return this; } // note synchronization is handled by the underlying object ????? public Monitor stop() { long endTime=System.currentTimeMillis(); setAccessStats(endTime);// saves some cycles by not recalculating time for time monitors add(endTime-startTime);// accrue status super.stop();// decrement active. return this; } public void reset() { super.reset(); startTime=0; } } jamonapi/src/java/com/jamonapi/TimeMon2.java0000666000175000017500000000434210673330600021200 0ustar twernertwernerpackage com.jamonapi; /** * Class that keeps time from the Factories but does not save it. Note this object *should not be reused. You should always get a new instance from MonitorFactory. * * Created on February 19, 2006, 9:05 AM */ import java.util.*; import com.jamonapi.utils.*; final class TimeMon2 extends TimeMon { public TimeMon2() { super(new MonKeyImp("timer","ms."),new MonInternals()); monData.activityStats=new ActivityStats(); monData.isTimeMonitor=true; } public String toString() { return getLastValue()+" ms."; } private static void testDisplay(String name, JAMonListener listener) { System.out.print("\n\n***"+name); Object[][] data=((JAMonBufferListener)listener).getDetailData().getData(); for (int i=0;i * MYKEY
* MyKey
* ... * * Other than that this class works like a regular HashMap. */ import java.util.*; public class AppMap extends java.util.HashMap { public AppMap() { } /** Constructs an empty HashMap with the default initial capacity (16) and the default load factor (0.75). * */ public AppMap(int initialCapacity) { super(initialCapacity); } /** Constructs an empty HashMap with the specified initial capacity and the default load factor (0.75). */ public AppMap(int initialCapacity, float loadFactor) { super(initialCapacity,loadFactor); } /** Constructs an empty HashMap with the specified initial capacity and load factor. */ public AppMap(Map m) { putAll(m); } public Object put(Object key, Object object) { return super.put(convertKey(key), object); } public boolean containsKey(Object key) { // The normal case is done first as a performance optimization. It seems to make checks around 30% faster // due to only converting the case of the string comparison only when required. return super.containsKey(key) || super.containsKey(convertKey(key)); } public Object get(Object key) { return super.get(convertKey(key)); } public static Object get(Map map, Object key) throws AppBaseException { Object object = map.get(key); if (object!=null) return object; else throw new AppBaseException(key+" does not exist in the HashMap."); } protected Object convertKey(Object key) { if (key instanceof String && key!=null) key = key.toString().toLowerCase(); return key; } public static Map createInstance() { return new AppMap(); } public static void main(String[] args) { Map map=new HashMap(); map.put("HeLLo", "world"); System.out.println("Should return null: "+map.get("HELLO")); System.out.println("Contents of HashMap="+map); map=new AppMap(map); System.out.println("Should return 'world': "+map.get("HELLO")); System.out.println("Contents of AppMap="+map); } } jamonapi/src/java/com/jamonapi/utils/ArrayElementComparator.java0000666000175000017500000000236710622152124025330 0ustar twernertwernerpackage com.jamonapi.utils; import java.util.Comparator; /** Maps a Comparator to a column number of an array starting at position 0. Used by JAMonArrayComparator */ class ArrayElementComparator implements Comparator { // Used to compare elements of an array that reside within the same column. The Object[][] array itself // will be sorted according to this algorithm. private int sortCol; private Comparator comparator; /** Constructor that takes the position in array to be compared as well as it's comparator */ public ArrayElementComparator(int sortCol, Comparator comparator) { this.comparator=comparator; this.sortCol=sortCol; } public ArrayElementComparator(int sortCol, boolean naturalOrder) { comparator=new JAMonComparator(naturalOrder); this.sortCol=sortCol; } /** Return col to be sorted/compared */ public int getSortCol() { return sortCol; } /** Call the comparator on the column */ public int compare(Object o1, Object o2) { return comparator.compare(o1, o2); } public String toString() { return "sortCol="+sortCol+", comparator="+comparator; } } jamonapi/src/java/com/jamonapi/utils/ArraySorter.java0000666000175000017500000001061510401426616023165 0ustar twernertwernerpackage com.jamonapi.utils; /** * ArraySorter is used to sort 2 dimensional arrays of objects by one of the columns in the array. Right now this class * sorts only for the monitor report, but could be made more generic. Look in the main method to see how this class is used. * **/ import java.util.*; class ArraySorter { private static void display(Object[][] array) { int rows=array.length; int cols=array[0].length; for (int i=0; iThis object can contain a configurable number of items in a buffer. The items kept are rows in a 2 dim * array and so the data can be viewed in a table. It is used in jamon to store recent exceptions, and * sql queries from the various proxy classes. However it may be used elsewhere. It is thread safe. * By default the buffer holds 50 elements, but this can be overridden in the constructor.

It uses a bufferHolder to determine whether a value should be added and another one removed when the buffer is full. For example * the value could only be added if the new value is greater than the smallest member of the BufferList. Simply implement the BufferHolder * interface to implement your desired rules. *

* @author steve souza * */ public class BufferList implements DetailData { private boolean enabled=true; private int bufferSize=50; private String[] header; private BufferHolder bufferHolder; /** Constructor that takes the header of the structure of the rows that are stored. * For example header could be {"Time", "Exception info",}; * Uses a FIFOBuffer. * @param header */ public BufferList(String[] header) { this.header=header; this.bufferHolder=new FIFOBufferHolder(); } /** Pass in the header and bufferHolder to be used */ public BufferList(String[] header, BufferHolder bufferHolder) { this.header=header; this.bufferHolder=bufferHolder; } /** Use a FIFOBuffer and specify its header and size */ public BufferList(String[] header, int bufferSize) { this.header=header; this.bufferSize=bufferSize; this.bufferHolder=new FIFOBufferHolder(); } /** Specify the header, bufferSize and BufferHolder to be used in the BufferList */ public BufferList(String[] header, int bufferSize, BufferHolder bufferHolder) { this.header=header; this.bufferSize=bufferSize; this.bufferHolder=bufferHolder; } private BufferList(String[] header, int bufferSize, boolean enabled, BufferHolder bufferHolder) { this.header=header; this.bufferSize=bufferSize; this.enabled=enabled; this.bufferHolder=bufferHolder; } /** * Get the number of Exceptions that can be stored in the buffer before the oldest entries must * be removed. * */ public synchronized int getBufferSize() { return bufferSize; } public synchronized void setBufferHolder(BufferHolder bufferHolder) { this.bufferHolder=bufferHolder; } /** * Set the number of Exceptions that can be stored in the buffer before the oldest entries must * be removed. A value of 0 will disable the collection of Exceptions in the buffer. Note if * MonProxy is disabled exceptions will also not be put in the buffer. * */ public synchronized void setBufferSize(int newBufferSize) { if (bufferSize>newBufferSize) resetBuffer(reduceBuffer(newBufferSize)); bufferSize=newBufferSize; } /** Reduce size of buffer while not losing current elements beyond what the size reduction would cause. If the size is increased then no loss * of data occurs. * */ private LinkedList reduceBuffer(int newSize) { LinkedList newBuffer=new LinkedList(); List original=bufferHolder.getOrderedCollection(); Collections.reverse(original);// reverse to save the most recent values Iterator iter=original.iterator(); int i=0; while (iter.hasNext() && i=bufferSize && bufferHolder.shouldReplaceWith(obj)) { bufferHolder.remove(obj); } if (getRowCount()0) { bufferListArray=bufferList.getCollection().toArray(); } } return bufferListArray; } private String[] buildHeader(String[] h) { // if header is bigger than data of first row then return header up to column width // if header is smaller than data then PAD HEADER with colN Object[] head=resize(h, getColCount()); if (head==null) return null; String[] headerStr=new String[head.length]; for (int i=0;isize) ? size : originalData.length; for (int i=0;icolsInFirstRow) ? colsInHeader : colsInFirstRow; } } jamonapi/src/java/com/jamonapi/utils/Command.java0000666000175000017500000000047310367776310022301 0ustar twernertwernerpackage com.jamonapi.utils; /** * Simple interface that is used in the implementation of the Gang Of 4 Command pattern in Java. * Implement this Interface to pass a command to an internal iterator **/ public interface Command { public void execute(Object value) throws Exception; } jamonapi/src/java/com/jamonapi/utils/CommandIterator.java0000666000175000017500000000565010445260154024004 0ustar twernertwernerpackage com.jamonapi.utils; /** Used with the Command interface to implement the Gang of 4 Command pattern to execute some logic for * every entry of various iterators. This class allows a Command object to be passed to various iterators. * This capability is also similar to function pointers in C. **/ import java.util.*; public class CommandIterator extends java.lang.Object { private CommandIterator() { } /** Iterate through a ResultSet passing in a Command object. The command object will be passed an Object[] * representing 1 row of the result set **/ /* public static void iterate(ResultSet resultSet, Command command)throws Exception { ResultSetUtils rsu = ResultSetUtils.createInstance(); ArrayList arrayList = new ArrayList(); rsu.resultSetToArrayList(arrayList,resultSet); iterate(arrayList, command); } */ /** Iterate through a Map passing Command object a Map.Entry. * Command code would look something like: * entry = (Map.Entry) object; * entry.getKey(), entry.getValue(); **/ public static void iterate(Map map, Command command)throws Exception { iterate(map.entrySet().iterator() , command); } /** Iterate through a Collection passing the Command object each element in the collection. **/ public static void iterate(Collection collection, Command command)throws Exception { iterate(collection.iterator() , command); } /** Iterate through an Enumeration passing the Command object each element in the Collection **/ public static void iterate(Enumeration enumer, Command command)throws Exception { iterate(new EnumIterator(enumer) , command); } /** Iterate passing each Command each Object that is being iterated **/ public static void iterate(Iterator iterator, Command command)throws Exception { while (iterator.hasNext()) { command.execute(iterator.next()); } } /** Test code for this class **/ public static void main(String argv[]) throws Exception { ArrayList arrayList = new ArrayList(); Vector vector = new Vector(); class TestCommand implements Command { public void execute(Object value) throws Exception { System.out.println("command"+value); } }; TestCommand testCommand = new TestCommand(); arrayList.add("1"); arrayList.add("2"); arrayList.add("3"); vector.addElement("4"); vector.addElement("5"); vector.addElement("6"); iterate(arrayList, testCommand); iterate(vector.elements(), testCommand); } } jamonapi/src/java/com/jamonapi/utils/DateMathComparator.java0000666000175000017500000000556710622152124024434 0ustar twernertwernerpackage com.jamonapi.utils; import java.util.*; /** Comparator that allows you to pass Calendar fields and a negative number for the number * of this filed (i.e. hours/days) that a Date should not exceed. Use fields like Calendar.DATE, HOUR_OF_DAY, DAY_OF_MONTH, DAY_OF_WEEK, DAY_OF_YEAR ETC. * . Values to b used for dateToAdd in the constructor could be -7 for 7 days ago, or -24 for 24 hours ago depending on what * was passed in the dateField. * * @author steve souza * */ public class DateMathComparator extends JAMonComparator { private int dateField; private int dateToAdd; /**Use fields like Calendar.DATE, HOUR_OF_DAY, DAY_OF_MONTH, DAY_OF_WEEK, DAY_OF_YEAR ETC.*/ private Calendar calendar=new GregorianCalendar(); private static final boolean NATURAL_ORDER=true; public DateMathComparator(int dateField, int dateToAdd) { super(NATURAL_ORDER); this.dateField=dateField; this.dateToAdd=dateToAdd; } protected int compareThis(Object o1, Object o2) { int retVal=0; if (o1 instanceof Date && o2 instanceof Date) { Date d1=(Date)o1; Date d2=(Date)o2; calendar.setTime(new Date()); calendar.add(dateField, dateToAdd);// i.e. todays date -7 days Date dateAddValue=calendar.getTime(); // 2 dates are equal if they are both above or below the Calendar field threshold // else date1 is greater if it is greater than threshold and date2 isn't // and date1 is less than the threshold than it is greater than date2. int d1CompNum=d1.compareTo(dateAddValue); int d2CompNum=d2.compareTo(dateAddValue); if (d1CompNum<=-1 && d2CompNum<=-1) return 0; else if (d1CompNum>=1 && d2CompNum>=1) return 0; else if (d1CompNum==1) return 1; else if (d1CompNum==-1) return -1; } return retVal; } /** Test code */ public static void main(String[] arg) { //7 DAYS //24 HOURS Date today=new Date(); Calendar calendar=new GregorianCalendar(); calendar.setTime(today); calendar.add(Calendar.DAY_OF_YEAR, -8); Date minus8Days=calendar.getTime(); System.out.println(new DateMathComparator(Calendar.DAY_OF_YEAR, -7).compare(today, minus8Days)); calendar.setTime(today); calendar.add(Calendar.DAY_OF_YEAR, -6); Date minus6Days=calendar.getTime(); System.out.println(new DateMathComparator(Calendar.DAY_OF_YEAR, -7).compare(today, minus6Days)); calendar.setTime(today); calendar.add(Calendar.HOUR_OF_DAY, -6); Date minus6Hours=calendar.getTime(); System.out.println(new DateMathComparator(Calendar.HOUR_OF_DAY, -7).compare(today, minus6Hours)); calendar.setTime(today); calendar.add(Calendar.HOUR_OF_DAY, -8); Date minus8Hours=calendar.getTime(); System.out.println(new DateMathComparator(Calendar.HOUR_OF_DAY, -7).compare(today, minus8Hours)); } } jamonapi/src/java/com/jamonapi/utils/DefaultGeneralizer.java0000666000175000017500000000106310626123510024455 0ustar twernertwernerpackage com.jamonapi.utils; import com.jamonapi.proxy.SQLDeArger;; public class DefaultGeneralizer implements Generalizer { /** Replaces numbers and quoted strings with '?'. For example * Original=ERROR Invalid login name: 'ssouza', 404 * becomes=ERROR Invalid login name: ?,? * * Original=ERROR Invalid login name: ssouza, _404 * becomes (no change)=ERROR Invalid login name: ssouza, _404 */ public String generalize(String detail) { return new SQLDeArger(detail).getParsedSQL(); } } jamonapi/src/java/com/jamonapi/utils/DetailData.java0000666000175000017500000000042010671013542022674 0ustar twernertwernerpackage com.jamonapi.utils; /** Interface for array tabular data */ public interface DetailData { public String[] getHeader(); public Object[][] getData(); // public boolean hasData(); // public boolean isEmpty(); // public int getRowCount(); } jamonapi/src/java/com/jamonapi/utils/EnumIterator.java0000666000175000017500000000120310445021776023325 0ustar twernertwernerpackage com.jamonapi.utils; /** Simple Wrapper utility class that makes an Enumeration behave like an Iterator. **/ import java.util.*; public class EnumIterator extends java.lang.Object implements java.util.Iterator { Enumeration enumer; public EnumIterator(Enumeration enumer) { this.enumer=enumer; } public boolean hasNext() { return enumer.hasMoreElements(); } public Object next() { return enumer.nextElement(); } public void remove() { throw new UnsupportedOperationException(); } } jamonapi/src/java/com/jamonapi/utils/FIFOBufferHolder.java0000666000175000017500000000203510622152124023713 0ustar twernertwernerpackage com.jamonapi.utils; import java.util.*; /** First-in, first-out buffer. When the BufferList is filled the first element is removed to * make room for the newest value, then the second oldest etc. Used in BufferList and subsequently * JAMonBufferListeners. * * @author steve souza * */ public class FIFOBufferHolder implements BufferHolder { private LinkedList bufferList=new LinkedList(); public void add(Object replaceWithObj) { bufferList.addLast(replaceWithObj); } public void remove(Object replaceWithObj) { bufferList.removeFirst(); } public boolean shouldReplaceWith(Object replaceWithObj) { return true; } public List getCollection() { return bufferList; } public List getOrderedCollection() { return bufferList; } public void setCollection(List list) { this.bufferList=(LinkedList) list; } public BufferHolder copy() { return new FIFOBufferHolder(); } } jamonapi/src/java/com/jamonapi/utils/FileUtils.java0000666000175000017500000000266210401426616022613 0ustar twernertwernerpackage com.jamonapi.utils; /** Reusable Utilities used for File manipulations such as reading a file as a String. **/ import java.io.*; import com.jamonapi.*; public class FileUtils extends java.lang.Object { /** * Read text files contents in as a String. * Sample Call: * String contents=FileUtils.getFileContents("autoexec.bat"); **/ public static String getFileContents(String fileName)throws FileNotFoundException,IOException { Monitor mon=MonitorFactory.start("com.jamonapi.utils.FileUtils-getFileContents()"); final int EOF=-1; StringBuffer fileContents = new StringBuffer(); BufferedReader inputStream=null; // Loop through text file storing contents of the file in a string buffer and return the files // contents to the caller. try { inputStream = new BufferedReader(new FileReader(fileName)); int inputChar = inputStream.read(); while (inputChar!=EOF) { fileContents.append((char) inputChar); inputChar = inputStream.read(); } } finally { if (inputStream!=null) { inputStream.close(); } mon.stop(); } return fileContents.toString(); } } jamonapi/src/java/com/jamonapi/utils/Generalizer.java0000666000175000017500000000113210626123510023145 0ustar twernertwernerpackage com.jamonapi.utils; /** This class interface will return a detail form in the getDetailLabel method which * is appropriate for logging. getSummaryLabel returns a label that is appropriate for jamon * (i.e. not too unique). toString() should always return the more general/summary label. * Example: getDetailLabel()=select * from table where key=100 and name='mindy' * getLabel()=select * from table where key=? and name=? * toString()=getLabel() * @author steve souza * */ public interface Generalizer { public String generalize(String detail); } jamonapi/src/java/com/jamonapi/utils/JAMonArrayComparator.java0000666000175000017500000000625210622152124024700 0ustar twernertwerner package com.jamonapi.utils; // FormattedDataSet API import java.util.*; /** *

This class allows you to compare Object[] arrays by multiple columns. It can also compare any Object that implements the ToArray interface. * Each column will use the underlying * Compareable interface in natural or reverse order depending on how it is added, or will use the passed in Comparator. *

* */ /** Note I took this code from fdsapi.com, and would like to eventually merge these 2 projects, so * this class will eventually be replaced by the one in FDS. */ public class JAMonArrayComparator extends java.lang.Object implements Comparator { private List sortCols=new ArrayList(); private static final Comparator defaultComparator=new JAMonComparator(); public JAMonArrayComparator() { } /** Sort/compare the passed in col number starting at 0 in natural (true) or reverse (false) * order based on the columns Compareable interface being called. * @param sortCol * @param naturalOrder */ public JAMonArrayComparator(int sortCol, boolean naturalOrder) { addCompareCol(sortCol, naturalOrder); } /** Compare the passed in col in natural order */ public void addCompareCol(int sortCol) { sortCols.add(new ArrayElementComparator(sortCol, true)); } /** Compare the passed in col in natural or reverse order */ public void addCompareCol(int sortCol, boolean naturalOrder) { sortCols.add(new ArrayElementComparator(sortCol, naturalOrder)); } /** Compare the passed in col based on the passed in Comparator */ public void addCompareCol(int sortCol, Comparator comparator) { sortCols.add(new ArrayElementComparator(sortCol, comparator)); } /** * Method used by the comparator interface.

* o1 < o2 - returns a negative integer
* o1==o2 - returns zero
* o1>o2 - returns a postitive integer

* Iterate through all columns that should be compared (in the proper order) * and call the Comparator for each of the column elements. */ public int compare(Object o1, Object o2) { Object[] array1=null; Object[] array2=null; int compareVal=0; // Put data into array format or call the underlying defaultComparator if // the data isn't tabular if (o1 instanceof ToArray && o2 instanceof ToArray) { array1=((ToArray)o1).toArray(); array2=((ToArray)o2).toArray(); } else if (o1 instanceof Object[] && o2 instanceof Object[]) { array1=(Object[])o1; array2=(Object[])o2; } else { return defaultComparator.compare(o1, o2); } /** Iterate through the Arrays comparators to compare columns */ Iterator iter=sortCols.iterator(); while (iter.hasNext()) { ArrayElementComparator elementComp=(ArrayElementComparator) iter.next(); Object element1=array1[elementComp.getSortCol()]; Object element2=array2[elementComp.getSortCol()]; compareVal=elementComp.compare(element1,element2); if (compareVal!=0) // if one of the elments is not equal we know all we need to know and can return break; } return compareVal; } } jamonapi/src/java/com/jamonapi/utils/JAMonComparator.java0000666000175000017500000000535110621676754023723 0ustar twernertwernerpackage com.jamonapi.utils; import java.util.Comparator; public class JAMonComparator implements Comparator { private static final int LESSTHAN=-1; private static final int EQUALTO=0; private static final int GREATERTHAN=1; private boolean naturalOrder=true; private Comparator childComparator=null;// used if JAMonComparator is used as a decorator. public JAMonComparator() { } public JAMonComparator(boolean naturalOrder) { this.naturalOrder=naturalOrder;; } public JAMonComparator(boolean naturalOrder, Comparator childComparator) { this.naturalOrder=naturalOrder; this.childComparator=childComparator; } /* * Returns * this object is less than - returns a negative integer, * this object equal to - retunrs zero * this object greater than - return positive integer */ public int compare(Object newObj, Object existingObj) { // Note the following if condition ensures that nulls may be in the array. if ((existingObj==null && newObj==null) || !(existingObj instanceof Comparable) || !(newObj instanceof Comparable)) // 2 nulls are considered equal. if either object is not comparable return that they are equal return EQUALTO; // if either other value is null then we don't want this to return less // (i.e. true for this conditional) so return 1 (greater than) // When existing is null always replace. else if (existingObj==null) return GREATERTHAN; else if (newObj==null) // when new is null never replace return LESSTHAN; if (childComparator==null) { // Note I am using the passed in value as the Comparable type as this seems more flexible. For example // You may not have control over the array values, but you do over what comparable value you pass in, but being // as the comparison is really asking if the col value is greater than the comparison value I am taking the negative // value. i.e. The compareTo() test is really asking if the compValue is greater than the colValue so flipping it with a // negative gives us the right answer. return reverseIfNeeded(compareThis(newObj, existingObj)); } else return reverseIfNeeded(childComparator.compare(newObj, existingObj)); } protected int compareThis(Object newObj, Object existingObj) { Comparable comparable=(Comparable) newObj; return comparable.compareTo(existingObj); } private int reverseIfNeeded(int retVal) { return (naturalOrder) ? retVal : -retVal; } public boolean isNaturalOrder() { return naturalOrder; } } jamonapi/src/java/com/jamonapi/utils/LocaleContext.java0000666000175000017500000001512610401426616023456 0ustar twernertwerner/* * Project: JAMon * $RCSfile: LocaleContext.java,v $ * $Revision: 1.8 $ ($Author: stevesouza $) * $Date: 2006/03/02 04:26:05 $ */ package com.jamonapi.utils; /** * Provides a context for localized parameters, mainly formatters, in thread local scope. * Note normally the Formatting classes are not thread safe. * This class uses ThreadLocal to return a unique object to each thread, so the getFloatingPointFormatter * returns the same object in one thread, and getIntegerFormatter returns another one in the same thread. * By using only LocaleContext to get Format objects instead of constructing Format objects * otherwise, thread-safity is ensured. This class is tuned for performance: constructing a Format * object is rather expensive, this class only creates them when really needed. * * @author Jeroen Borgers * @author Steve Souza */ import java.text.DateFormat; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.util.Locale; public final class LocaleContext { /** the thread local storage for the locale specific formatters */ private static final ThreadLocalFormatterStorage formatterStorage = new ThreadLocalFormatterStorage(); /** @return the thread local storage for the fixed-point number formatting specific for the locale */ public static DecimalFormat getFloatingPointFormatter() { return getFormatters().getFloatingPointFormatter(); } /** @return the thread local storage for the integral number formatting specific for the locale */ public static DecimalFormat getIntegerFormatter() { return getFormatters().getIntegerFormatter(); } /** @return the thread local storage for the percent number formatting specific for the locale */ public static DecimalFormat getPercentFormatter() { return getFormatters().getPercentFormatter(); } /** @return the thread local storage for the currency formatting specific for the locale */ public static DecimalFormat getCurrencyFormatter() { return getFormatters().getCurrencyFormatter(); } /** * @return the thread local storage for the number formatting decimal grouping separator * specific for the locale */ public static char getDecimalGroupSeparator() { return getFormatters().getDecimalGroupSeparator(); } /** @return the locale specific date time formatter */ public static DateFormat getDateFormatter() { return getFormatters().getDateFormatter(); } /** * Sets the locale to apply for formatting. * * @param locale the locale to apply for formatting. */ public static void setLocale(Locale locale) { getFormatters().setLocale(locale); } /** * @return Returns the thread associated, locale specific formatters */ private static Formatters getFormatters() { return (Formatters) formatterStorage.get(); } /** Inner class for storage of thread-associated formatters */ private static final class ThreadLocalFormatterStorage extends ThreadLocal { /** * @return the current thread's initial value for this thread-local variable. This method * will be invoked at most once per accessing thread for each thread-local. */ protected Object initialValue() { return new Formatters(); } } /** Inner class of thread-associated formatters */ private static class Formatters { private Locale locale; private DecimalFormat floatingPointFormatter; private DecimalFormat integerFormatter; /** the number formatting decimal grouping separator */ private char decimalSeparator = 0; private DateFormat dateFormatter; private DecimalFormat percentFormatter; private DecimalFormat currencyFormatter; void setLocale(Locale locale) { this.locale = locale; // now all formatters need to re-created when needed, to apply the new locale floatingPointFormatter = null; integerFormatter = null; decimalSeparator = 0; dateFormatter = null; percentFormatter = null; currencyFormatter = null; } Locale getLocale() { if (locale == null) { // if no locale specified from client locale = Locale.getDefault(); } return locale; } DecimalFormat getFloatingPointFormatter() { if (floatingPointFormatter == null) { floatingPointFormatter = (DecimalFormat) NumberFormat.getNumberInstance(getLocale()); floatingPointFormatter.applyPattern("#,###.#"); } return floatingPointFormatter; } DecimalFormat getIntegerFormatter() { if (integerFormatter == null) { integerFormatter = (DecimalFormat) NumberFormat.getNumberInstance(getLocale()); integerFormatter.applyPattern("#,###"); } return integerFormatter; } DecimalFormat getPercentFormatter() { if (percentFormatter == null) { percentFormatter = (DecimalFormat) NumberFormat.getPercentInstance(getLocale()); } return percentFormatter; } DecimalFormat getCurrencyFormatter() { if (currencyFormatter == null) { currencyFormatter = (DecimalFormat) NumberFormat.getCurrencyInstance(getLocale()); } return currencyFormatter; } char getDecimalGroupSeparator() { if (decimalSeparator == 0) { DecimalFormatSymbols symbols = new DecimalFormatSymbols(getLocale()); decimalSeparator = (new Character(symbols.getGroupingSeparator())).charValue(); } return decimalSeparator; } DateFormat getDateFormatter() { if (dateFormatter == null) { dateFormatter = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.DEFAULT, getLocale()); } return dateFormatter; } } /** contructs a new LocaleContext, for private use only */ private LocaleContext() { } } jamonapi/src/java/com/jamonapi/utils/Logger.java0000666000175000017500000000214610367776310022141 0ustar twernertwernerpackage com.jamonapi.utils; /** Very Simple Utility class used for Logging. **/ public class Logger extends java.lang.Object { String prefix=""; protected Logger() { } private static Logger logger = new Logger(); private static Logger createInstance() { return logger; } public static void log(Object obj) { createInstance().instanceLog(obj); } public static void logInfo(Object obj) { // This function will be able to be disabled at runtime. i.e. do nothing whereas log is permanent. // for now they do the same thing however. createInstance().instanceLog(obj); } public static void logDebug(Object obj) { // This function will be able to be disabled at runtime. i.e. do nothing whereas log is permanent. // for now they do the same thing however. createInstance().instanceLog(obj); } protected void instanceLog(Object obj) { System.out.println(prefix+obj); } } jamonapi/src/java/com/jamonapi/utils/Misc.java0000666000175000017500000002261610672050612021606 0ustar twernertwernerpackage com.jamonapi.utils; /** Difficult to group Utilities **/ import java.util.*; import java.text.*; import com.jamonapi.Monitor; public class Misc extends java.lang.Object { /** Returns an Objects ClassName minus the package name * Sample Call: * String className=Misc.getClassName("My Object"); // returns "String" **/ public static String getClassName(Object object) { String className = (object==null ? "null" : object.getClass().getName()); // gov.gsa.fss.fim.Misc return className.substring(className.lastIndexOf(".")+1); // Misc } // Return Exception information as a row (1 dim array) public static String getExceptionTrace(Throwable exception) { // each line of the stack trace will be returned in the array. StackTraceElement elements[] = exception.getStackTrace(); StringBuffer trace = new StringBuffer().append(exception).append("\n"); for (int i = 0; i < elements.length; i++) { trace.append(elements[i]).append("\n"); } return trace.toString(); } /** Note if the passed object is a Colleciton itself or an Object[] it will be expanded*/ public static void addTo(Collection coll, Object objToAdd) { if (objToAdd instanceof Collection) coll.addAll((Collection)objToAdd); else if (objToAdd instanceof Object[]) coll.addAll(Arrays.asList((Object[])objToAdd)); else if (objToAdd instanceof ToArray) coll.addAll(Arrays.asList(((ToArray)objToAdd).toArray())); else coll.add(objToAdd); } public static String getAsString(Object obj) { if (obj==null) return null; else if (obj instanceof Collection) return getCollAsString((Collection)obj); else if (obj instanceof Object[]) return getArrAsString((Object[]) obj); else if (obj instanceof ToArray) return getArrAsString(((ToArray)obj).toArray()); else if (obj instanceof Throwable) return getExceptionTrace((Throwable)obj); else return obj.toString(); } private static String getCollAsString(Collection coll) { int currentElement=1; int lastElement=coll.size(); Iterator iter=coll.iterator(); StringBuffer buff=new StringBuffer(); // loop through elements creating a comma delimeted list of the values while(iter.hasNext()) { Object obj=iter.next(); if (obj instanceof Throwable) obj=getExceptionTrace((Throwable)obj); buff.append(obj); if (currentElement!=lastElement) buff.append(",\n"); currentElement++; } return buff.toString(); } private static String getArrAsString(Object[] arr) { int lastElement=arr.length-1; StringBuffer buff=new StringBuffer(); for (int i=0;iwww.jamonapi.com

Steve Souza - admin@jamonapi.com JAMon

jamonapi/src/java/com/jamonapi/utils/ToArray.java0000666000175000017500000000036510621632366022277 0ustar twernertwernerpackage com.jamonapi.utils; /** Interface used to return info from a monitor. Primarily will be used in the JAMonBufferListener * class. * * @author steve souza * */ public interface ToArray { public Object[] toArray(); } jamonapi/src/java/com/jamontomcat/0000777000175000017500000000000010674450736017434 5ustar twernertwernerjamonapi/src/java/com/jamontomcat/JAMonTomcat4Valve.java0000666000175000017500000001016210674051434023465 0ustar twernertwernerpackage com.jamontomcat; /** Note this is simply a copy of com.jamonapi.http.JAMonTomcatValve. Tomcat 5.5 would not work with the valve in the same jar as the jamon classes * and also display stats in jamon.war. Simply compile this class sepeartely and put it in tomcats /server/classes/com/jamontomcat/http and put jamon-2.7.jar or higher in tomcats * common/lib directory. Note this class should also work in tomcat 6 although it is easier simply to put jamon-2.7.jar (or higher in the server/lib) * for tomcat 6. * * * same as default above * * * @author steve souza * */ import javax.servlet.*; import java.io.IOException; import org.apache.catalina.*; import org.apache.catalina.valves.*; import com.jamonapi.http.*; public class JAMonTomcat4Valve extends ValveBase { private static final String PREFIX="com.jamontomcat.http.JAMonTomcat4Valve"; private static final String DEFAULT_SUMMARY="default, response.getContentCount().bytes, response.getStatus().value.httpStatus, request.contextpath.ms"; HttpMonFactory httpMonFactory=new HttpMonFactory(PREFIX); private final String jamonSummaryLabels="default"; public JAMonTomcat4Valve() { setSummaryLabels(jamonSummaryLabels); } /** * Extract the desired request property, and pass it (along with the * specified request and response objects) to the protected * process() method to perform the actual filtering. * This method must be implemented by a concrete subclass. * * @param request The servlet request to be processed * @param response The servlet response to be created * * @exception IOException if an input/output error occurs * @exception ServletException if a servlet error occurs * http://www.jdocs.com/tomcat/5.5.17/org/apache/catalina/valves/RequestFilterValve.html * * log response, request to see what they do. * debug mode? * test xml - read property */ public void invoke(Request request, Response response, ValveContext valveContext) throws IOException, ServletException { HttpMon httpMon=null; try { httpMon=httpMonFactory.start(request, response); // tomcat 4/5 if (valveContext!=null) valveContext.invokeNext(request, response); } catch (Throwable e) { httpMon.throwException(e); } finally { httpMon.stop(); } } public void setSummaryLabels(String jamonSummaryLabels) { httpMonFactory.setSummaryLabels(jamonSummaryLabels, DEFAULT_SUMMARY); } public String getSummaryLabels() { return httpMonFactory.getSummaryLabels(); } public void addSummaryLabel(String jamonSummaryLabel) { httpMonFactory.addSummaryLabel(jamonSummaryLabel); } public boolean getIgnoreHttpParams() { return httpMonFactory.getIgnoreHttpParams(); } public void setIgnoreHttpParams(boolean ignoreHttpParams) { httpMonFactory.setIgnoreHttpParams(ignoreHttpParams); } public void setEnabled(boolean enable) { httpMonFactory.setEnabled(enable); } public int getSize() { return httpMonFactory.getSize(); } public boolean getEnabled() { return httpMonFactory.getEnabled(); } public void setSize(int size) { httpMonFactory.setSize(size); } public String getInfo() { return PREFIX; } } jamonapi/src/java/com/jamontomcat/JAMonTomcat55Valve.java0000666000175000017500000001156410674051434023562 0ustar twernertwernerpackage com.jamontomcat; /** Note this is simply a copy of com.jamonapi.http.JAMonTomcatValve. Tomcat 5.5 would not work with the valve in the same jar as the jamon classes * and also display stats in jamon.war. Simply compile this class sepeartely and put it in tomcats /server/classes/com/jamontomcatvalve/http and put jamon-2.7.jar or higher in tomcats * common/lib directory. Note this class should also work in tomcat 6 although it is easier simply to put jamon-2.7.jar (or higher in the server/lib) * for tomcat 6. * * * same as default above * * * @author steve souza * */ //START tomcat 4/5 /* Note I have not tested or run TomcatValves in tomcat 4 or 5, however they will work if coded in a manner similar to the following. // tomcat 4/5 import org.apache.catalina.*; import org.apache.catalina.valves.*; // use catalina.jar from tomcat4 public class JAMonTomcat55Valve extends org.apache.catalina.valves.ValveBase { // tomcat 4/5 public void invoke(Request request, Response response, ValveContext valveContext) throws IOException, ServletException { start monitoring // tomcat 4/5 if (valveContext!=null) valveContext.invokeNext(request, response); stop monitoring } */ import javax.servlet.*; import org.apache.catalina.Valve; import java.io.IOException; //START tomcat 5.5/6 import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; import com.jamonapi.http.*; public class JAMonTomcat55Valve extends org.apache.catalina.valves.ValveBase { private static final String PREFIX="com.jamontomcat.http.JAMonTomcat55Valve"; private static final String DEFAULT_SUMMARY="default, response.getContentCount().bytes, response.getStatus().value.httpStatus, request.contextpath.ms"; HttpMonFactory httpMonFactory=new HttpMonFactory(PREFIX); private final String jamonSummaryLabels="default"; public JAMonTomcat55Valve() { setSummaryLabels(jamonSummaryLabels); } /** * Extract the desired request property, and pass it (along with the * specified request and response objects) to the protected * process() method to perform the actual filtering. * This method must be implemented by a concrete subclass. * * @param request The servlet request to be processed * @param response The servlet response to be created * * @exception IOException if an input/output error occurs * @exception ServletException if a servlet error occurs * http://www.jdocs.com/tomcat/5.5.17/org/apache/catalina/valves/RequestFilterValve.html * * log response, request to see what they do. * debug mode? * test xml - read property */ public void invoke(Request request, Response response) throws IOException, ServletException { HttpMon httpMon=null; try { httpMon=httpMonFactory.start(request, response); Valve nextValve=getNext(); if (nextValve!=null) nextValve.invoke(request, response); } catch (Throwable e) { httpMon.throwException(e); } finally { httpMon.stop(); } } public void setSummaryLabels(String jamonSummaryLabels) { httpMonFactory.setSummaryLabels(jamonSummaryLabels, DEFAULT_SUMMARY); } public String getSummaryLabels() { return httpMonFactory.getSummaryLabels(); } public void addSummaryLabel(String jamonSummaryLabel) { httpMonFactory.addSummaryLabel(jamonSummaryLabel); } public boolean getIgnoreHttpParams() { return httpMonFactory.getIgnoreHttpParams(); } public void setIgnoreHttpParams(boolean ignoreHttpParams) { httpMonFactory.setIgnoreHttpParams(ignoreHttpParams); } public void setEnabled(boolean enable) { httpMonFactory.setEnabled(enable); } public int getSize() { return httpMonFactory.getSize(); } public boolean getEnabled() { return httpMonFactory.getEnabled(); } public void setSize(int size) { httpMonFactory.setSize(size); } public String getInfo() { return PREFIX; } } jamonapi/src/java/com/jamontomcat/JAMonTomcat5Valve.java0000666000175000017500000000037310672330410023461 0ustar twernertwernerpackage com.jamontomcat; import com.jamonapi.http.*; public class JAMonTomcat5Valve extends JAMonTomcat4Valve { public JAMonTomcat5Valve() { httpMonFactory=new HttpMonFactory("com.jamontomcat.http.JAMonTomcat5Valve"); } } jamonapi/src/java/com/jamontomcat/JAMonTomcat6Valve.java0000666000175000017500000000037410672330410023463 0ustar twernertwernerpackage com.jamontomcat; import com.jamonapi.http.*; public class JAMonTomcat6Valve extends JAMonTomcat55Valve { public JAMonTomcat6Valve() { httpMonFactory=new HttpMonFactory("com.jamontomcat.http.JAMonTomcat6Valve"); } } jamonapi/src/java/com/overview.html0000666000175000017500000000773510376452606017665 0ustar twernertwerner The Java Application Monitor (JAMon v1.0) is a free, simple, high performance, thread safe, Java API that allows developers to easily monitor production applications.

JAMon can be used to determine application performance bottlenecks, user/application interactions, and application scalability. JAMon gathers summary statistics such as hits, execution times (total, average, minimum, maximum, standard deviation), simultaneous application requests and more. JAMon statistics are displayed in the JAMon report.

JAMon was developed primarily for monitoring J2EE applications, however it can be used in any JDK 1.2 or higher environment. JAMon can be used in Servlets, JSP's, EJB's and Java Beans in various J2EE Application Servers (Sybase's EAServer, and BEA's WebLogic,…), and can also be used in other programming environments that can call Java code (ColdFusion, PowerBuilder, BroadVision, ...).

JAMon Implementation details

The implementation of JAMon uses several design patterns from the book "Design Patterns Elements of Reusable Object-Oriented Software" by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. A number of the creational patterns are used to create monitors. The convention is that any class involved in the creation of monitors has the word Factory in its name. Other patterns such as the decorator, and composite patterns are also used.

Despite JAMon's relatively high number of classes, in most cases the only methods that are required to begin monitoring are MonitorFactory.add(...), MonitorFactory.start(…), Monitor.stop(), and MonitorFactory.getReport(). Most of JAMon's classes are pretty simple.

"TestClass" has sample invocations of JAMon code. I run the TestClass's main() method whenever code changes are made to ensure that errors weren't introduced. "TestClassPerformance" has various performance tests.

The decorator pattern dominates the design of JAMon. The decorator pattern is a way to chain objects with the same interface (Monitor), but different responsibilities.

When a method such as start(...) is called on the first Monitor in the chain it performs its work and calls the start(...) method on the next Monitor in the chain. The classes referenced in this decorator chain are the most important in JAMon and are responsible for tracking all JAMon statistics.

The outer most Monitor returned to the client in the decorator chain (for Timing Monitors) is unique per invocation of the start(...) method. All of the other Monitors in the chain gather summary statistics and are shared by every invocation of MonitorFactory.start(...) that is passed the same label. Because multiple threads may simultaneously attempt to update the summary statistics, Monitors are thread safe.

JAMon makes extensive use of HashMaps in storing Monitors. The label passed to "MonitorFactory.start("myMonitorLabel");" is the key to the HashMap and Monitors are the values.

JAMon consumes very few resources and is fast. On a 2 GHz Pentium IV, start() and stop() were were called 625,000 times in 1 second! JAMon can also be disabled at runtime to even further minimize impact on application performance.

JAMon follows a few conventions:

  • Interfaces are public, and classes are hidden with package access only
  • Many of the interfaces have a base class implementation that adds methods that can be used in inheritance. For example an interface in JAMon is Range, and the base class is RangeImp

For further information on JAMon look at the JAMon user's manual at www.jamonapi.com or review the source code which is also available via this site.

Steve Souza - admin@jamonapi.com (JAMon v1.0)