From de6e1de1eddc5b01750737d5009fd42fc21fc4df Mon Sep 17 00:00:00 2001 From: Michael Gorovoy Date: Mon, 22 Aug 2011 19:25:02 -0400 Subject: [PATCH] 352222 Moved JmxMonitor functionality from Codehaus --- jetty-monitor/pom.xml | 102 +++- .../org/eclipse/jetty/monitor/JMXMonitor.java | 189 +++++++ .../eclipse/jetty/monitor/ThreadMonitor.java | 2 + .../integration/JavaMonitorAction.java | 415 ++++++++++++++ .../monitor/integration/JavaMonitorTools.java | 270 ++++++++++ .../integration/JavaMonitorTrigger.java | 77 +++ .../jetty/monitor/jmx/ConsoleNotifier.java | 56 ++ .../jetty/monitor/jmx/EventNotifier.java | 34 ++ .../eclipse/jetty/monitor/jmx/EventState.java | 202 +++++++ .../jetty/monitor/jmx/EventTrigger.java | 69 +++ .../jetty/monitor/jmx/LoggingNotifier.java | 57 ++ .../jetty/monitor/jmx/MonitorAction.java | 174 ++++++ .../jetty/monitor/jmx/MonitorTask.java | 111 ++++ .../jetty/monitor/jmx/NotifierGroup.java | 115 ++++ .../jetty/monitor/jmx/ServiceConnection.java | 164 ++++++ .../jetty/monitor/jmx/SimpleAction.java | 41 ++ .../{ => thread}/ThreadMonitorException.java | 2 +- .../{ => thread}/ThreadMonitorInfo.java | 2 +- .../triggers/AggregateEventTrigger.java | 164 ++++++ .../monitor/triggers/AndEventTrigger.java | 129 +++++ .../monitor/triggers/AttrEventTrigger.java | 232 ++++++++ .../triggers/EqualToAttrEventTrigger.java | 85 +++ .../triggers/GreaterThanAttrEventTrigger.java | 87 +++ .../GreaterThanOrEqualToAttrEventTrigger.java | 87 +++ .../triggers/LessThanAttrEventTrigger.java | 87 +++ .../LessThanOrEqualToAttrEventTrigger.java | 87 +++ .../monitor/triggers/OrEventTrigger.java | 133 +++++ .../triggers/RangeAttrEventTrigger.java | 96 ++++ .../triggers/RangeInclAttrEventTrigger.java | 96 ++++ .../jmx/JavaMonitorTools-mbean.properties | 12 + .../jetty/monitor/AttrEventTriggerTest.java | 509 ++++++++++++++++++ .../monitor/JavaMonitorIntegrationTest.java | 163 ++++++ .../eclipse/jetty/monitor/JmxServiceTest.java | 160 ++++++ .../jetty/monitor/ProgramConfigTest.java | 179 ++++++ .../eclipse/jetty/monitor/RequestCounter.java | 35 ++ .../eclipse/jetty/monitor/XmlConfigTest.java | 159 ++++++ .../test/resources/monitor/etc/jetty-jmx.xml | 82 +++ .../src/test/resources/monitor/start.ini | 7 + .../monitor/java-monitor-integration.xml | 62 +++ .../jetty/monitor/jetty-monitor-service.xml | 68 +++ .../jetty/monitor/jetty-monitor-test.xml | 65 +++ .../jmx/RequestCounter-mbean.properties | 3 + 42 files changed, 4862 insertions(+), 7 deletions(-) create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/JMXMonitor.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorAction.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorTools.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorTrigger.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/ConsoleNotifier.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventNotifier.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventState.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventTrigger.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/LoggingNotifier.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/MonitorAction.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/MonitorTask.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/NotifierGroup.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/ServiceConnection.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/SimpleAction.java rename jetty-monitor/src/main/java/org/eclipse/jetty/monitor/{ => thread}/ThreadMonitorException.java (96%) rename jetty-monitor/src/main/java/org/eclipse/jetty/monitor/{ => thread}/ThreadMonitorInfo.java (99%) create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AggregateEventTrigger.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AndEventTrigger.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AttrEventTrigger.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/EqualToAttrEventTrigger.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/GreaterThanAttrEventTrigger.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/GreaterThanOrEqualToAttrEventTrigger.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/LessThanAttrEventTrigger.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/LessThanOrEqualToAttrEventTrigger.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/OrEventTrigger.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/RangeAttrEventTrigger.java create mode 100644 jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/RangeInclAttrEventTrigger.java create mode 100644 jetty-monitor/src/main/resources/org/mortbay/jetty/monitor/integration/jmx/JavaMonitorTools-mbean.properties create mode 100644 jetty-monitor/src/test/java/org/eclipse/jetty/monitor/AttrEventTriggerTest.java create mode 100644 jetty-monitor/src/test/java/org/eclipse/jetty/monitor/JavaMonitorIntegrationTest.java create mode 100644 jetty-monitor/src/test/java/org/eclipse/jetty/monitor/JmxServiceTest.java create mode 100644 jetty-monitor/src/test/java/org/eclipse/jetty/monitor/ProgramConfigTest.java create mode 100644 jetty-monitor/src/test/java/org/eclipse/jetty/monitor/RequestCounter.java create mode 100644 jetty-monitor/src/test/java/org/eclipse/jetty/monitor/XmlConfigTest.java create mode 100644 jetty-monitor/src/test/resources/monitor/etc/jetty-jmx.xml create mode 100644 jetty-monitor/src/test/resources/monitor/start.ini create mode 100644 jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/java-monitor-integration.xml create mode 100644 jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/jetty-monitor-service.xml create mode 100644 jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/jetty-monitor-test.xml create mode 100644 jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/jmx/RequestCounter-mbean.properties diff --git a/jetty-monitor/pom.xml b/jetty-monitor/pom.xml index f0da6e9f5b0..17cc6993e2d 100644 --- a/jetty-monitor/pom.xml +++ b/jetty-monitor/pom.xml @@ -1,3 +1,20 @@ + org.eclipse.jetty @@ -10,6 +27,9 @@ Performance monitoring artifact for jetty. ${project.groupId}.jmx + ${project.build.directory}/test-wars + ${project.build.directory}/test-libs + ${project.build.directory}/test-dist @@ -59,6 +79,41 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + always + + + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack-jetty-distro + process-test-resources + + unpack + + + + + org.eclipse.jetty + jetty-distribution + ${project.version} + zip + true + + + true + ${test-dist-dir} + true + true + + + + org.codehaus.mojo findbugs-maven-plugin @@ -69,15 +124,52 @@ - - junit - junit - test - org.eclipse.jetty jetty-util ${project.version} + + org.eclipse.jetty + jetty-io + ${project.version} + + + org.eclipse.jetty + jetty-http + ${project.version} + + + org.eclipse.jetty + jetty-xml + ${project.version} + + + org.eclipse.jetty + jetty-client + ${project.version} + + + org.eclipse.jetty + jetty-jmx + ${project.version} + test + + + org.eclipse.jetty + jetty-server + ${project.version} + test + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + junit + junit + test + diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/JMXMonitor.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/JMXMonitor.java new file mode 100644 index 00000000000..3fdc2220812 --- /dev/null +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/JMXMonitor.java @@ -0,0 +1,189 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + +package org.eclipse.jetty.monitor; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +import javax.management.MBeanServerConnection; + +import org.eclipse.jetty.monitor.jmx.MonitorAction; +import org.eclipse.jetty.monitor.jmx.MonitorTask; +import org.eclipse.jetty.monitor.jmx.ServiceConnection; +import org.eclipse.jetty.xml.XmlConfiguration; + +/* ------------------------------------------------------------ */ +/** + * JMXMonitor + * + * Performs monitoring of the values of the attributes of MBeans + * and executes specified actions as well as sends notifications + * of the specified events that have occurred. + */ +public class JMXMonitor +{ + private static JMXMonitor __monitor = new JMXMonitor(); + + private String _serverUrl; + private ServiceConnection _serviceConnection; + + private Set _actions = new HashSet(); + + /* ------------------------------------------------------------ */ + /** + * Constructs a JMXMonitor instance. Used for XML Configuration. + * + * !! DO NOT INSTANTIATE EXPLICITLY !! + */ + public JMXMonitor() {} + + /* ------------------------------------------------------------ */ + /** + * Adds monitor actions to the monitor + * + * @param actions monitor actions to add + * @return true if successful + */ + public boolean addActions(MonitorAction... actions) + { + return getInstance().add(actions); + } + + /* ------------------------------------------------------------ */ + /** + * Removes monitor actions from the monitor + * + * @param actions monitor actions to remove + * @return true if successful + */ + public boolean removeActions(MonitorAction... actions) + { + return getInstance().remove(actions); + } + + /* ------------------------------------------------------------ */ + /** + * Sets the JMX server URL + * + * @param url URL of the JMX server + */ + public void setUrl(String url) + { + getInstance().set(url); + } + + public MBeanServerConnection getConnection() + throws IOException + { + return getInstance().get(); + } + + public static JMXMonitor getInstance() + { + return __monitor; + } + + public static boolean addMonitorActions(MonitorAction... actions) + { + return getInstance().add(actions); + } + + public static boolean removeMonitorActions(MonitorAction... actions) + { + return getInstance().remove(actions); + } + + public static void setServiceUrl(String url) + { + getInstance().set(url); + } + + /* ------------------------------------------------------------ */ + /** + * Retrieves a connection to JMX service + * + * @return server connection + * @throws IOException + */ + public static MBeanServerConnection getServiceConnection() + throws IOException + { + return getInstance().getConnection(); + } + + public static void main(final String args[]) throws Exception + { + XmlConfiguration.main(args); + } + + private synchronized boolean add(MonitorAction... actions) + { + boolean result = true; + + for (MonitorAction action : actions) + { + if (!_actions.add(action)) + { + result = false; + } + else + { + MonitorTask.schedule(action); + } + } + + return result; + } + + private synchronized boolean remove(MonitorAction... actions) + { + boolean result = true; + + for (MonitorAction action : actions) + { + if (!_actions.remove(action)) + { + result = false; + } + + MonitorTask.cancel(action); + } + + return result; + } + + private synchronized void set(String url) + { + _serverUrl = url; + + if (_serviceConnection != null) + { + _serviceConnection.disconnect(); + _serviceConnection = null; + } + } + + private synchronized MBeanServerConnection get() + throws IOException + { + if (_serviceConnection == null) + { + _serviceConnection = new ServiceConnection(_serverUrl); + _serviceConnection.connect(); + } + + return _serviceConnection.getConnection(); + } +} diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/ThreadMonitor.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/ThreadMonitor.java index 6a63b88b13f..9badd226528 100644 --- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/ThreadMonitor.java +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/ThreadMonitor.java @@ -23,6 +23,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.eclipse.jetty.monitor.thread.ThreadMonitorException; +import org.eclipse.jetty.monitor.thread.ThreadMonitorInfo; import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.log.Log; diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorAction.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorAction.java new file mode 100644 index 00000000000..65fe1d3f0c2 --- /dev/null +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorAction.java @@ -0,0 +1,415 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + +package org.eclipse.jetty.monitor.integration; + +import static java.lang.Integer.parseInt; +import static java.lang.System.getProperty; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.Socket; +import java.net.URL; +import java.util.Collection; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import javax.management.MBeanServerConnection; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +import org.eclipse.jetty.client.ContentExchange; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.http.HttpMethods; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.io.ByteArrayBuffer; +import org.eclipse.jetty.monitor.JMXMonitor; +import org.eclipse.jetty.monitor.jmx.EventNotifier; +import org.eclipse.jetty.monitor.jmx.EventState; +import org.eclipse.jetty.monitor.jmx.EventState.TriggerState; +import org.eclipse.jetty.monitor.jmx.EventTrigger; +import org.eclipse.jetty.monitor.jmx.MonitorAction; +import org.eclipse.jetty.monitor.triggers.AggregateEventTrigger; +import org.eclipse.jetty.util.log.Log; + + +/* ------------------------------------------------------------ */ +/** + */ +public class JavaMonitorAction extends MonitorAction +{ + private final HttpClient _client; + + private final String _url; + private final String _uuid; + private final String _appid; + + private String _srvip; + private String _session; + + /* ------------------------------------------------------------ */ + /** + * @param notifier + * @param pollInterval + * @throws Exception + * @throws MalformedObjectNameException + */ + public JavaMonitorAction(EventNotifier notifier, String url, String uuid, String appid, long pollInterval) + throws Exception + { + super(new AggregateEventTrigger(),notifier,pollInterval); + + _url = url; + _uuid = uuid; + _appid = appid; + + _client = new HttpClient(); + _client.setTimeout(60000); + _client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL); + + try + { + _client.start(); + _srvip = getServerIP(); + } + catch (Exception ex) + { + Log.debug(ex); + } + + sendData(new Properties()); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.monitor.jmx.MonitorAction#execute(org.eclipse.jetty.monitor.jmx.EventTrigger, org.eclipse.jetty.monitor.jmx.EventState, long) + */ + @Override + public void execute(EventTrigger trigger, EventState state, long timestamp) + { + exec(trigger, state, timestamp); + } + + /* ------------------------------------------------------------ */ + /** + * @param trigger + * @param state + * @param timestamp + */ + private void exec(EventTrigger trigger, EventState state, long timestamp) + { + Collection> trs = state.values(); + + Properties data = new Properties(); + for (TriggerState ts : trs) + { + Object value = ts.getValue(); + + StringBuffer buffer = new StringBuffer(); + buffer.append(value == null ? "" : value.toString()); + buffer.append("|"); + buffer.append(getClassID(value)); + buffer.append("||"); + buffer.append(ts.getDescription()); + + data.setProperty(ts.getID(), buffer.toString()); + + try + { + sendData(data); + } + catch (Exception ex) + { + Log.debug(ex); + } + } + } + + /* ------------------------------------------------------------ */ + /** + * @param data + * @throws Exception + */ + private void sendData(Properties data) + throws Exception + { + data.put("account", _uuid); + data.put("appserver", "Jetty"); + data.put("localIp", _srvip); + if (_appid == null) + data.put("lowestPort", getHttpPort()); + else + data.put("lowestPort", _appid); + if (_session != null) + data.put("session", _session); + + Properties response = sendRequest(data); + + parseResponse(response); + } + + /* ------------------------------------------------------------ */ + /** + * @param request + * @return + * @throws Exception + */ + private Properties sendRequest(Properties request) + throws Exception + { + ByteArrayOutputStream reqStream = null; + ByteArrayInputStream resStream = null; + Properties response = null; + + try { + ContentExchange reqEx = new ContentExchange(); + reqEx.setURL(_url); + reqEx.setMethod(HttpMethods.POST); + reqEx.addRequestHeader("Connection","close"); + + reqStream = new ByteArrayOutputStream(); + request.storeToXML(reqStream,null); + ByteArrayBuffer reqBuff = new ByteArrayBuffer(reqStream.toByteArray()); + + reqEx.setRequestContent(reqBuff); + _client.send(reqEx); + + reqEx.waitForDone(); + + if (reqEx.getResponseStatus() == HttpStatus.OK_200) + { + response = new Properties(); + resStream = new ByteArrayInputStream(reqEx.getResponseContentBytes()); + response.loadFromXML(resStream); + } + } + finally + { + try + { + if (reqStream != null) + reqStream.close(); + } + catch (IOException ex) + { + Log.ignore(ex); + } + + try + { + if (resStream != null) + resStream.close(); + } + catch (IOException ex) + { + Log.ignore(ex); + } + } + + return response; + } + + /* ------------------------------------------------------------ */ + private void parseResponse(Properties response) + { + if (response.get("onhold") != null) + throw new Error("Suspended"); + + + if (response.get("session") != null) + { + _session = (String) response.remove("session"); + + AggregateEventTrigger trigger = (AggregateEventTrigger)getTrigger(); + + String queryString; + ObjectName[] queryResults; + for (Map.Entry entry : response.entrySet()) + { + String[] values = ((String) entry.getValue()).split("\\|"); + + queryString = values[0]; + if (queryString.startsWith("com.javamonitor.openfire")) + continue; + + if (queryString.startsWith("com.javamonitor")) + { + queryString = "org.eclipse.jetty.monitor.integration:type=javamonitortools,id=0"; + } + + queryResults = null; + try + { + queryResults = queryNames(queryString); + } + catch (IOException e) + { + Log.debug(e); + } + catch (MalformedObjectNameException e) + { + Log.debug(e); + } + + if (queryResults != null) + { + int idx = 0; + for(ObjectName objName : queryResults) + { + String id = entry.getKey().toString()+(idx == 0 ? "" : ":"+idx); + String name = queryString.equals(objName.toString()) ? "" : objName.toString(); + boolean repeat = Boolean.parseBoolean(values[2]); + trigger.add(new JavaMonitorTrigger(objName, values[1], id, name, repeat)); + } + } + } + } + } + + /* ------------------------------------------------------------ */ + /** + * @param value + * @return + */ + private int getClassID(final Object value) + { + if (value == null) + return 0; + + if (value instanceof Byte || + value instanceof Short || + value instanceof Integer || + value instanceof Long) + return 1; + + if (value instanceof Float || + value instanceof Double) + return 2; + + if (value instanceof Boolean) + return 3; + + return 4; // String + } + + /* ------------------------------------------------------------ */ + /** + * @return + * @throws Exception + */ + private String getServerIP() + throws Exception + { + Socket s = null; + try { + if (getProperty("http.proxyHost") != null) + { + s = new Socket(getProperty("http.proxyHost"), + parseInt(getProperty("http.proxyPort", "80"))); + } + else + { + int port = 80; + + URL url = new URL(_url); + if (url.getPort() != -1) { + port = url.getPort(); + } + s = new Socket(url.getHost(), port); + } + return s.getLocalAddress().getHostAddress(); + } + finally + { + try + { + if (s != null) + s.close(); + } + catch (IOException ex) + { + Log.ignore(ex); + } + } + } + + /* ------------------------------------------------------------ */ + public Integer getHttpPort() + { + Collection connectors = null; + MBeanServerConnection service; + try + { + service = JMXMonitor.getServiceConnection(); + + connectors = service.queryNames(new ObjectName("org.eclipse.jetty.nio:type=selectchannelconnector,*"), null); + if (connectors != null && connectors.size() > 0) + { + Integer lowest = Integer.MAX_VALUE; + for (final ObjectName connector : connectors) { + lowest = (Integer)service.getAttribute(connector, "port"); + } + + if (lowest < Integer.MAX_VALUE) + return lowest; + } + } + catch (Exception ex) + { + Log.debug(ex); + } + + return 0; + } + + /* ------------------------------------------------------------ */ + /** + * @param param + * @return + * @throws IOException + * @throws NullPointerException + * @throws MalformedObjectNameException + */ + private ObjectName[] queryNames(ObjectName param) + throws IOException, MalformedObjectNameException + { + ObjectName[] result = null; + + MBeanServerConnection connection = JMXMonitor.getServiceConnection(); + Set names = connection.queryNames(param, null); + if (names != null && names.size() > 0) + { + result = new ObjectName[names.size()]; + + int idx = 0; + for(Object name : names) + { + if (name instanceof ObjectName) + result[idx++] = (ObjectName)name; + else + result[idx++] = new ObjectName(name.toString()); + } + } + + return result; + } + + private ObjectName[] queryNames(String param) + throws IOException, MalformedObjectNameException + { + return queryNames(new ObjectName(param)); + } + + } diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorTools.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorTools.java new file mode 100644 index 00000000000..a8d088622ca --- /dev/null +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorTools.java @@ -0,0 +1,270 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + +package org.eclipse.jetty.monitor.integration; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.Security; +import java.util.HashMap; +import java.util.Map; + +/* ------------------------------------------------------------ */ +/** + * Derived from the JMX bean classes created by Kees Jan Koster for the java-monitor + * J2EE probe http://code.google.com/p/java-monitor-probes/source/browse/. + * + * @author kjkoster + */ +public class JavaMonitorTools +{ + private static final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); + + private static Method findDeadlockMethod = null; + + static + { + try + { + findDeadlockMethod = ThreadMXBean.class.getMethod("findDeadlockedThreads"); + } + catch (Exception ignored) + { + // this is a 1.5 JVM + try + { + findDeadlockMethod = ThreadMXBean.class.getMethod("findMonitorDeadlockedThreads"); + } + catch (SecurityException e) + { + e.printStackTrace(); + } + catch (NoSuchMethodException e) + { + e.printStackTrace(); + } + } + } + + private ThreadInfo[] findDeadlock() + throws IllegalAccessException, InvocationTargetException + { + final long[] threadIds = (long[])findDeadlockMethod.invoke(threadMXBean,(Object[])null); + + if (threadIds == null || threadIds.length < 1) + { + // no deadlock, we're done + return null; + } + + final ThreadInfo[] threads = threadMXBean.getThreadInfo(threadIds,Integer.MAX_VALUE); + return threads; + } + + public String getDeadlockStacktraces() + { + try + { + final ThreadInfo[] threads = findDeadlock(); + if (threads == null) + { + // no deadlock, we're done + return null; + } + + return stacktraces(threads,0); + } + catch (Exception e) + { + return e.getMessage(); + } + } + + private static final int MAX_STACK = 10; + + private String stacktraces(final ThreadInfo[] threads, final int i) + { + if (i >= threads.length) + { + return ""; + } + final ThreadInfo thread = threads[i]; + + final StringBuilder trace = new StringBuilder(); + for (int stack_i = 0; stack_i < Math.min(thread.getStackTrace().length,MAX_STACK); stack_i++) + { + if (stack_i == (MAX_STACK - 1)) + { + trace.append(" ..."); + } + else + { + trace.append(" at ").append(thread.getStackTrace()[stack_i]).append("\n"); + } + } + + return "\"" + thread.getThreadName() + "\", id " + thread.getThreadId() + " is " + thread.getThreadState() + " on " + thread.getLockName() + + ", owned by " + thread.getLockOwnerName() + ", id " + thread.getLockOwnerId() + "\n" + trace + "\n\n" + stacktraces(threads,i + 1); + } + + /** + * We keep track of the last time we sampled the thread states. + * It is a crude optimization to avoid having to query for the + * threads states very often. + */ + private long lastSampled = 0L; + + private final Map states = new HashMap(); + + public int getThreadsBlocked() + { + sampleThreads(); + + return states.get(Thread.State.BLOCKED); + } + + public int getThreadsNew() + { + sampleThreads(); + + return states.get(Thread.State.NEW); + } + + public int getThreadsTerminated() + { + sampleThreads(); + + return states.get(Thread.State.TERMINATED); + } + + public int getThreadsTimedWaiting() + { + sampleThreads(); + + return states.get(Thread.State.TIMED_WAITING); + } + + public int getThreadsWaiting() + { + sampleThreads(); + + return states.get(Thread.State.WAITING); + } + + public int getThreadsRunnable() + { + sampleThreads(); + + return states.get(Thread.State.RUNNABLE); + } + + private synchronized void sampleThreads() + { + if ((lastSampled + 50L) < System.currentTimeMillis()) + { + lastSampled = System.currentTimeMillis(); + for (final Thread.State state : Thread.State.values()) + { + states.put(state,0); + } + + for (final ThreadInfo thread : threadMXBean.getThreadInfo(threadMXBean.getAllThreadIds())) + { + if (thread != null) + { + final Thread.State state = thread.getThreadState(); + states.put(state,states.get(state) + 1); + } + else + { + states.put(Thread.State.TERMINATED,states.get(Thread.State.TERMINATED) + 1); + } + } + } + } + + private static final String POLICY = "sun.net.InetAddressCachePolicy"; + + public int getCacheSeconds() throws ClassNotFoundException, + IllegalAccessException, InvocationTargetException, + NoSuchMethodException { + final Class policy = Class.forName(POLICY); + final Object returnValue = policy.getMethod("get", (Class[]) null) + .invoke(null, (Object[]) null); + Integer seconds = (Integer) returnValue; + + return seconds.intValue(); + } + + public int getCacheNegativeSeconds() throws ClassNotFoundException, + IllegalAccessException, InvocationTargetException, + NoSuchMethodException { + final Class policy = Class.forName(POLICY); + final Object returnValue = policy.getMethod("getNegative", + (Class[]) null).invoke(null, (Object[]) null); + Integer seconds = (Integer) returnValue; + + return seconds.intValue(); + } + + private static final String DEFAULT = "default"; + + private static final String SECURITY = "security"; + + private static final String SYSTEM = "system"; + + private static final String BOTH = "both"; + + private static final String SECURITY_TTL = "networkaddress.cache.ttl"; + + private static final String SYSTEM_TTL = "sun.net.inetaddr.ttl"; + + private static final String SECURITY_NEGATIVE_TTL = "networkaddress.cache.negative.ttl"; + + private static final String SYSTEM_NEGATIVE_TTL = "sun.net.inetaddr.negative.ttl"; + + public String getCacheTweakedFrom() { + if (Security.getProperty(SECURITY_TTL) != null) { + if (System.getProperty(SYSTEM_TTL) != null) { + return BOTH; + } + + return SECURITY; + } + + if (System.getProperty(SYSTEM_TTL) != null) { + return SYSTEM; + } + + return DEFAULT; + } + + public String getCacheNegativeTweakedFrom() { + if (Security.getProperty(SECURITY_NEGATIVE_TTL) != null) { + if (System.getProperty(SYSTEM_NEGATIVE_TTL) != null) { + return BOTH; + } + + return SECURITY; + } + + if (System.getProperty(SYSTEM_NEGATIVE_TTL) != null) { + return SYSTEM; + } + + return DEFAULT; + } +} diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorTrigger.java new file mode 100644 index 00000000000..64f6252ecff --- /dev/null +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorTrigger.java @@ -0,0 +1,77 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + +package org.eclipse.jetty.monitor.integration; + +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +import org.eclipse.jetty.monitor.triggers.AttrEventTrigger; + + +/* ------------------------------------------------------------ */ +/** + */ +public class JavaMonitorTrigger > + extends AttrEventTrigger +{ + private final String _id; + private final String _name; + private final boolean _dynamic; + private int _count; + + /* ------------------------------------------------------------ */ + /** + * @param nameObject + * @param attributeName + * @param id + * @param dynamic + * @throws IllegalArgumentException + */ + public JavaMonitorTrigger(ObjectName nameObject, String attributeName, String id, String name, boolean dynamic) + throws IllegalArgumentException + { + super(nameObject, attributeName); + + _id = id; + _name = name; + _dynamic = dynamic; + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.monitor.triggers.AttrEventTrigger#match(java.lang.Comparable) + */ + @Override + public boolean match(Comparable value) + { + return _dynamic ? true : (_count++ < 1); + } + + protected boolean getSaveAll() + { + return false; + } + + @Override + public String getID() + { + return _id; + } + + @Override + public String getNameString() + { + return _name; + } +} diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/ConsoleNotifier.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/ConsoleNotifier.java new file mode 100644 index 00000000000..9e03bfe0f68 --- /dev/null +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/ConsoleNotifier.java @@ -0,0 +1,56 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + +package org.eclipse.jetty.monitor.jmx; + + +/* ------------------------------------------------------------ */ +/** + * ConsoleNotifier + * + * Provides a way to output notification messages to the server console + */ +public class ConsoleNotifier implements EventNotifier +{ + String _messageFormat; + + + /* ------------------------------------------------------------ */ + /** + * Constructs a new notifier with specified format string + * + * @param format the {@link java.util.Formatter format string} + * @throws IllegalArgumentException + */ + public ConsoleNotifier(String format) + throws IllegalArgumentException + { + if (format == null) + throw new IllegalArgumentException("Message format cannot be null"); + + _messageFormat = format; + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.monitor.jmx.EventNotifier#notify(org.eclipse.jetty.monitor.jmx.EventTrigger, org.eclipse.jetty.monitor.jmx.EventState, long) + */ + public void notify(EventTrigger trigger, EventState state, long timestamp) + { + String output = String.format("%1$tF %1$tT.%1$tL:NOTIFY::", timestamp); + + output += String.format(_messageFormat, state); + + System.out.println(output); + } +} diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventNotifier.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventNotifier.java new file mode 100644 index 00000000000..f9d7fdc777b --- /dev/null +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventNotifier.java @@ -0,0 +1,34 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + +package org.eclipse.jetty.monitor.jmx; + + +/* ------------------------------------------------------------ */ +/** + * EventNotifier + * + * Interface for classes used to send event notifications + */ +public interface EventNotifier +{ + + /* ------------------------------------------------------------ */ + /** + * This method is called when a notification event is received by the containing object + * + * @param state an {@link org.eclipse.jetty.monitor.jmx.EventState event state} + * @param timestamp time stamp of the event + */ + public void notify(EventTrigger trigger, EventState state, long timestamp); +} diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventState.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventState.java new file mode 100644 index 00000000000..393617ac1dc --- /dev/null +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventState.java @@ -0,0 +1,202 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + +package org.eclipse.jetty.monitor.jmx; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + + +/* ------------------------------------------------------------ */ +/** + * EventState + * + * Holds the state of one or more {@link org.eclipse.jetty.monitor.jmx.EventTrigger event trigger} + * instances to be used when sending notifications as well as executing the actions + */ +public class EventState +{ + + /* ------------------------------------------------------------ */ + /** + * State + * + * Holds the state of a single {@link org.eclipse.jetty.monitor.jmx.EventTrigger event trigger} + */ + public static class TriggerState + { + private final String _id; + private final String _desc; + private final TYPE _value; + + /* ------------------------------------------------------------ */ + /** + * Construct a trigger state + * + * @param id unique identification string of the associated event trigger + * @param desc description of the associated event trigger + * @param value effective value of the MXBean attribute (if applicable) + */ + public TriggerState(String id, String desc, TYPE value) + { + _id = id; + _desc = desc; + _value = value; + } + + /* ------------------------------------------------------------ */ + /** + * Retrieve the identification string of associated event trigger + * + * @return unique identification string + */ + public String getID() + { + return _id; + } + + /* ------------------------------------------------------------ */ + /** + * Retrieve the description string set by event trigger + * + * @return description string + */ + public String getDescription() + { + return _desc; + } + + /* ------------------------------------------------------------ */ + /** + * Retrieve the effective value of the MXBean attribute (if applicable) + * + * @return attribute value + */ + public TYPE getValue() + { + return _value; + } + + /* ------------------------------------------------------------ */ + /** + * @return string representation of the state + */ + public String toString() + { + StringBuilder result = new StringBuilder(); + + result.append(_desc); + result.append('='); + result.append(_value); + + return result.toString(); + } + } + + protected Map> _states; + + /* ------------------------------------------------------------ */ + /** + * Constructs an empty event state + */ + public EventState() + { + _states = new ConcurrentHashMap>(); + } + + + /* ------------------------------------------------------------ */ + /** + * Constructs an event state and adds a specified trigger state to it + * + * @param id unique identification string of the associated event trigger + * @param desc description of the associated event trigger + * @param value effective value of the MXBean attribute (if applicable) + */ + public EventState(String id, String desc, TYPE value) + { + this(); + + add(new TriggerState(id, desc, value)); + } + + /* ------------------------------------------------------------ */ + /** + * Adds a trigger state to the event state + * + * @param state trigger state to add + */ + public void add(TriggerState state) + { + _states.put(state.getID(), state); + } + + /* ------------------------------------------------------------ */ + /** + * Adds a collection of trigger states to the event state + * + * @param entries collection of trigger states to add + */ + public void addAll(Collection> entries) + { + for (TriggerState entry : entries) + { + add(entry); + } + } + + /* ------------------------------------------------------------ */ + /** + * Retrieves a single trigger state + * + * @param id unique identification string of the event trigger + * @return requested trigger state or null if not found + */ + public TriggerState get(String id) + { + return _states.get(id); + } + + /* ------------------------------------------------------------ */ + /** + * Retrieves a collection of all trigger states of the event state + * + * @return collection of the trigger states + */ + public Collection> values() + { + return Collections.unmodifiableCollection(_states.values()); + } + + /* ------------------------------------------------------------ */ + /** + * Returns a string representation of the event state + * + * @return string representation of the event state + */ + public String toString() + { + int cnt = 0; + StringBuilder result = new StringBuilder(); + + for (TriggerState value : _states.values()) + { + result.append(cnt++>0?"#":""); + result.append(value.toString()); + } + + return result.toString(); + } +} diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventTrigger.java new file mode 100644 index 00000000000..27687dd20fb --- /dev/null +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventTrigger.java @@ -0,0 +1,69 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + + +package org.eclipse.jetty.monitor.jmx; + +import static java.util.UUID.randomUUID; + +/* ------------------------------------------------------------ */ +/** + * EventTrigger + * + * Abstract base class for all EventTrigger implementations. + * Used to determine whether the necessary conditions for + * triggering an event are present. + */ +public abstract class EventTrigger +{ + private final String _id; + + /* ------------------------------------------------------------ */ + /** + * Construct an event trigger + */ + public EventTrigger() + { + _id = randomUUID().toString(); + } + + /* ------------------------------------------------------------ */ + /** + * Retrieve the identification string of the event trigger + * + * @return unique identification string + */ + public String getID() + { + return _id; + } + + /* ------------------------------------------------------------ */ + /** + * Abstract method to verify if the event trigger conditions + * are in the appropriate state for an event to be triggered + * + * @return true to trigger an event + */ + public abstract boolean match(long timestamp) throws Exception; + + /* ------------------------------------------------------------ */ + /** + * Retrieve the event state associated with specified invocation + * of the event trigger match method + * + * @param timestamp time stamp associated with invocation + * @return event state or null if not found + */ + public abstract EventState getState(long timestamp); +} diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/LoggingNotifier.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/LoggingNotifier.java new file mode 100644 index 00000000000..38296e552cf --- /dev/null +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/LoggingNotifier.java @@ -0,0 +1,57 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + + +package org.eclipse.jetty.monitor.jmx; + +import org.eclipse.jetty.util.log.Log; + + +/* ------------------------------------------------------------ */ +/** + * ConsoleNotifier + * + * Provides a way to output notification messages to a log file + */ +public class LoggingNotifier implements EventNotifier +{ + String _messageFormat; + + /* ------------------------------------------------------------ */ + /** + * Constructs a new notifier with specified format string + * + * @param format the {@link java.util.Formatter format string} + * @throws IllegalArgumentException + */ + public LoggingNotifier(String format) + throws IllegalArgumentException + { + if (format == null) + throw new IllegalArgumentException("Message format cannot be null"); + + _messageFormat = format; + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.monitor.jmx.EventNotifier#notify(org.eclipse.jetty.monitor.jmx.EventTrigger, org.eclipse.jetty.monitor.jmx.EventState, long) + */ + public void notify(EventTrigger trigger, EventState state, long timestamp) + { + String output = String.format(_messageFormat, state); + + Log.info(output); + } + +} diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/MonitorAction.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/MonitorAction.java new file mode 100644 index 00000000000..0748ec90d6e --- /dev/null +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/MonitorAction.java @@ -0,0 +1,174 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + +package org.eclipse.jetty.monitor.jmx; + +import static java.util.UUID.randomUUID; + +import java.security.InvalidParameterException; + +/* ------------------------------------------------------------ */ +/** + * MonitorAction + * + * Abstract base class for all MonitorAction implementations. + * Receives notification when an associated EventTrigger is matched. + */ +public abstract class MonitorAction + extends NotifierGroup +{ + public static final int DEFAULT_POLL_INTERVAL = 5000; + + private final String _id; + private final EventTrigger _trigger; + private final EventNotifier _notifier; + private final long _pollInterval; + private final long _pollDelay; + + /* ------------------------------------------------------------ */ + /** + * Creates a new monitor action + * + * @param trigger event trigger to be associated with this action + * @throws InvalidParameterException + */ + public MonitorAction(EventTrigger trigger) + throws InvalidParameterException + { + this(trigger, null, 0, 0); + } + + + /* ------------------------------------------------------------ */ + /** + * Creates a new monitor action + * + * @param trigger event trigger to be associated with this action + * @param notifier event notifier to be associated with this action + * @throws InvalidParameterException + */ + public MonitorAction(EventTrigger trigger, EventNotifier notifier) + throws InvalidParameterException + { + this(trigger, notifier, 0); + } + + /* ------------------------------------------------------------ */ + /** + * Creates a new monitor action + * + * @param trigger event trigger to be associated with this action + * @param notifier event notifier to be associated with this action + * @param pollInterval interval for polling of the JMX server + * @throws InvalidParameterException + */ + public MonitorAction(EventTrigger trigger, EventNotifier notifier, long pollInterval) + throws InvalidParameterException + { + this(trigger, notifier, pollInterval, 0); + } + + /* ------------------------------------------------------------ */ + /** + * Creates a new monitor action + * + * @param trigger event trigger to be associated with this action + * @param notifier event notifier to be associated with this action + * @param pollInterval interval for polling of the JMX server + * @param pollDelay delay before starting to poll the JMX server + * @throws InvalidParameterException + */ + public MonitorAction(EventTrigger trigger, EventNotifier notifier, long pollInterval, long pollDelay) + throws InvalidParameterException + { + if (trigger == null) + throw new InvalidParameterException("Trigger cannot be null"); + + _id = randomUUID().toString(); + _trigger = trigger; + _notifier = notifier; + _pollInterval = pollInterval > 0 ? pollInterval : DEFAULT_POLL_INTERVAL; + _pollDelay = pollDelay > 0 ? pollDelay : _pollInterval; + } + + /* ------------------------------------------------------------ */ + /** + * Retrieve the identification string of the monitor action + * + * @return unique identification string + */ + + public final String getID() + { + return _id; + } + + + /* ------------------------------------------------------------ */ + /** + * Retrieve the event trigger of the monitor action + * + * @return associated event trigger + */ + public EventTrigger getTrigger() + { + return _trigger; + } + + /* ------------------------------------------------------------ */ + /** + * Retrieve the poll interval + * + * @return interval value (in milliseconds) + */ + public long getPollInterval() + { + return _pollInterval; + } + + + /* ------------------------------------------------------------ */ + /** Retrieve the poll delay + * @return delay value (in milliseconds) + */ + public long getPollDelay() + { + return _pollDelay; + } + + /* ------------------------------------------------------------ */ + /** + * This method will be called when event trigger associated + * with this monitor action matches its conditions. + * + * @param timestamp time stamp of the event + */ + public final void doExecute(long timestamp) + { + EventState state =_trigger.getState(timestamp); + if (_notifier != null) + _notifier.notify(_trigger, state, timestamp); + execute(_trigger, state, timestamp); + } + + /* ------------------------------------------------------------ */ + /** + * This method will be called to allow subclass to execute + * the desired action in response to the event. + * + * @param trigger event trigger associated with this monitor action + * @param state event state associated with current invocation of event trigger + * @param timestamp time stamp of the current invocation of event trigger + */ + public abstract void execute(EventTrigger trigger, EventState state, long timestamp); + } diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/MonitorTask.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/MonitorTask.java new file mode 100644 index 00000000000..1c3220544c2 --- /dev/null +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/MonitorTask.java @@ -0,0 +1,111 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + +package org.eclipse.jetty.monitor.jmx; + +import java.util.HashMap; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.thread.ExecutorThreadPool; +import org.eclipse.jetty.util.thread.ThreadPool; + +/* ------------------------------------------------------------ */ +/** + * MonitorTask + * + * Invokes polling of the JMX server for the MBean attribute values + * through executing timer task scheduled using java.util.Timer + * at specified poll interval following a specified delay. + */ +public class MonitorTask extends TimerTask +{ + private static Timer __timer = new Timer(true); + private static ThreadPool _callback = new ExecutorThreadPool(4,64,60,TimeUnit.SECONDS);; + private static Map __tasks = new HashMap(); + + private final MonitorAction _action; + + /* ------------------------------------------------------------ */ + /** + * Creates new instance of MonitorTask + * + * @param action instance of MonitorAction to use + */ + private MonitorTask(MonitorAction action) + { + _action = action; + } + + /* ------------------------------------------------------------ */ + /** + * Schedule new timer task for specified monitor action + * + * @param action monitor action + */ + public static void schedule(MonitorAction action) + { + TimerTask task = new MonitorTask(action); + __timer.scheduleAtFixedRate(task, + action.getPollDelay(), + action.getPollInterval()); + + __tasks.put(action.getID(), task); + } + + /* ------------------------------------------------------------ */ + /** + * Cancel timer task for specified monitor action + * + * @param action monitor action + */ + public static void cancel(MonitorAction action) + { + TimerTask task = __tasks.remove(action.getID()); + if (task != null) + task.cancel(); + } + + /* ------------------------------------------------------------ */ + /** + * This method is invoked when poll interval has elapsed + * to check if the event trigger conditions are satisfied + * in order to fire event. + * + * @see java.util.TimerTask#run() + */ + @Override + public final void run() + { + final long timestamp = System.currentTimeMillis(); + final EventTrigger trigger = _action.getTrigger(); + + _callback.dispatch(new Runnable() { + public void run() + { + try + { + if(trigger.match(timestamp)) + _action.doExecute(timestamp); + } + catch (Exception ex) + { + Log.debug(ex); + } + } + }); + } +} diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/NotifierGroup.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/NotifierGroup.java new file mode 100644 index 00000000000..31e85ce9bf5 --- /dev/null +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/NotifierGroup.java @@ -0,0 +1,115 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + + +package org.eclipse.jetty.monitor.jmx; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + + + +/* ------------------------------------------------------------ */ +/** + * NotifierGroup + * + * This class allows for grouping of the event notifiers + */ +public class NotifierGroup implements EventNotifier +{ + private Set _group; + + /* ------------------------------------------------------------ */ + /** + * Create a notifier group + */ + public NotifierGroup() + { + _group = new HashSet(); + } + + /* ------------------------------------------------------------ */ + /** + * Retrieve all event notifier associated with this group + * + * @return collection of event notifiers + */ + public Collection getNotifiers() + { + return Collections.unmodifiableSet(_group); + } + + /* ------------------------------------------------------------ */ + /** + * Add specified event notifier to event notifier group + * + * @param notifier event notifier to be added + * @return true if successful + */ + public boolean addNotifier(EventNotifier notifier) + { + return _group.add(notifier); + } + + /* ------------------------------------------------------------ */ + /** + * Add a collection of event notifiers to event notifier group + * + * @param notifiers collection of event notifiers to be added + * @return true if successful + */ + public boolean addNotifiers(Collection notifiers) + { + return _group.addAll(notifiers); + } + + /* ------------------------------------------------------------ */ + /** + * Remove event notifier from event notifier group + * + * @param notifier event notifier to be removed + * @return true if successful + */ + public boolean removeNotifier(EventNotifier notifier) + { + return _group.remove(notifier); + } + + /* ------------------------------------------------------------ */ + /** + * Remove a collection of event notifiers from event notifier group + * + * @param notifiers collection of event notifiers to be removed + * @return true if successful + */ + public boolean removeNotifiers(Collection notifiers) + { + return _group.removeAll(notifiers); + } + + /* ------------------------------------------------------------ */ + /** + * Invoke the notify() method of each of the notifiers in group + * + * @see org.eclipse.jetty.monitor.jmx.EventNotifier#notify(org.eclipse.jetty.monitor.jmx.EventTrigger, org.eclipse.jetty.monitor.jmx.EventState, long) + */ + public void notify(EventTrigger trigger, EventState state, long timestamp) + { + for (EventNotifier notifier: _group) + { + notifier.notify(trigger, state, timestamp); + } + } +} diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/ServiceConnection.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/ServiceConnection.java new file mode 100644 index 00000000000..fa0bef139fe --- /dev/null +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/ServiceConnection.java @@ -0,0 +1,164 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + +package org.eclipse.jetty.monitor.jmx; + +import java.io.IOException; +import java.lang.management.ManagementFactory; + +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +import org.eclipse.jetty.util.log.Log; + + +/* ------------------------------------------------------------ */ +/** + * ServerConnection + * + * Provides ability to create a connection to either an external + * JMX server, or a loopback connection to the internal one. + */ +public class ServiceConnection +{ + private String _serviceUrl; + private MBeanServer _server; + private JMXConnectorServer _connectorServer; + private JMXConnector _serverConnector; + private MBeanServerConnection _serviceConnection; + + /* ------------------------------------------------------------ */ + /** + * Construct a loopback connection to an internal server + * + * @throws IOException + */ + public ServiceConnection() + throws IOException + { + this(null); + } + + /* ------------------------------------------------------------ */ + /** + * Construct a connection to specified server + * + * @param url URL of JMX server + * @throws IOException + */ + public ServiceConnection(String url) + throws IOException + { + _serviceUrl = url; + } + + /** + * Retrieve an external URL for the JMX server + * + * @return service URL + */ + public String getServiceUrl() + { + return _serviceUrl; + } + + /* ------------------------------------------------------------ */ + /** + * Retrieve a connection to MBean server + * + * @return connection to MBean server + */ + public MBeanServerConnection getConnection() + { + return _serviceConnection; + } + + public void connect() + throws IOException + { + if (_serviceConnection == null) + { + if (_serviceUrl == null) + openLoopbackConnection(); + else + openServerConnection(_serviceUrl); + } + } + /* ------------------------------------------------------------ */ + /** + * Open a loopback connection to local JMX server + * + * @throws IOException + */ + private void openLoopbackConnection() + throws IOException + { + _server = ManagementFactory.getPlatformMBeanServer(); + + JMXServiceURL serviceUrl = new JMXServiceURL("service:jmx:rmi://"); + _connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(serviceUrl, null, _server); + _connectorServer.start(); + + _serviceUrl = _connectorServer.getAddress().toString(); + + _serverConnector = JMXConnectorFactory.connect(_connectorServer.getAddress()); + _serviceConnection = _serverConnector.getMBeanServerConnection(); + } + + /* ------------------------------------------------------------ */ + /** + * Open a connection to remote JMX server + * + * @param url + * @throws IOException + */ + private void openServerConnection(String url) + throws IOException + { + _serviceUrl = url; + + JMXServiceURL serviceUrl = new JMXServiceURL(_serviceUrl); + _serverConnector = JMXConnectorFactory.connect(serviceUrl); + _serviceConnection = _serverConnector.getMBeanServerConnection(); + } + + /* ------------------------------------------------------------ */ + /** + * Close the connections + */ + public void disconnect() + { + try + { + if (_serverConnector != null) + { + _serverConnector.close(); + _serviceConnection = null; + } + if (_connectorServer != null) + { + _connectorServer.stop(); + _connectorServer = null; + } + } + catch (Exception ex) + { + Log.debug(ex); + } + } +} diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/SimpleAction.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/SimpleAction.java new file mode 100644 index 00000000000..4de09e304cd --- /dev/null +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/SimpleAction.java @@ -0,0 +1,41 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + +package org.eclipse.jetty.monitor.jmx; + +import java.security.InvalidParameterException; + + + +/* ------------------------------------------------------------ */ +/** + */ +public class SimpleAction extends MonitorAction +{ + public SimpleAction(EventTrigger trigger, EventNotifier notifier, long pollInterval) + throws InvalidParameterException + { + super(trigger,notifier,pollInterval); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.monitor.jmx.MonitorAction#execute(org.eclipse.jetty.monitor.jmx.EventTrigger, org.eclipse.jetty.monitor.jmx.EventState, long) + */ + + @Override + public void execute(EventTrigger trigger, EventState state, long timestamp) + { + System.out.printf("Action time: %tc%n", timestamp); + } +} diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/ThreadMonitorException.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/thread/ThreadMonitorException.java similarity index 96% rename from jetty-monitor/src/main/java/org/eclipse/jetty/monitor/ThreadMonitorException.java rename to jetty-monitor/src/main/java/org/eclipse/jetty/monitor/thread/ThreadMonitorException.java index 50881116538..b457dc03ab9 100644 --- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/ThreadMonitorException.java +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/thread/ThreadMonitorException.java @@ -12,7 +12,7 @@ // ======================================================================== -package org.eclipse.jetty.monitor; +package org.eclipse.jetty.monitor.thread; /* ------------------------------------------------------------ */ diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/ThreadMonitorInfo.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/thread/ThreadMonitorInfo.java similarity index 99% rename from jetty-monitor/src/main/java/org/eclipse/jetty/monitor/ThreadMonitorInfo.java rename to jetty-monitor/src/main/java/org/eclipse/jetty/monitor/thread/ThreadMonitorInfo.java index 0ac4bea9af1..ef2b71d1151 100644 --- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/ThreadMonitorInfo.java +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/thread/ThreadMonitorInfo.java @@ -12,7 +12,7 @@ // ======================================================================== -package org.eclipse.jetty.monitor; +package org.eclipse.jetty.monitor.thread; /* ------------------------------------------------------------ */ diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AggregateEventTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AggregateEventTrigger.java new file mode 100644 index 00000000000..c590152f6d4 --- /dev/null +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AggregateEventTrigger.java @@ -0,0 +1,164 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + + +package org.eclipse.jetty.monitor.triggers; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jetty.monitor.jmx.EventState; +import org.eclipse.jetty.monitor.jmx.EventTrigger; + + +/* ------------------------------------------------------------ */ +/** + * AggregateEventTrigger + * + * EventTrigger aggregation that executes every aggregated event + * triggers in left to right order, and returns match if any one + * of them have returned match. + */ +public class AggregateEventTrigger extends EventTrigger +{ + protected final List _triggers; + + /* ------------------------------------------------------------ */ + /** + * Construct an event trigger + */ + public AggregateEventTrigger() + { + _triggers = new ArrayList(); + } + + /* ------------------------------------------------------------ */ + /** + * Construct an event trigger and associate the list + * of event triggers to be aggregated by this trigger + * + * @param triggers list of event triggers to add + */ + public AggregateEventTrigger(List triggers) + { + _triggers = new ArrayList(triggers); + } + + /* ------------------------------------------------------------ */ + /** + * Construct an event trigger and associate the array + * of event triggers to be aggregated by this trigger + * + * @param triggers list of event triggers to add + */ + public AggregateEventTrigger(EventTrigger... triggers) + { + _triggers = Arrays.asList(triggers); + } + + /* ------------------------------------------------------------ */ + /** + * @param trigger + */ + public void add(EventTrigger trigger) + { + _triggers.add(trigger); + } + + /* ------------------------------------------------------------ */ + /** + * @param triggers + */ + public void addAll(List triggers) + { + _triggers.addAll(triggers); + } + + /* ------------------------------------------------------------ */ + /** + * @param triggers + */ + public void addAll(EventTrigger... triggers) + { + _triggers.addAll(Arrays.asList(triggers)); + } + + /* ------------------------------------------------------------ */ + /** + * Retrieve the event state associated with specified invocation + * of the event trigger match method. This event trigger retrieves + * the combined event state of all aggregated event triggers. + * + * @param timestamp time stamp associated with invocation + * @return event state or null if not found + * + * @see org.eclipse.jetty.monitor.jmx.EventTrigger#getState(long) + */ + @Override + public EventState getState(long timestamp) + { + EventState state = new EventState(); + + for (EventTrigger trigger : _triggers) + { + EventState subState = trigger.getState(timestamp); + if (subState != null) + { + state.addAll(subState.values()); + } + } + + return state; + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.monitor.jmx.EventTrigger#match(long) + */ + @Override + public boolean match(long timestamp) throws Exception + { + boolean result = false; + for(EventTrigger trigger : _triggers) + { + result = trigger.match(timestamp) ? true : result; + } + return true; + } + + /* ------------------------------------------------------------ */ + /** + * Returns the string representation of this event trigger + * in the format "AND(triger1,trigger2,...)". + * + * @return string representation of the event trigger + * + * @see java.lang.Object#toString() + */ + public String toString() + { + int cnt = 0; + StringBuilder result = new StringBuilder(); + + result.append("ANY("); + for (EventTrigger trigger : _triggers) + { + result.append(cnt++ > 0 ? "," : ""); + result.append(trigger); + } + result.append(')'); + + return result.toString(); + } +} diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AndEventTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AndEventTrigger.java new file mode 100644 index 00000000000..c2910b31e97 --- /dev/null +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AndEventTrigger.java @@ -0,0 +1,129 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + + +package org.eclipse.jetty.monitor.triggers; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jetty.monitor.jmx.EventState; +import org.eclipse.jetty.monitor.jmx.EventTrigger; + + +/* ------------------------------------------------------------ */ +/** + * AndEventTrigger + * + * EventTrigger aggregation using logical AND operation + * that executes matching of the aggregated event triggers + * in left to right order + */ +public class AndEventTrigger extends EventTrigger +{ + protected final List _triggers; + + /* ------------------------------------------------------------ */ + /** + * Construct an event trigger and associate the list + * of event triggers to be aggregated by this trigger + * + * @param triggers list of event triggers to add + */ + public AndEventTrigger(List triggers) + { + _triggers = triggers; + } + + /* ------------------------------------------------------------ */ + /** + * Construct an event trigger and associate the array + * of event triggers to be aggregated by this trigger + * + * @param triggers array of event triggers to add + */ + public AndEventTrigger(EventTrigger... triggers) + { + _triggers = Arrays.asList(triggers); + } + + /* ------------------------------------------------------------ */ + /** + * Verify if the event trigger conditions are in the + * appropriate state for an event to be triggered. + * This event trigger will match if all aggregated + * event triggers would return a match. + * + * @see org.eclipse.jetty.monitor.jmx.EventTrigger#match(long) + */ + public boolean match(long timestamp) + throws Exception + { + for(EventTrigger trigger : _triggers) + { + if (!trigger.match(timestamp)) + return false; + } + return true; + } + + /* ------------------------------------------------------------ */ + /** + * Retrieve the event state associated with specified invocation + * of the event trigger match method. This event trigger retrieves + * the combined event state of all aggregated event triggers. + * + * @param timestamp time stamp associated with invocation + * @return event state or null if not found + * + * @see org.eclipse.jetty.monitor.jmx.EventTrigger#getState(long) + */ + @Override + public EventState getState(long timestamp) + { + EventState state = new EventState(); + + for (EventTrigger trigger : _triggers) + { + EventState subState = trigger.getState(timestamp); + state.addAll(subState.values()); + } + + return state; + } + + /* ------------------------------------------------------------ */ + /** + * Returns the string representation of this event trigger + * in the format "AND(triger1,trigger2,...)". + * + * @return string representation of the event trigger + * + * @see java.lang.Object#toString() + */ + public String toString() + { + int cnt = 0; + StringBuilder result = new StringBuilder(); + + result.append("AND("); + for (EventTrigger trigger : _triggers) + { + result.append(cnt++ > 0 ? "," : ""); + result.append(trigger); + } + result.append(')'); + + return result.toString(); + } +} diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AttrEventTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AttrEventTrigger.java new file mode 100644 index 00000000000..1a86696d525 --- /dev/null +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AttrEventTrigger.java @@ -0,0 +1,232 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + + +package org.eclipse.jetty.monitor.triggers; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javax.management.MBeanServerConnection; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.openmbean.CompositeData; + +import org.eclipse.jetty.monitor.JMXMonitor; +import org.eclipse.jetty.monitor.jmx.EventState; +import org.eclipse.jetty.monitor.jmx.EventTrigger; +import org.eclipse.jetty.util.log.Log; + + +/* ------------------------------------------------------------ */ +/** + * AttrEventTrigger + * + * Event trigger that polls a value of an MXBean attribute + * and matches every invocation of this trigger. It can be + * used to send notifications of the value of an attribute + * of the MXBean being polled at a certain interval, or as + * a base class for the event triggers that match the + * value of an attribute of the MXBean being polled against + * some specified criteria. + */ +public class AttrEventTrigger> + extends EventTrigger +{ + private final ObjectName _nameObject; + + protected final String _objectName; + protected final String _attributeName; + protected Map> _states; + + /* ------------------------------------------------------------ */ + /** + * Construct event trigger and specify the MXBean attribute + * that will be polled by this event trigger. + * + * @param objectName object name of an MBean to be polled + * @param attributeName name of an MBean attribute to be polled + * + * @throws MalformedObjectNameException + * @throws IllegalArgumentException + */ + public AttrEventTrigger(String objectName, String attributeName) + throws MalformedObjectNameException, IllegalArgumentException + { + if (objectName == null) + throw new IllegalArgumentException("Object name cannot be null"); + if (attributeName == null) + throw new IllegalArgumentException("Attribute name cannot be null"); + + _states = new ConcurrentHashMap>(); + + _objectName = objectName; + _attributeName = attributeName; + + _nameObject = new ObjectName(_objectName); + } + + /* ------------------------------------------------------------ */ + /** + * Construct event trigger and specify the MXBean attribute + * that will be polled by this event trigger. + * + * @param nameObject object name of an MBean to be polled + * @param attributeName name of an MBean attribute to be polled + * + * @throws IllegalArgumentException + */ + public AttrEventTrigger(ObjectName nameObject, String attributeName) + throws IllegalArgumentException + { + if (nameObject == null) + throw new IllegalArgumentException("Object name cannot be null"); + if (attributeName == null) + throw new IllegalArgumentException("Attribute name cannot be null"); + + _states = new ConcurrentHashMap>(); + + _objectName = nameObject.toString(); + _attributeName = attributeName; + + _nameObject = nameObject; + } + + /* ------------------------------------------------------------ */ + /** + * Verify if the event trigger conditions are in the + * appropriate state for an event to be triggered. + * This event trigger uses the match(Comparable) + * method to compare the value of the MXBean attribute + * to the conditions specified by the subclasses. + * + * @see org.eclipse.jetty.monitor.jmx.EventTrigger#match(long) + */ + @SuppressWarnings("unchecked") + public final boolean match(long timestamp) + throws Exception + { + MBeanServerConnection serverConnection = JMXMonitor.getServiceConnection(); + + TYPE value = null; + try + { + int pos = _attributeName.indexOf('.'); + if (pos < 0) + value = (TYPE)serverConnection.getAttribute(_nameObject,_attributeName); + else + value = getValue((CompositeData)serverConnection.getAttribute(_nameObject, _attributeName.substring(0, pos)), + _attributeName.substring(pos+1)); + } + catch (Exception ex) + { + Log.debug(ex); + } + + boolean result = false; + if (value != null) + { + result = match(value); + + if (result || getSaveAll()) + { + _states.put(timestamp, + new EventState(this.getID(), this.getNameString(), value)); + } + } + + return result; + } + + + /* ------------------------------------------------------------ */ + /** + * Verify if the event trigger conditions are in the + * appropriate state for an event to be triggered. + * Allows subclasses to override the default behavior + * that matches every invocation of this trigger + */ + public boolean match(Comparable value) + { + return true; + } + + /* ------------------------------------------------------------ */ + /** + * Retrieve the event state associated with specified invocation + * of the event trigger match method. + * + * @param timestamp time stamp associated with invocation + * @return event state or null if not found + * + * @see org.eclipse.jetty.monitor.jmx.EventTrigger#getState(long) + */ + @Override + public final EventState getState(long timestamp) + { + return _states.get(timestamp); + } + + /* ------------------------------------------------------------ */ + /** + * Returns the string representation of this event trigger + * in the format "[object_name:attribute_name]". + * + * @return string representation of the event trigger + * + * @see java.lang.Object#toString() + */ + public String toString() + { + return getNameString(); + } + + /* ------------------------------------------------------------ */ + /** + * Returns the string representation of this event trigger + * in the format "[object_name:attribute_name]". Allows + * subclasses to override the name string used to identify + * this event trigger in the event state object as well as + * string representation of the subclasses. + * + * @return string representation of the event trigger + */ + protected String getNameString() + { + StringBuilder result = new StringBuilder(); + + result.append('['); + result.append(_objectName); + result.append(":"); + result.append(_attributeName); + result.append("]"); + + return result.toString(); + } + + protected boolean getSaveAll() + { + return true; + } + + protected TYPE getValue(CompositeData compValue, String fieldName) + { + int pos = fieldName.indexOf('.'); + if (pos < 0) + return (TYPE)compValue.get(fieldName); + else + return getValue((CompositeData)compValue.get(fieldName.substring(0, pos)), + fieldName.substring(pos+1)); + + } +} diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/EqualToAttrEventTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/EqualToAttrEventTrigger.java new file mode 100644 index 00000000000..70ff65daffa --- /dev/null +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/EqualToAttrEventTrigger.java @@ -0,0 +1,85 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + + +package org.eclipse.jetty.monitor.triggers; + +import javax.management.MalformedObjectNameException; + + +/* ------------------------------------------------------------ */ +/** + * EqualToAttrEventTrigger + * + * Event trigger that polls a value of an MXBean attribute and + * checks if it is equal to specified value. + */ +public class EqualToAttrEventTrigger> extends AttrEventTrigger +{ + protected final TYPE _value; + + /* ------------------------------------------------------------ */ + /** + * Construct event trigger and specify the MXBean attribute + * that will be polled by this event trigger as well as the + * target value of the attribute. + * + * @param objectName object name of an MBean to be polled + * @param attributeName name of an MBean attribute to be polled + * @param value target value of the attribute + * + * @throws MalformedObjectNameException + * @throws IllegalArgumentException + */ + public EqualToAttrEventTrigger(String objectName, String attributeName, TYPE value) + throws MalformedObjectNameException, IllegalArgumentException + { + super(objectName,attributeName); + + if (value == null) + throw new IllegalArgumentException("Value cannot be null"); + + _value = value; + } + + /* ------------------------------------------------------------ */ + /** + * Compare the value of the MXBean attribute being polling + * to check if it is equal to the specified value. + */ + @Override + public boolean match(Comparable value) + { + return (value.compareTo(_value) == 0); + } + + /* ------------------------------------------------------------ */ + /** + * Returns the string representation of this event trigger + * in the format "name=value". + * + * @return string representation of the event trigger + * + * @see java.lang.Object#toString() + */ + public String toString() + { + StringBuilder result = new StringBuilder(); + + result.append(getNameString()); + result.append("=="); + result.append(_value); + + return result.toString(); + } +} diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/GreaterThanAttrEventTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/GreaterThanAttrEventTrigger.java new file mode 100644 index 00000000000..9d86b7dd7ce --- /dev/null +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/GreaterThanAttrEventTrigger.java @@ -0,0 +1,87 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + + +package org.eclipse.jetty.monitor.triggers; + +import javax.management.MalformedObjectNameException; + + +/* ------------------------------------------------------------ */ +/** + * GreaterThanAttrEventTrigger + * + * Event trigger that polls a value of an MXBean attribute and + * checks if it is greater than specified min value. + */ +public class GreaterThanAttrEventTrigger> extends AttrEventTrigger +{ + protected final TYPE _min; + + /* ------------------------------------------------------------ */ + /** + * Construct event trigger and specify the MXBean attribute + * that will be polled by this event trigger as well as min + * value of the attribute. + * + * @param objectName object name of an MBean to be polled + * @param attributeName name of an MBean attribute to be polled + * @param min minimum value of the attribute + * + * @throws MalformedObjectNameException + * @throws IllegalArgumentException + */ + public GreaterThanAttrEventTrigger(String objectName, String attributeName, TYPE min) + throws MalformedObjectNameException, IllegalArgumentException + { + super(objectName,attributeName); + + if (min == null) + throw new IllegalArgumentException("Value cannot be null"); + + _min = min; + } + + /* ------------------------------------------------------------ */ + /** + * Compare the value of the MXBean attribute being polling + * to check if it is greater than the min value. + * + * @see org.eclipse.jetty.monitor.triggers.AttrEventTrigger#match(java.lang.Comparable) + */ + @Override + public boolean match(Comparable value) + { + return (value.compareTo(_min) > 0); + } + + /* ------------------------------------------------------------ */ + /** + * Returns the string representation of this event trigger + * in the format "min> extends AttrEventTrigger +{ + protected final TYPE _min; + + /* ------------------------------------------------------------ */ + /** + * Construct event trigger and specify the MXBean attribute + * that will be polled by this event trigger as well as min + * value of the attribute. + * + * @param objectName object name of an MBean to be polled + * @param attributeName name of an MBean attribute to be polled + * @param min minimum value of the attribute + * + * @throws MalformedObjectNameException + * @throws IllegalArgumentException + */ + public GreaterThanOrEqualToAttrEventTrigger(String objectName, String attributeName, TYPE min) + throws MalformedObjectNameException, IllegalArgumentException + { + super(objectName,attributeName); + + if (min == null) + throw new IllegalArgumentException("Value cannot be null"); + + _min = min; + } + + /* ------------------------------------------------------------ */ + /** + * Compare the value of the MXBean attribute being polling + * to check if it is greater than or equal to the min value. + * + * @see org.eclipse.jetty.monitor.triggers.AttrEventTrigger#match(java.lang.Comparable) + */ + @Override + public boolean match(Comparable value) + { + return (value.compareTo(_min) >= 0); + } + + /* ------------------------------------------------------------ */ + /** + * Returns the string representation of this event trigger + * in the format "min<=name". + * + * @return string representation of the event trigger + * + * @see java.lang.Object#toString() + */ + public String toString() + { + StringBuilder result = new StringBuilder(); + + result.append(_min); + result.append("<="); + result.append(getNameString()); + + return result.toString(); + } +} diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/LessThanAttrEventTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/LessThanAttrEventTrigger.java new file mode 100644 index 00000000000..7a002433d05 --- /dev/null +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/LessThanAttrEventTrigger.java @@ -0,0 +1,87 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + + +package org.eclipse.jetty.monitor.triggers; + +import javax.management.MalformedObjectNameException; + + +/* ------------------------------------------------------------ */ +/** + * LessThanAttrEventTrigger + * + * Event trigger that polls a value of an MXBean attribute and + * checks if it is greater than specified max value. + */ +public class LessThanAttrEventTrigger> extends AttrEventTrigger +{ + protected final TYPE _max; + + /* ------------------------------------------------------------ */ + /** + * Construct event trigger and specify the MXBean attribute + * that will be polled by this event trigger as well as max + * value of the attribute. + * + * @param objectName object name of an MBean to be polled + * @param attributeName name of an MBean attribute to be polled + * @param max maximum value of the attribute + * + * @throws MalformedObjectNameException + * @throws IllegalArgumentException + */ + public LessThanAttrEventTrigger(String objectName, String attributeName, TYPE max) + throws MalformedObjectNameException, IllegalArgumentException + { + super(objectName,attributeName); + + if (max == null) + throw new IllegalArgumentException("Value cannot be null"); + + _max = max; + } + + /* ------------------------------------------------------------ */ + /** + * Compare the value of the MXBean attribute being polling + * to check if it is less than the min value. + * + * @see org.eclipse.jetty.monitor.triggers.AttrEventTrigger#match(java.lang.Comparable) + */ + @Override + public boolean match(Comparable value) + { + return (value.compareTo(_max) < 0); + } + + /* ------------------------------------------------------------ */ + /** + * Returns the string representation of this event trigger + * in the format "name> extends AttrEventTrigger +{ + protected final TYPE _max; + + /* ------------------------------------------------------------ */ + /** + * Construct event trigger and specify the MXBean attribute + * that will be polled by this event trigger as well as max + * value of the attribute. + * + * @param objectName object name of an MBean to be polled + * @param attributeName name of an MBean attribute to be polled + * @param max maximum value of the attribute + * + * @throws MalformedObjectNameException + * @throws IllegalArgumentException + */ + public LessThanOrEqualToAttrEventTrigger(String objectName, String attributeName, TYPE max) + throws MalformedObjectNameException, IllegalArgumentException + { + super(objectName,attributeName); + + if (max == null) + throw new IllegalArgumentException("Value cannot be null"); + + _max = max; + } + + /* ------------------------------------------------------------ */ + /** + * Compare the value of the MXBean attribute being polling + * to check if it is less than or equal to the max value. + * + * @see org.eclipse.jetty.monitor.triggers.AttrEventTrigger#match(java.lang.Comparable) + */ + @Override + public boolean match(Comparable value) + { + return (value.compareTo(_max) <= 0); + } + + /* ------------------------------------------------------------ */ + /** + * Returns the string representation of this event trigger + * in the format "name<=max". + * + * @return string representation of the event trigger + * + * @see java.lang.Object#toString() + */ + public String toString() + { + StringBuilder result = new StringBuilder(); + + result.append(getNameString()); + result.append("<="); + result.append(_max); + + return result.toString(); + } +} diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/OrEventTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/OrEventTrigger.java new file mode 100644 index 00000000000..632ac5ae8f3 --- /dev/null +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/OrEventTrigger.java @@ -0,0 +1,133 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + +package org.eclipse.jetty.monitor.triggers; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jetty.monitor.jmx.EventState; +import org.eclipse.jetty.monitor.jmx.EventTrigger; + + +/* ------------------------------------------------------------ */ +/** + * AndEventTrigger + * + * EventTrigger aggregation using logical OR operation + * that executes matching of the aggregated event triggers + * in left to right order + */ +public class OrEventTrigger + extends EventTrigger +{ + private final List _triggers; + + /* ------------------------------------------------------------ */ + /** + * Construct an event trigger and associate the list + * of event triggers to be aggregated by this trigger + * + * @param triggers list of event triggers to add + */ + public OrEventTrigger(List triggers) + { + _triggers = triggers; + } + + /* ------------------------------------------------------------ */ + /** + * Construct an event trigger and associate the array + * of event triggers to be aggregated by this trigger + * + * @param triggers array of event triggers to add + */ + public OrEventTrigger(EventTrigger... triggers) + { + _triggers = Arrays.asList(triggers); + } + + /* ------------------------------------------------------------ */ + /** + * Verify if the event trigger conditions are in the + * appropriate state for an event to be triggered. + * This event trigger will match if any of aggregated + * event triggers would return a match. + * + * @see org.eclipse.jetty.monitor.jmx.EventTrigger#match(long) + */ + public boolean match(long timestamp) + throws Exception + { + for(EventTrigger trigger : _triggers) + { + if (trigger.match(timestamp)) + return true; + } + return false; + } + + /* ------------------------------------------------------------ */ + /** + * Retrieve the event state associated with specified invocation + * of the event trigger match method. This event trigger retrieves + * the combined event state of all aggregated event triggers. + * + * @param timestamp time stamp associated with invocation + * @return event state or null if not found + * + * @see org.eclipse.jetty.monitor.jmx.EventTrigger#getState(long) + */ + @Override + @SuppressWarnings("unchecked") + public EventState getState(long timestamp) + { + EventState state = new EventState(); + + for (EventTrigger trigger : _triggers) + { + EventState subState = trigger.getState(timestamp); + if (subState!=null) + { + state.addAll(subState.values()); + } + } + + return state; + } + + /* ------------------------------------------------------------ */ + /** + * Returns the string representation of this event trigger + * in the format "OR(triger1,trigger2,...)". + * + * @return string representation of the event trigger + * + * @see java.lang.Object#toString() + */ + public String toString() + { + int cnt = 0; + StringBuilder result = new StringBuilder(); + + result.append("OR("); + for (EventTrigger trigger : _triggers) + { + result.append(cnt++ > 0 ? "," : ""); + result.append(trigger); + } + result.append(')'); + + return result.toString(); + } +} diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/RangeAttrEventTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/RangeAttrEventTrigger.java new file mode 100644 index 00000000000..d9140f4d4d1 --- /dev/null +++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/RangeAttrEventTrigger.java @@ -0,0 +1,96 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + + +package org.eclipse.jetty.monitor.triggers; + +import javax.management.MalformedObjectNameException; + + +/* ------------------------------------------------------------ */ +/** + * RangeAttrEventTrigger + * + * Event trigger that polls a value of an MXBean attribute and + * checks if it is in a range from specified min value to + * specified max value. + */ +public class RangeAttrEventTrigger> extends AttrEventTrigger +{ + protected final TYPE _min; + protected final TYPE _max; + + /* ------------------------------------------------------------ */ + /** + * Construct event trigger and specify the MXBean attribute + * that will be polled by this event trigger as well as min + * and max value of the attribute. + * + * @param objectName object name of an MBean to be polled + * @param attributeName name of an MBean attribute to be polled + * @param min minimum value of the attribute + * @param max maximum value of the attribute + * + * @throws MalformedObjectNameException + * @throws IllegalArgumentException + */ + public RangeAttrEventTrigger(String objectName, String attributeName,TYPE min, TYPE max) + throws MalformedObjectNameException, IllegalArgumentException + { + super(objectName,attributeName); + + if (min == null) + throw new IllegalArgumentException("Value cannot be null"); + if (max == null) + throw new IllegalArgumentException("Value cannot be null"); + + _min = min; + _max = max; + } + + /* ------------------------------------------------------------ */ + /** + * Compare the value of the MXBean attribute being polling + * to check if it is in a range from specified min value to + * specified max value. + * + * @see org.eclipse.jetty.monitor.triggers.AttrEventTrigger#match(java.lang.Comparable) + */ + @Override + public boolean match(Comparable value) + { + return (value.compareTo(_min) > 0) &&(value.compareTo(_max) < 0); + } + + /* ------------------------------------------------------------ */ + /** + * Returns the string representation of this event trigger + * in the format "min> extends AttrEventTrigger +{ + protected final TYPE _min; + protected final TYPE _max; + + /* ------------------------------------------------------------ */ + /** + * Construct event trigger and specify the MXBean attribute + * that will be polled by this event trigger as well as min + * and max value of the attribute. + * + * @param objectName object name of an MBean to be polled + * @param attributeName name of an MBean attribute to be polled + * @param min minimum value of the attribute + * @param max maximum value of the attribute + * + * @throws MalformedObjectNameException + * @throws IllegalArgumentException + */ + public RangeInclAttrEventTrigger(String objectName, String attributeName,TYPE min, TYPE max) + throws MalformedObjectNameException, IllegalArgumentException + { + super(objectName,attributeName); + + if (min == null) + throw new IllegalArgumentException("Value cannot be null"); + if (max == null) + throw new IllegalArgumentException("Value cannot be null"); + + _min = min; + _max = max; + } + + /* ------------------------------------------------------------ */ + /** + * Compare the value of the MXBean attribute being polling + * to check if it is in a range from specified min value to + * specified max value including the range bounds. + * + * @see org.eclipse.jetty.monitor.triggers.AttrEventTrigger#match(java.lang.Comparable) + */ + @Override + public boolean match(Comparable value) + { + return (value.compareTo(_min) >= 0) &&(value.compareTo(_max) <= 0); + } + + /* ------------------------------------------------------------ */ + /** + * Returns the string representation of this event trigger + * in the format "min<=name<=max". + * + * @return string representation of the event trigger + * + * @see java.lang.Object#toString() + */ + public String toString() + { + StringBuilder result = new StringBuilder(); + + result.append(_min); + result.append("<="); + result.append(getNameString()); + result.append("<="); + result.append(_max); + + return result.toString(); + } +} diff --git a/jetty-monitor/src/main/resources/org/mortbay/jetty/monitor/integration/jmx/JavaMonitorTools-mbean.properties b/jetty-monitor/src/main/resources/org/mortbay/jetty/monitor/integration/jmx/JavaMonitorTools-mbean.properties new file mode 100644 index 00000000000..4722fdaa790 --- /dev/null +++ b/jetty-monitor/src/main/resources/org/mortbay/jetty/monitor/integration/jmx/JavaMonitorTools-mbean.properties @@ -0,0 +1,12 @@ +JavaMonitorTools: Retrieves additional information required by java-monitor +DeadlockStacktraces: RO:Detailed report on the deadlocked threads. +ThreadsNew: RO:Number of new threads +ThreadsRunnable: RO:Number of runnable threads +ThreadsBlocked: RO:Number of blocked threads +ThreadsWaiting: RO:Number of waiting threads +ThreadsTimedWaiting: RO:Number of sleeping and waiting threads +ThreadsTerminated: RO:Number of terminated threads +CacheSeconds: RO:Amount of time successful DNS queries are cached for +CacheTweakedFrom: RO:Cache policy for successful DNS lookups was changed from the hard-coded default +CacheNegativeSeconds: RO:Amount of time failed DNS queries are cached for +CacheNegativeTweakedFrom: RO:Cache policy for failed DNS lookups was changed from the hard-coded default diff --git a/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/AttrEventTriggerTest.java b/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/AttrEventTriggerTest.java new file mode 100644 index 00000000000..5cc7bdfd2d0 --- /dev/null +++ b/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/AttrEventTriggerTest.java @@ -0,0 +1,509 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + +package org.eclipse.jetty.monitor; + +import static org.junit.Assert.assertEquals; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.management.ManagementFactory; +import java.util.Collection; +import java.util.Iterator; +import java.util.TreeSet; + +import javax.management.MBeanServer; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.client.ContentExchange; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.security.Realm; +import org.eclipse.jetty.client.security.SimpleRealmResolver; +import org.eclipse.jetty.http.HttpMethods; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.jmx.MBeanContainer; +import org.eclipse.jetty.monitor.JMXMonitor; +import org.eclipse.jetty.monitor.jmx.ConsoleNotifier; +import org.eclipse.jetty.monitor.jmx.EventNotifier; +import org.eclipse.jetty.monitor.jmx.EventState; +import org.eclipse.jetty.monitor.jmx.EventTrigger; +import org.eclipse.jetty.monitor.jmx.MonitorAction; +import org.eclipse.jetty.monitor.jmx.EventState.TriggerState; +import org.eclipse.jetty.monitor.triggers.AndEventTrigger; +import org.eclipse.jetty.monitor.triggers.AttrEventTrigger; +import org.eclipse.jetty.monitor.triggers.EqualToAttrEventTrigger; +import org.eclipse.jetty.monitor.triggers.GreaterThanAttrEventTrigger; +import org.eclipse.jetty.monitor.triggers.GreaterThanOrEqualToAttrEventTrigger; +import org.eclipse.jetty.monitor.triggers.LessThanAttrEventTrigger; +import org.eclipse.jetty.monitor.triggers.LessThanOrEqualToAttrEventTrigger; +import org.eclipse.jetty.monitor.triggers.OrEventTrigger; +import org.eclipse.jetty.monitor.triggers.RangeAttrEventTrigger; +import org.eclipse.jetty.monitor.triggers.RangeInclAttrEventTrigger; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.eclipse.jetty.server.nio.SelectChannelConnector; +import org.eclipse.jetty.util.log.Log; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + + +/* ------------------------------------------------------------ */ +/** + */ +public class AttrEventTriggerTest +{ + private Server _server; + private TestHandler _handler; + private RequestCounter _counter; + private JMXMonitor _monitor; + private HttpClient _client; + private String _requestUrl; + + @Before + public void setUp() + throws Exception + { + File docRoot = new File("target/test-output/docroot/"); + docRoot.mkdirs(); + docRoot.deleteOnExit(); + + System.setProperty("org.eclipse.jetty.util.log.DEBUG",""); + _server = new Server(); + + SelectChannelConnector connector = new SelectChannelConnector(); + _server.addConnector(connector); + + _handler = new TestHandler(); + _server.setHandler(_handler); + + MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); + MBeanContainer mBeanContainer = new MBeanContainer(mBeanServer); + mBeanContainer.addBean(Log.getLog()); + + _counter = _handler.getRequestCounter(); + mBeanContainer.addBean(_counter); + + _server.addBean(mBeanContainer); + _server.getContainer().addEventListener(mBeanContainer); + _server.start(); + + startClient(null); + + _monitor = new JMXMonitor(); + + int port = _server.getConnectors()[0].getLocalPort(); + _requestUrl = "http://localhost:"+port+ "/"; + } + + @After + public void tearDown() + throws Exception + { + stopClient(); + + if (_server != null) + { + _server.stop(); + _server = null; + } + } + + @Test + public void testNoCondition() + throws Exception + { + long requestCount = 10; + + AttrEventTrigger trigger = + new AttrEventTrigger("org.eclipse.jetty.monitor:type=requestcounter,id=0", "counter"); + + EventNotifier notifier = new ConsoleNotifier("%s"); + CounterAction action = new CounterAction(trigger, notifier, 500, 100); + + performTest(action, requestCount, 1000); + + ResultSet result = new ResultSet(1,requestCount); + assertEquals(result, action.getHits()); + } + + @Test + public void testEqual_TRUE() + throws Exception + { + long requestCount = 10; + long testValue = 5; + + EqualToAttrEventTrigger trigger = + new EqualToAttrEventTrigger("org.eclipse.jetty.monitor:type=requestcounter,id=0", "counter",testValue); + + EventNotifier notifier = new ConsoleNotifier("%s"); + CounterAction action = new CounterAction(trigger, notifier, 500, 100); + + performTest(action, requestCount, 1000); + + ResultSet result = new ResultSet(testValue); + assertEquals(result, action.getHits()); + } + + @Test + public void testEqual_FALSE() + throws Exception + { + long requestCount = 10; + long testValue = 11; + + EqualToAttrEventTrigger trigger = + new EqualToAttrEventTrigger("org.eclipse.jetty.monitor:type=requestcounter,id=0", "counter", + testValue); + + EventNotifier notifier = new ConsoleNotifier("%s"); + CounterAction action = new CounterAction(trigger, notifier, 500, 100); + + performTest(action, requestCount, 1000); + + ResultSet result = new ResultSet(); + assertEquals(result, action.getHits()); + } + + @Test + public void testLowerLimit() + throws Exception + { + long requestCount = 10; + long testRangeLow = 5; + + GreaterThanAttrEventTrigger trigger = + new GreaterThanAttrEventTrigger("org.eclipse.jetty.monitor:type=requestcounter,id=0", "counter", + testRangeLow); + + EventNotifier notifier = new ConsoleNotifier("%s"); + CounterAction action = new CounterAction(trigger, notifier, 500, 100); + + performTest(action, requestCount, 1000); + + ResultSet result = new ResultSet(6,10); + assertEquals(result, action.getHits()); + } + + @Test + public void testLowerLimitIncl() + throws Exception + { + long requestCount = 10; + long testRangeLow = 5; + + GreaterThanOrEqualToAttrEventTrigger trigger = + new GreaterThanOrEqualToAttrEventTrigger("org.eclipse.jetty.monitor:type=requestcounter,id=0", "counter", + testRangeLow); + + EventNotifier notifier = new ConsoleNotifier("%s"); + CounterAction action = new CounterAction(trigger, notifier, 500, 100); + + performTest(action, requestCount, 1000); + + ResultSet result = new ResultSet(5,10); + assertEquals(result, action.getHits()); + } + + @Test + public void testUpperLimit() + throws Exception + { + long requestCount = 10; + long testRangeHigh = 5; + + LessThanAttrEventTrigger trigger = + new LessThanAttrEventTrigger("org.eclipse.jetty.monitor:type=requestcounter,id=0", "counter", + testRangeHigh); + + EventNotifier notifier = new ConsoleNotifier("%s"); + CounterAction action = new CounterAction(trigger, notifier, 500, 100); + + performTest(action, requestCount, 1000); + + ResultSet result = new ResultSet(1,4); + assertEquals(result, action.getHits()); + } + + + @Test + public void testUpperLimitIncl() + throws Exception + { + long requestCount = 10; + long testRangeHigh = 5; + + LessThanOrEqualToAttrEventTrigger trigger = + new LessThanOrEqualToAttrEventTrigger("org.eclipse.jetty.monitor:type=requestcounter,id=0", "counter", + testRangeHigh); + + EventNotifier notifier = new ConsoleNotifier("%s"); + CounterAction action = new CounterAction(trigger, notifier, 500, 100); + + performTest(action, requestCount, 1000); + + ResultSet result = new ResultSet(1,5); + assertEquals(result, action.getHits()); + } + + @Test + public void testRangeInclusive() + throws Exception + { + long requestCount = 10; + long testRangeLow = 3; + long testRangeHigh = 8; + + RangeInclAttrEventTrigger trigger = + new RangeInclAttrEventTrigger("org.eclipse.jetty.monitor:type=requestcounter,id=0", "counter", + testRangeLow, testRangeHigh); + + EventNotifier notifier = new ConsoleNotifier("%s"); + CounterAction action = new CounterAction(trigger, notifier, 500, 100); + + performTest(action, requestCount, 1000); + + ResultSet result = new ResultSet(testRangeLow,testRangeHigh); + assertEquals(result, action.getHits()); + } + + @Test + public void testInsideRangeExclusive() + throws Exception + { + long requestCount = 10; + long testRangeLow = 3; + long testRangeHigh = 8; + + RangeAttrEventTrigger trigger = + new RangeAttrEventTrigger("org.eclipse.jetty.monitor:type=requestcounter,id=0", "counter", + testRangeLow, testRangeHigh); + + EventNotifier notifier = new ConsoleNotifier("%s"); + CounterAction action = new CounterAction(trigger, notifier, 500, 100); + + performTest(action, requestCount, 1000); + + ResultSet result = new ResultSet(testRangeLow+1,testRangeHigh-1); + assertEquals(result, action.getHits()); + } + + @Test + public void testRangeComposite() + throws Exception + { + long requestCount = 10; + long testRangeLow = 4; + long testRangeHigh = 7; + + GreaterThanAttrEventTrigger trigger1 = + new GreaterThanAttrEventTrigger("org.eclipse.jetty.monitor:type=requestcounter,id=0", "counter", + testRangeLow); + LessThanOrEqualToAttrEventTrigger trigger2 = + new LessThanOrEqualToAttrEventTrigger("org.eclipse.jetty.monitor:type=requestcounter,id=0", "counter", + testRangeHigh); + AndEventTrigger trigger = new AndEventTrigger(trigger1, trigger2); + EventNotifier notifier = new ConsoleNotifier("%s"); + CounterAction action = new CounterAction(trigger, notifier, 500, 100); + + performTest(action, requestCount, 1000); + + ResultSet result = new ResultSet(testRangeLow+1,testRangeHigh); + assertEquals(result, action.getHits()); + } + + @Test + public void testRangeOuter() + throws Exception + { + long requestCount = 10; + long testRangeLow = 4; + long testRangeHigh = 7; + + LessThanOrEqualToAttrEventTrigger trigger1 = + new LessThanOrEqualToAttrEventTrigger("org.eclipse.jetty.monitor:type=requestcounter,id=0", "counter", + testRangeLow); + GreaterThanAttrEventTrigger trigger2 = + new GreaterThanAttrEventTrigger("org.eclipse.jetty.monitor:type=requestcounter,id=0", "counter", + testRangeHigh); + OrEventTrigger trigger = new OrEventTrigger(trigger1, trigger2); + EventNotifier notifier = new ConsoleNotifier("%s"); + CounterAction action = new CounterAction(trigger, notifier, 500, 100); + + performTest(action, requestCount, 1000); + + ResultSet result = new ResultSet(1,testRangeLow,testRangeHigh+1, requestCount); + assertEquals(result, action.getHits()); + } + + protected void performTest(MonitorAction action, long count, long interval) + throws Exception + { + _monitor.addActions(action); + + for (long cnt=0; cnt < count; cnt++) + { + try + { + ContentExchange getExchange = new ContentExchange(); + getExchange.setURL(_requestUrl); + getExchange.setMethod(HttpMethods.GET); + + _client.send(getExchange); + int state = getExchange.waitForDone(); + + String content = ""; + int responseStatus = getExchange.getResponseStatus(); + if (responseStatus == HttpStatus.OK_200) + { + content = getExchange.getResponseContent(); + } + + assertEquals(HttpStatus.OK_200,responseStatus); + Thread.sleep(interval); + } + catch (InterruptedException ex) + { + break; + } + } + + Thread.sleep(interval); + + _monitor.removeActions(action); + } + + protected void startClient(Realm realm) + throws Exception + { + _client = new HttpClient(); + _client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL); + if (realm != null) + _client.setRealmResolver(new SimpleRealmResolver(realm)); + _client.start(); + } + + protected void stopClient() + throws Exception + { + if (_client != null) + { + _client.stop(); + _client = null; + } + } + + protected static class TestHandler + extends AbstractHandler + { + private RequestCounter _counter = new RequestCounter(); + + public void handle(String target, Request baseRequest, + HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException + { + if (baseRequest.isHandled()) { + return; + } + _counter.increment(); + + response.setContentType("text/plain"); + response.setStatus(HttpServletResponse.SC_OK); + PrintWriter writer = response.getWriter(); + writer.println("===TEST RESPONSE==="); + baseRequest.setHandled(true); + } + + public RequestCounter getRequestCounter() + { + return _counter; + } + } + + protected static class ResultSet extends TreeSet + { + public ResultSet() {} + + public ResultSet(long value) + { + add(value); + } + + public ResultSet(long start, long end) + { + addEntries(start, end); + } + + public ResultSet(long start, long pause, long resume, long end) + { + addEntries(start, pause); + addEntries(resume, end); + } + + public void addEntries(long start, long stop) + { + if (start > 0 && stop > 0) + { + for(long idx=start; idx <= stop; idx++) + { + add(idx); + } + } + } + + public boolean equals(ResultSet set) + { + return (this.size() == set.size()) && containsAll(set); + } + } + + protected static class CounterAction + extends MonitorAction + { + private ResultSet _hits = new ResultSet(); + + public CounterAction(EventTrigger trigger, EventNotifier notifier, long interval, long delay) + { + super(trigger, notifier, interval, delay); + } + + public void execute(EventTrigger trigger, EventState state, long timestamp) + { + if (trigger != null && state != null) + { + Collection values = state.values(); + + Iterator it = values.iterator(); + while(it.hasNext()) + { + TriggerState entry = (TriggerState)it.next(); + Object value = entry.getValue(); + if (value != null) + { + _hits.add((Long)value); + } + } + } + } + + public ResultSet getHits() + { + return _hits; + } + } +} diff --git a/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/JavaMonitorIntegrationTest.java b/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/JavaMonitorIntegrationTest.java new file mode 100644 index 00000000000..aa21ca06f00 --- /dev/null +++ b/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/JavaMonitorIntegrationTest.java @@ -0,0 +1,163 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + +package org.eclipse.jetty.monitor; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.client.ContentExchange; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.http.HttpMethods; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.monitor.JMXMonitor; +import org.eclipse.jetty.toolchain.test.JettyDistro; +import org.eclipse.jetty.toolchain.test.PropertyFlag; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.thread.ExecutorThreadPool; +import org.eclipse.jetty.util.thread.ThreadPool; +import org.eclipse.jetty.xml.XmlConfiguration; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + + +/* ------------------------------------------------------------ */ +/** + */ +public class JavaMonitorIntegrationTest +{ + private static JettyDistro jetty; + + @BeforeClass + public static void initJetty() throws Exception + { + PropertyFlag.assume("JAVAMONITOR"); + + jetty = new JettyDistro(JavaMonitorIntegrationTest.class); + + jetty.delete("contexts/javadoc.xml"); + + jetty.overlayConfig("monitor"); + + jetty.start(); + + JMXMonitor.setServiceUrl(jetty.getJmxUrl()); + } + + @AfterClass + public static void shutdownJetty() throws Exception + { + if (jetty != null) + { + jetty.stop(); + } + } + + @Before + public void setUp() + throws Exception + { + Resource configRes = Resource.newClassPathResource("/org/eclipse/jetty/monitor/java-monitor-integration.xml"); + XmlConfiguration xmlConfig = new XmlConfiguration(configRes.getURL()); + xmlConfig.configure(); + } + + @Test + public void testIntegration() + throws Exception + { + final int threadCount = 100; + final long requestCount = 500; + final String requestUrl = jetty.getBaseUri().resolve("d.txt").toASCIIString(); + final CountDownLatch gate = new CountDownLatch(threadCount); + + ThreadPool worker = new ExecutorThreadPool(threadCount,threadCount,60,TimeUnit.SECONDS); + for (int idx=0; idx < threadCount; idx++) + { + worker.dispatch(new Runnable() { + public void run() + { + runTest(requestUrl, requestCount); + gate.countDown(); + } + }); + Thread.sleep(500); + } + gate.await(); + assertTrue(true); + } + + protected static void runTest(String requestUrl, long count) + { + HttpClient client = new HttpClient(); + client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL); + try + { + client.start(); + } + catch (Exception ex) + { + Log.debug(ex); + } + + if (client != null) + { + Random rnd = new Random(); + for (long cnt=0; cnt < count; cnt++) + { + try + { + ContentExchange getExchange = new ContentExchange(); + getExchange.setURL(requestUrl); + getExchange.setMethod(HttpMethods.GET); + + client.send(getExchange); + int state = getExchange.waitForDone(); + + String content = ""; + int responseStatus = getExchange.getResponseStatus(); + if (responseStatus == HttpStatus.OK_200) + { + content = getExchange.getResponseContent(); + } + + Thread.sleep(200); + } + catch (InterruptedException ex) + { + break; + } + catch (IOException ex) + { + Log.debug(ex); + } + } + + try + { + client.stop(); + } + catch (Exception ex) + { + Log.debug(ex); + } + } + } +} diff --git a/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/JmxServiceTest.java b/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/JmxServiceTest.java new file mode 100644 index 00000000000..b131502ed96 --- /dev/null +++ b/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/JmxServiceTest.java @@ -0,0 +1,160 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + +package org.eclipse.jetty.monitor; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import javax.management.MBeanServerConnection; + +import org.eclipse.jetty.client.ContentExchange; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.http.HttpMethods; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.monitor.JMXMonitor; +import org.eclipse.jetty.toolchain.jmx.JmxServiceConnection; +import org.eclipse.jetty.toolchain.test.JettyDistro; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.thread.ExecutorThreadPool; +import org.eclipse.jetty.util.thread.ThreadPool; +import org.eclipse.jetty.xml.XmlConfiguration; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + + +/* ------------------------------------------------------------ */ +/** + */ +public class JmxServiceTest +{ + private static JettyDistro jetty; + + @BeforeClass + public static void initJetty() throws Exception + { + jetty = new JettyDistro(JmxServiceTest.class); + + jetty.delete("contexts/javadoc.xml"); + + jetty.overlayConfig("monitor"); + + jetty.start(); + + JMXMonitor.setServiceUrl(jetty.getJmxUrl()); + } + + @AfterClass + public static void shutdownJetty() throws Exception + { + if (jetty != null) + { + jetty.stop(); + } + } + + @Before + public void setUp() + throws Exception + { + Resource configRes = Resource.newClassPathResource("/org/eclipse/jetty/monitor/jetty-monitor-service.xml"); + XmlConfiguration xmlConfig = new XmlConfiguration(configRes.getURL()); + xmlConfig.configure(); + } + + @Test + public void testThreadPoolMXBean() + throws Exception + { + final int threadCount = 100; + final long requestCount = 100; + final String requestUrl = jetty.getBaseUri().resolve("d.txt").toASCIIString(); + final CountDownLatch gate = new CountDownLatch(threadCount); + ThreadPool worker = new ExecutorThreadPool(threadCount,threadCount,60,TimeUnit.SECONDS); + for (int idx=0; idx < threadCount; idx++) + { + worker.dispatch(new Runnable() { + public void run() + { + runTest(requestUrl, requestCount); + gate.countDown(); + } + }); + Thread.sleep(100); + } + gate.await(); + assertTrue(true); + } + + protected static void runTest(String requestUrl, long count) + { + HttpClient client = new HttpClient(); + client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL); + try + { + client.start(); + } + catch (Exception ex) + { + Log.debug(ex); + } + + if (client != null) + { + for (long cnt=0; cnt < count; cnt++) + { + try + { + ContentExchange getExchange = new ContentExchange(); + getExchange.setURL(requestUrl); + getExchange.setMethod(HttpMethods.GET); + + client.send(getExchange); + int state = getExchange.waitForDone(); + + String content = ""; + int responseStatus = getExchange.getResponseStatus(); + if (responseStatus == HttpStatus.OK_200) + { + content = getExchange.getResponseContent(); + } + + Thread.sleep(100); + } + catch (InterruptedException ex) + { + break; + } + catch (IOException ex) + { + Log.debug(ex); + } + } + + try + { + client.stop(); + } + catch (Exception ex) + { + Log.debug(ex); + } + } + } +} diff --git a/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/ProgramConfigTest.java b/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/ProgramConfigTest.java new file mode 100644 index 00000000000..e96f59696c0 --- /dev/null +++ b/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/ProgramConfigTest.java @@ -0,0 +1,179 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + +package org.eclipse.jetty.monitor; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import javax.management.MBeanServerConnection; + +import org.eclipse.jetty.client.ContentExchange; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.http.HttpMethods; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.monitor.JMXMonitor; +import org.eclipse.jetty.monitor.jmx.ConsoleNotifier; +import org.eclipse.jetty.monitor.jmx.EventNotifier; +import org.eclipse.jetty.monitor.jmx.EventState; +import org.eclipse.jetty.monitor.jmx.EventTrigger; +import org.eclipse.jetty.monitor.jmx.MonitorAction; +import org.eclipse.jetty.monitor.triggers.GreaterThanAttrEventTrigger; +import org.eclipse.jetty.monitor.triggers.LessThanOrEqualToAttrEventTrigger; +import org.eclipse.jetty.monitor.triggers.OrEventTrigger; +import org.eclipse.jetty.toolchain.jmx.JmxServiceConnection; +import org.eclipse.jetty.toolchain.test.JettyDistro; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.thread.ExecutorThreadPool; +import org.eclipse.jetty.util.thread.ThreadPool; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + + +/* ------------------------------------------------------------ */ +/** + */ +public class ProgramConfigTest +{ + private static JettyDistro jetty; + + @BeforeClass + public static void initJetty() throws Exception + { + jetty = new JettyDistro(ProgramConfigTest.class); + + jetty.delete("contexts/javadoc.xml"); + + jetty.overlayConfig("monitor"); + + jetty.start(); + + JMXMonitor.setServiceUrl(jetty.getJmxUrl()); + } + + @AfterClass + public static void shutdownJetty() throws Exception + { + if (jetty != null) + { + jetty.stop(); + } + } + + @Test + public void testThreadPoolMXBean() + throws Exception + { + int testRangeLow = 4; + int testRangeHigh = 7; + + LessThanOrEqualToAttrEventTrigger trigger1 = + new LessThanOrEqualToAttrEventTrigger("org.eclipse.jetty.util.thread:type=queuedthreadpool,id=0", "idleThreads", + testRangeLow); + GreaterThanAttrEventTrigger trigger2 = + new GreaterThanAttrEventTrigger("org.eclipse.jetty.util.thread:type=queuedthreadpool,id=0", "idleThreads", + testRangeHigh); + OrEventTrigger trigger = new OrEventTrigger(trigger1, trigger2); + EventNotifier notifier = new ConsoleNotifier("%s"); + final AtomicLong counter = new AtomicLong(); + MonitorAction action = new MonitorAction(trigger, notifier, 500) { + @Override + public void execute(EventTrigger trigger, EventState state, long timestamp) + { + System.out.println(counter.incrementAndGet()); + } + }; + JMXMonitor.addMonitorActions(action); + + final int threadCount = 100; + final long requestCount = 100; + final String requestUrl = jetty.getBaseUri().resolve("d.txt").toASCIIString(); + final CountDownLatch gate = new CountDownLatch(threadCount); + ThreadPool worker = new ExecutorThreadPool(threadCount,threadCount,60,TimeUnit.SECONDS); + for (int idx=0; idx < threadCount; idx++) + { + worker.dispatch(new Runnable() { + public void run() + { + runTest(requestUrl, requestCount); + gate.countDown(); + } + }); + Thread.sleep(100); + } + gate.await(); + JMXMonitor.removeMonitorActions(action); + assertTrue(true); + } + + protected static void runTest(String requestUrl, long count) + { + HttpClient client = new HttpClient(); + client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL); + try + { + client.start(); + } + catch (Exception ex) + { + Log.debug(ex); + } + + if (client != null) + { + for (long cnt=0; cnt < count; cnt++) + { + try + { + ContentExchange getExchange = new ContentExchange(); + getExchange.setURL(requestUrl); + getExchange.setMethod(HttpMethods.GET); + + client.send(getExchange); + getExchange.waitForDone(); + + String content = ""; + int responseStatus = getExchange.getResponseStatus(); + if (responseStatus == HttpStatus.OK_200) + { + content = getExchange.getResponseContent(); + } + + Thread.sleep(100); + } + catch (InterruptedException ex) + { + break; + } + catch (IOException ex) + { + Log.debug(ex); + } + } + + try + { + client.stop(); + } + catch (Exception ex) + { + Log.debug(ex); + } + } + } +} diff --git a/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/RequestCounter.java b/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/RequestCounter.java new file mode 100644 index 00000000000..5b93f13b54e --- /dev/null +++ b/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/RequestCounter.java @@ -0,0 +1,35 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + +package org.eclipse.jetty.monitor; + + +public class RequestCounter +{ + public long _counter; + + public synchronized long getCounter() + { + return _counter; + } + + public synchronized void increment() + { + _counter++; + } + + public synchronized void reset() + { + _counter = 0; + } +} diff --git a/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/XmlConfigTest.java b/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/XmlConfigTest.java new file mode 100644 index 00000000000..b1f97306e91 --- /dev/null +++ b/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/XmlConfigTest.java @@ -0,0 +1,159 @@ +// ======================================================================== +// Copyright (c) Webtide LLC +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + +package org.eclipse.jetty.monitor; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.client.ContentExchange; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.http.HttpMethods; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.monitor.JMXMonitor; +import org.eclipse.jetty.toolchain.test.JettyDistro; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.thread.ExecutorThreadPool; +import org.eclipse.jetty.util.thread.ThreadPool; +import org.eclipse.jetty.xml.XmlConfiguration; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + + +/* ------------------------------------------------------------ */ +/** + */ +public class XmlConfigTest +{ + private static JettyDistro jetty; + + @BeforeClass + public static void initJetty() throws Exception + { + jetty = new JettyDistro(XmlConfigTest.class); + + jetty.delete("contexts/javadoc.xml"); + + jetty.overlayConfig("monitor"); + + jetty.start(); + + JMXMonitor.setServiceUrl(jetty.getJmxUrl()); + } + + @AfterClass + public static void shutdownJetty() throws Exception + { + if (jetty != null) + { + jetty.stop(); + } + } + + @Before + public void setUp() + throws Exception + { + Resource configRes = Resource.newClassPathResource("/org/eclipse/jetty/monitor/jetty-monitor-test.xml"); + XmlConfiguration xmlConfig = new XmlConfiguration(configRes.getURL()); + xmlConfig.configure(); + } + + @Test + public void testThreadPoolMXBean() + throws Exception + { + final int threadCount = 100; + final long requestCount = 100; + final String requestUrl = jetty.getBaseUri().resolve("d.txt").toASCIIString(); + final CountDownLatch gate = new CountDownLatch(threadCount); + ThreadPool worker = new ExecutorThreadPool(threadCount,threadCount,60,TimeUnit.SECONDS); + for (int idx=0; idx < threadCount; idx++) + { + worker.dispatch(new Runnable() { + public void run() + { + runTest(requestUrl, requestCount); + gate.countDown(); + } + }); + Thread.sleep(100); + } + gate.await(); + assertTrue(true); + } + + protected static void runTest(String requestUrl, long count) + { + HttpClient client = new HttpClient(); + client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL); + try + { + client.start(); + } + catch (Exception ex) + { + Log.debug(ex); + } + + if (client != null) + { + Random rnd = new Random(); + for (long cnt=0; cnt < count; cnt++) + { + try + { + ContentExchange getExchange = new ContentExchange(); + getExchange.setURL(requestUrl); + getExchange.setMethod(HttpMethods.GET); + + client.send(getExchange); + int state = getExchange.waitForDone(); + + String content = ""; + int responseStatus = getExchange.getResponseStatus(); + if (responseStatus == HttpStatus.OK_200) + { + content = getExchange.getResponseContent(); + } + + Thread.sleep(100); + } + catch (InterruptedException ex) + { + break; + } + catch (IOException ex) + { + Log.debug(ex); + } + } + + try + { + client.stop(); + } + catch (Exception ex) + { + Log.debug(ex); + } + } + } +} diff --git a/jetty-monitor/src/test/resources/monitor/etc/jetty-jmx.xml b/jetty-monitor/src/test/resources/monitor/etc/jetty-jmx.xml new file mode 100644 index 00000000000..8b4f49fa628 --- /dev/null +++ b/jetty-monitor/src/test/resources/monitor/etc/jetty-jmx.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + rmi + + 0 + /jndi/rmi://localhost:0/jettyjmx + + + org.eclipse.jetty:name=rmiconnectorserver + + + + diff --git a/jetty-monitor/src/test/resources/monitor/start.ini b/jetty-monitor/src/test/resources/monitor/start.ini new file mode 100644 index 00000000000..fe5d7bd5235 --- /dev/null +++ b/jetty-monitor/src/test/resources/monitor/start.ini @@ -0,0 +1,7 @@ +OPTIONS=Server,jsp,jmx,resources,websocket,ext +etc/jetty-jmx.xml +etc/jetty.xml +etc/jetty-deploy.xml +etc/jetty-webapps.xml +etc/jetty-contexts.xml +etc/jetty-testrealm.xml diff --git a/jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/java-monitor-integration.xml b/jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/java-monitor-integration.xml new file mode 100644 index 00000000000..757507c6c84 --- /dev/null +++ b/jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/java-monitor-integration.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + http://194.109.206.51/lemongrass/1.1/push + 57e48e79-f0e6-4909-a6da-e8c1267cbf49 + 8080 + 15000 + + + + + + + diff --git a/jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/jetty-monitor-service.xml b/jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/jetty-monitor-service.xml new file mode 100644 index 00000000000..bf86f3e0e2e --- /dev/null +++ b/jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/jetty-monitor-service.xml @@ -0,0 +1,68 @@ + + + + + + + + service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jettyjmx + + + + + + + + + + + + + org.eclipse.jetty.util.thread:type=queuedthreadpool,id=0 + + idleThreads + 4 + + + + + org.eclipse.jetty.util.thread:type=queuedthreadpool,id=0 + + idleThreads + 7 + + + + + + + + + %s + + + 500 + + + + + + \ No newline at end of file diff --git a/jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/jetty-monitor-test.xml b/jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/jetty-monitor-test.xml new file mode 100644 index 00000000000..49843770c86 --- /dev/null +++ b/jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/jetty-monitor-test.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + org.eclipse.jetty.util.thread:type=queuedthreadpool,id=0 + + idleThreads + 4 + + + + + org.eclipse.jetty.util.thread:type=queuedthreadpool,id=0 + + idleThreads + 7 + + + + + + + + + %s + + + 500 + + + + + + \ No newline at end of file diff --git a/jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/jmx/RequestCounter-mbean.properties b/jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/jmx/RequestCounter-mbean.properties new file mode 100644 index 00000000000..1685bfd6125 --- /dev/null +++ b/jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/jmx/RequestCounter-mbean.properties @@ -0,0 +1,3 @@ +RequestCounter: Request counter +counter: current value of the counter +increment(): increment the counter \ No newline at end of file