Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoakim Erdfelt2011-08-25 19:09:36 +0000
committerJoakim Erdfelt2011-08-25 19:09:36 +0000
commit4ace18376491c6d9937dae2906f1289c824ad054 (patch)
tree419148e9477658fc5816ecd02c31e670fcc8f959 /jetty-monitor
parent953b3512f70d0a9f1a479130c358b3fd815bab7b (diff)
parentf874b0412c65a49334a9956cc9ab0689010a4d41 (diff)
downloadorg.eclipse.jetty.project-4ace18376491c6d9937dae2906f1289c824ad054.tar.gz
org.eclipse.jetty.project-4ace18376491c6d9937dae2906f1289c824ad054.tar.xz
org.eclipse.jetty.project-4ace18376491c6d9937dae2906f1289c824ad054.zip
Merging from master
Diffstat (limited to 'jetty-monitor')
-rw-r--r--jetty-monitor/pom.xml98
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/JMXMonitor.java189
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/ThreadMonitor.java2
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorAction.java418
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorTools.java270
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorTrigger.java77
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/ConsoleNotifier.java56
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventNotifier.java34
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventState.java202
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventTrigger.java69
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/LoggingNotifier.java60
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/MonitorAction.java174
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/MonitorTask.java114
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/NotifierGroup.java115
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/ServiceConnection.java167
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/SimpleAction.java41
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/thread/ThreadMonitorException.java (renamed from jetty-monitor/src/main/java/org/eclipse/jetty/monitor/ThreadMonitorException.java)2
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/thread/ThreadMonitorInfo.java (renamed from jetty-monitor/src/main/java/org/eclipse/jetty/monitor/ThreadMonitorInfo.java)2
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AggregateEventTrigger.java164
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AndEventTrigger.java129
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AttrEventTrigger.java235
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/EqualToAttrEventTrigger.java85
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/GreaterThanAttrEventTrigger.java87
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/GreaterThanOrEqualToAttrEventTrigger.java87
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/LessThanAttrEventTrigger.java87
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/LessThanOrEqualToAttrEventTrigger.java87
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/OrEventTrigger.java133
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/RangeAttrEventTrigger.java96
-rw-r--r--jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/RangeInclAttrEventTrigger.java96
-rw-r--r--jetty-monitor/src/main/resources/org/mortbay/jetty/monitor/integration/jmx/JavaMonitorTools-mbean.properties12
-rw-r--r--jetty-monitor/src/test/java/org/eclipse/jetty/monitor/AttrEventTriggerTest.java509
-rw-r--r--jetty-monitor/src/test/java/org/eclipse/jetty/monitor/JavaMonitorIntegrationTest.java166
-rw-r--r--jetty-monitor/src/test/java/org/eclipse/jetty/monitor/JmxServiceTest.java163
-rw-r--r--jetty-monitor/src/test/java/org/eclipse/jetty/monitor/ProgramConfigTest.java182
-rw-r--r--jetty-monitor/src/test/java/org/eclipse/jetty/monitor/RequestCounter.java35
-rw-r--r--jetty-monitor/src/test/java/org/eclipse/jetty/monitor/XmlConfigTest.java162
-rw-r--r--jetty-monitor/src/test/resources/monitor/etc/jetty-jmx.xml82
-rw-r--r--jetty-monitor/src/test/resources/monitor/start.ini7
-rw-r--r--jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/java-monitor-integration.xml62
-rw-r--r--jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/jetty-monitor-service.xml68
-rw-r--r--jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/jetty-monitor-test.xml65
-rw-r--r--jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/jmx/RequestCounter-mbean.properties3
42 files changed, 4887 insertions, 5 deletions
diff --git a/jetty-monitor/pom.xml b/jetty-monitor/pom.xml
index f0da6e9f5b..17cc6993e2 100644
--- a/jetty-monitor/pom.xml
+++ b/jetty-monitor/pom.xml
@@ -1,3 +1,20 @@
+<!--
+// ========================================================================
+// 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.apache.org/licenses/LICENSE-2.0.txt
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty</groupId>
@@ -10,6 +27,9 @@
<description>Performance monitoring artifact for jetty.</description>
<properties>
<bundle-symbolic-name>${project.groupId}.jmx</bundle-symbolic-name>
+ <test-wars-dir>${project.build.directory}/test-wars</test-wars-dir>
+ <test-libs-dir>${project.build.directory}/test-libs</test-libs-dir>
+ <test-dist-dir>${project.build.directory}/test-dist</test-dist-dir>
</properties>
<build>
<plugins>
@@ -60,6 +80,41 @@
</executions>
</plugin>
<plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <forkMode>always</forkMode>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack-jetty-distro</id>
+ <phase>process-test-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-distribution</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ <overWrite>true</overWrite>
+ </artifactItem>
+ </artifactItems>
+ <outputAbsoluteArtifactFilename>true</outputAbsoluteArtifactFilename>
+ <outputDirectory>${test-dist-dir}</outputDirectory>
+ <overWriteSnapshots>true</overWriteSnapshots>
+ <overWriteIfNewer>true</overWriteIfNewer>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<configuration>
@@ -70,14 +125,51 @@
</build>
<dependencies>
<dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-io</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-http</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-xml</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-client</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-jmx</artifactId>
+ <version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
+ <artifactId>jetty-server</artifactId>
<version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.toolchain</groupId>
+ <artifactId>jetty-test-helper</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
</dependency>
</dependencies>
</project>
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 0000000000..3fdc222081
--- /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<MonitorAction> _actions = new HashSet<MonitorAction>();
+
+ /* ------------------------------------------------------------ */
+ /**
+ * 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 6676271987..70c84fcf43 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 0000000000..d1e4d0e6a5
--- /dev/null
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorAction.java
@@ -0,0 +1,418 @@
+// ========================================================================
+// 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;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+/**
+ */
+public class JavaMonitorAction extends MonitorAction
+{
+ private static final Logger LOG = Log.getLogger(JavaMonitorAction.class);
+
+ 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 <T> void exec(EventTrigger trigger, EventState<T> state, long timestamp)
+ {
+ Collection<TriggerState<T>> trs = state.values();
+
+ Properties data = new Properties();
+ for (TriggerState<T> 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<Object, Object> 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<ObjectName> 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 0000000000..a8d088622c
--- /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 <kjkoster@gmail.com>
+ */
+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<Thread.State, Integer> states = new HashMap<Thread.State, Integer>();
+
+ 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 0000000000..64f6252ecf
--- /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 <TYPE extends Comparable<TYPE>>
+ extends AttrEventTrigger<TYPE>
+{
+ 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<TYPE> 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 0000000000..9e03bfe0f6
--- /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 0000000000..f9d7fdc777
--- /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 0000000000..393617ac1d
--- /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<TYPE>
+{
+
+ /* ------------------------------------------------------------ */
+ /**
+ * State
+ *
+ * Holds the state of a single {@link org.eclipse.jetty.monitor.jmx.EventTrigger event trigger}
+ */
+ public static class TriggerState<TYPE>
+ {
+ 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<String, TriggerState<TYPE>> _states;
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Constructs an empty event state
+ */
+ public EventState()
+ {
+ _states = new ConcurrentHashMap<String, TriggerState<TYPE>>();
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * 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<TYPE>(id, desc, value));
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Adds a trigger state to the event state
+ *
+ * @param state trigger state to add
+ */
+ public void add(TriggerState<TYPE> 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<TriggerState<TYPE>> entries)
+ {
+ for (TriggerState<TYPE> 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<TYPE> 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<TriggerState<TYPE>> 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<TYPE> 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 0000000000..27687dd20f
--- /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 0000000000..032bd9ffb0
--- /dev/null
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/LoggingNotifier.java
@@ -0,0 +1,60 @@
+// ========================================================================
+// 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;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+/**
+ * ConsoleNotifier
+ *
+ * Provides a way to output notification messages to a log file
+ */
+public class LoggingNotifier implements EventNotifier
+{
+ private static final Logger LOG = Log.getLogger(LoggingNotifier.class);
+
+ 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 0000000000..0748ec90d6
--- /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 0000000000..65526ba67e
--- /dev/null
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/MonitorTask.java
@@ -0,0 +1,114 @@
+// ========================================================================
+// 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.log.Logger;
+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 final Logger LOG = Log.getLogger(MonitorTask.class);
+
+ private static Timer __timer = new Timer(true);
+ private static ThreadPool _callback = new ExecutorThreadPool(4,64,60,TimeUnit.SECONDS);;
+ private static Map<String,TimerTask> __tasks = new HashMap<String,TimerTask>();
+
+ 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 0000000000..31e85ce9bf
--- /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<EventNotifier> _group;
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Create a notifier group
+ */
+ public NotifierGroup()
+ {
+ _group = new HashSet<EventNotifier>();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Retrieve all event notifier associated with this group
+ *
+ * @return collection of event notifiers
+ */
+ public Collection<EventNotifier> 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<EventNotifier> 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<EventNotifier> 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 0000000000..fff0911656
--- /dev/null
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/ServiceConnection.java
@@ -0,0 +1,167 @@
+// ========================================================================
+// 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;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+/**
+ * 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 static final Logger LOG = Log.getLogger(ServiceConnection.class);
+
+ 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 0000000000..4de09e304c
--- /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
index 5088111653..b457dc03ab 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
index 0ac4bea9af..ef2b71d115 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 0000000000..c590152f6d
--- /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<EventTrigger> _triggers;
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Construct an event trigger
+ */
+ public AggregateEventTrigger()
+ {
+ _triggers = new ArrayList<EventTrigger>();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * 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<EventTrigger> triggers)
+ {
+ _triggers = new ArrayList<EventTrigger>(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<EventTrigger> 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 0000000000..c2910b31e9
--- /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<EventTrigger> _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<EventTrigger> 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 0000000000..ee85ac36f0
--- /dev/null
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AttrEventTrigger.java
@@ -0,0 +1,235 @@
+// ========================================================================
+// 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;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+/**
+ * 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<TYPE extends Comparable<TYPE>>
+ extends EventTrigger
+{
+ private static final Logger LOG = Log.getLogger(AttrEventTrigger.class);
+
+ private final ObjectName _nameObject;
+
+ protected final String _objectName;
+ protected final String _attributeName;
+ protected Map<Long, EventState<TYPE>> _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<Long,EventState<TYPE>>();
+
+ _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<Long,EventState<TYPE>>();
+
+ _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<TYPE>)
+ * 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<TYPE>(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<TYPE> 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<TYPE> 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 0000000000..70ff65daff
--- /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<TYPE extends Comparable<TYPE>> extends AttrEventTrigger<TYPE>
+{
+ 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<TYPE> 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 0000000000..9d86b7dd7c
--- /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<TYPE extends Comparable<TYPE>> extends AttrEventTrigger<TYPE>
+{
+ 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<TYPE> 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/GreaterThanOrEqualToAttrEventTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/GreaterThanOrEqualToAttrEventTrigger.java
new file mode 100644
index 0000000000..a5a97b786a
--- /dev/null
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/GreaterThanOrEqualToAttrEventTrigger.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;
+
+
+/* ------------------------------------------------------------ */
+/**
+ * GreaterThanOrEqualToAttrEventTrigger
+ *
+ * Event trigger that polls a value of an MXBean attribute and
+ * checks if it is greater than or equal to specified min value.
+ */
+public class GreaterThanOrEqualToAttrEventTrigger<TYPE extends Comparable<TYPE>> extends AttrEventTrigger<TYPE>
+{
+ 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<TYPE> 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 0000000000..7a002433d0
--- /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<TYPE extends Comparable<TYPE>> extends AttrEventTrigger<TYPE>
+{
+ 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<TYPE> 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/LessThanOrEqualToAttrEventTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/LessThanOrEqualToAttrEventTrigger.java
new file mode 100644
index 0000000000..1d3d7b2e31
--- /dev/null
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/LessThanOrEqualToAttrEventTrigger.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;
+
+
+/* ------------------------------------------------------------ */
+/**
+ * LessThanOrEqualToAttrEventTrigger
+ *
+ * Event trigger that polls a value of an MXBean attribute and
+ * checks if it is less than or equal to specified max value.
+ */
+public class LessThanOrEqualToAttrEventTrigger<TYPE extends Comparable<TYPE>> extends AttrEventTrigger<TYPE>
+{
+ 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<TYPE> 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 0000000000..632ac5ae8f
--- /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<EventTrigger> _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<EventTrigger> 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 0000000000..d9140f4d4d
--- /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<TYPE extends Comparable<TYPE>> extends AttrEventTrigger<TYPE>
+{
+ 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<TYPE> 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/java/org/eclipse/jetty/monitor/triggers/RangeInclAttrEventTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/RangeInclAttrEventTrigger.java
new file mode 100644
index 0000000000..b333924a49
--- /dev/null
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/RangeInclAttrEventTrigger.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;
+
+
+/* ------------------------------------------------------------ */
+/**
+ * RangeInclAttrEventTrigger
+ *
+ * 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 including the range bounds.
+ */
+public class RangeInclAttrEventTrigger<TYPE extends Comparable<TYPE>> extends AttrEventTrigger<TYPE>
+{
+ 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<TYPE> 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 0000000000..4722fdaa79
--- /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 0000000000..5cc7bdfd2d
--- /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<Long> trigger =
+ new AttrEventTrigger<Long>("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<Long> trigger =
+ new EqualToAttrEventTrigger<Long>("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<Long> trigger =
+ new EqualToAttrEventTrigger<Long>("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<Long> trigger =
+ new GreaterThanAttrEventTrigger<Long>("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<Long> trigger =
+ new GreaterThanOrEqualToAttrEventTrigger<Long>("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<Long> trigger =
+ new LessThanAttrEventTrigger<Long>("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<Long> trigger =
+ new LessThanOrEqualToAttrEventTrigger<Long>("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<Long> trigger =
+ new RangeInclAttrEventTrigger<Long>("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<Long> trigger =
+ new RangeAttrEventTrigger<Long>("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<Long> trigger1 =
+ new GreaterThanAttrEventTrigger<Long>("org.eclipse.jetty.monitor:type=requestcounter,id=0", "counter",
+ testRangeLow);
+ LessThanOrEqualToAttrEventTrigger<Long> trigger2 =
+ new LessThanOrEqualToAttrEventTrigger<Long>("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<Long> trigger1 =
+ new LessThanOrEqualToAttrEventTrigger<Long>("org.eclipse.jetty.monitor:type=requestcounter,id=0", "counter",
+ testRangeLow);
+ GreaterThanAttrEventTrigger<Long> trigger2 =
+ new GreaterThanAttrEventTrigger<Long>("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<Long>
+ {
+ 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 0000000000..6213ce7aa2
--- /dev/null
+++ b/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/JavaMonitorIntegrationTest.java
@@ -0,0 +1,166 @@
+// ========================================================================
+// 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.log.Logger;
+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 final Logger LOG = Log.getLogger(JavaMonitorIntegrationTest.class);
+
+ 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 0000000000..994a24d715
--- /dev/null
+++ b/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/JmxServiceTest.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.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.log.Logger;
+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 final Logger LOG = Log.getLogger(JmxServiceTest.class);
+
+ 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 0000000000..b111a952a1
--- /dev/null
+++ b/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/ProgramConfigTest.java
@@ -0,0 +1,182 @@
+// ========================================================================
+// 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.log.Logger;
+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 final Logger LOG = Log.getLogger(ProgramConfigTest.class);
+
+ 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<Integer> trigger1 =
+ new LessThanOrEqualToAttrEventTrigger<Integer>("org.eclipse.jetty.util.thread:type=queuedthreadpool,id=0", "idleThreads",
+ testRangeLow);
+ GreaterThanAttrEventTrigger<Integer> trigger2 =
+ new GreaterThanAttrEventTrigger<Integer>("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 0000000000..5b93f13b54
--- /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 0000000000..612be3c135
--- /dev/null
+++ b/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/XmlConfigTest.java
@@ -0,0 +1,162 @@
+// ========================================================================
+// 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.log.Logger;
+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 final Logger LOG = Log.getLogger(XmlConfigTest.class);
+
+ 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 0000000000..8b4f49fa62
--- /dev/null
+++ b/jetty-monitor/src/test/resources/monitor/etc/jetty-jmx.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+
+<!-- ============================================================================ -->
+<!-- To correctly start Jetty with JMX module enabled, this configuration -->
+<!-- file must appear first in the list of the configuration files. -->
+<!-- The simplest way to achieve this is to add etc/jetty-jmx.xml as the -->
+<!-- first file in configuration file list at the end of start.ini file. -->
+<!-- ============================================================================ -->
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+ <!-- =========================================================== -->
+ <!-- Initialize an mbean server -->
+ <!-- =========================================================== -->
+ <Call id="MBeanServer" class="java.lang.management.ManagementFactory"
+ name="getPlatformMBeanServer" />
+
+ <!-- =========================================================== -->
+ <!-- Initialize the Jetty MBean container -->
+ <!-- =========================================================== -->
+ <New id="MBeanContainer" class="org.eclipse.jetty.jmx.MBeanContainer">
+ <Arg>
+ <Ref id="MBeanServer" />
+ </Arg>
+ </New>
+
+ <!-- Add to the Server to listen for object events -->
+ <Get id="Container" name="container">
+ <Call name="addEventListener">
+ <Arg>
+ <Ref id="MBeanContainer" />
+ </Arg>
+ </Call>
+ </Get>
+
+ <!-- Add to the Server as a lifecycle -->
+ <!-- Only do this if you know you will only have a single jetty server -->
+ <Call name="addBean">
+ <Arg>
+ <Ref id="MBeanContainer" />
+ </Arg>
+ </Call>
+
+ <!-- Add the static log -->
+ <Get id="Logger" class="org.eclipse.jetty.util.log.Log" name="log" />
+ <Ref id="MBeanContainer">
+ <Call name="addBean">
+ <Arg>
+ <Ref id="Logger" />
+ </Arg>
+ </Call>
+ </Ref>
+
+ <!-- In order to connect to the JMX server remotely from a different
+ process, possibly running on a different host, Jetty JMX module
+ can create a remote JMX connector.
+ -->
+
+
+ <!-- Optionally add a remote JMX connector. The parameters of the constructor
+ below specify the JMX service URL, and the object name string for the
+ connector server bean. The parameters of the JMXServiceURL constructor
+ specify the protocol that clients will use to connect to the remote JMX
+ connector (RMI), the hostname of the server (local hostname), port number
+ (automatically assigned), and the URL path. Note that URL path contains
+ the RMI registry hostname and port number, that may need to be modified
+ in order to comply with the firewall requirements.
+ -->
+ <New id="ConnectorServer" class="org.eclipse.jetty.jmx.ConnectorServer">
+ <Arg>
+ <New class="javax.management.remote.JMXServiceURL">
+ <Arg type="java.lang.String">rmi</Arg>
+ <Arg type="java.lang.String" />
+ <Arg type="java.lang.Integer">0</Arg>
+ <Arg type="java.lang.String">/jndi/rmi://localhost:0/jettyjmx</Arg>
+ </New>
+ </Arg>
+ <Arg>org.eclipse.jetty:name=rmiconnectorserver</Arg>
+ <Call name="start" />
+ </New>
+</Configure>
+
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 0000000000..fe5d7bd523
--- /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 0000000000..757507c6c8
--- /dev/null
+++ b/jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/java-monitor-integration.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+
+<!--
+// ========================================================================
+// 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.apache.org/licenses/LICENSE-2.0.txt
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+-->
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+<!--
+<Call id="MBeanServer" class="java.lang.management.ManagementFactory" name="getPlatformMBeanServer">
+ <Call name="registerMBean">
+ <Arg><New class="com.javamonitor.mbeans.DNSCachePolicy" /></Arg>
+ <Arg>
+ <New class="javax.management.ObjectName">
+ <Arg>com.javamonitor:type=DNSCachePolicy</Arg>
+ </New>
+ </Arg>
+ </Call>
+ <Call name="registerMBean">
+ <Arg><New class="com.javamonitor.mbeans.Threading" /></Arg>
+ <Arg>
+ <New class="javax.management.ObjectName">
+ <Arg>com.javamonitor:type=Threading</Arg>
+ </New>
+ </Arg>
+ </Call>
+</Call>
+-->
+
+<Call id="JMXMonitor" class="org.eclipse.jetty.monitor.JMXMonitor" name="getInstance">
+ <Call name="addActions">
+ <Arg>
+ <Array type="org.eclipse.jetty.monitor.jmx.MonitorAction">
+ <Item>
+ <New id="MonitorAction" class="org.eclipse.jetty.monitor.integration.JavaMonitorAction">
+ <Arg />
+ <Arg>http://194.109.206.51/lemongrass/1.1/push</Arg>
+ <Arg>57e48e79-f0e6-4909-a6da-e8c1267cbf49</Arg>
+ <Arg>8080</Arg>
+ <Arg type="java.lang.Integer">15000</Arg>
+ </New>
+ </Item>
+ </Array>
+ </Arg>
+ </Call>
+</Call>
+</Configure>
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 0000000000..bf86f3e0e2
--- /dev/null
+++ b/jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/jetty-monitor-service.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+
+<!--
+// ========================================================================
+// 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.apache.org/licenses/LICENSE-2.0.txt
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+-->
+
+<Configure id="Monitor" class="org.eclipse.jetty.monitor.JMXMonitor">
+ <Call name="setUrl">
+ <Arg>service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jettyjmx</Arg>
+ </Call>
+ <Call name="addActions">
+ <Arg>
+ <Array type="org.eclipse.jetty.monitor.jmx.MonitorAction">
+ <Item>
+ <New class="org.eclipse.jetty.monitor.jmx.SimpleAction">
+ <Arg>
+ <New class="org.eclipse.jetty.monitor.triggers.OrEventTrigger">
+ <Arg>
+ <Array type="org.eclipse.jetty.monitor.jmx.EventTrigger">
+ <Item>
+ <New
+ class="org.eclipse.jetty.monitor.triggers.LessThanOrEqualToAttrEventTrigger">
+ <Arg>org.eclipse.jetty.util.thread:type=queuedthreadpool,id=0
+ </Arg>
+ <Arg>idleThreads</Arg>
+ <Arg type="java.lang.Integer">4</Arg>
+ </New>
+ </Item>
+ <Item>
+ <New
+ class="org.eclipse.jetty.monitor.triggers.GreaterThanAttrEventTrigger">
+ <Arg>org.eclipse.jetty.util.thread:type=queuedthreadpool,id=0
+ </Arg>
+ <Arg>idleThreads</Arg>
+ <Arg type="java.lang.Integer">7</Arg>
+ </New>
+ </Item>
+ </Array>
+ </Arg>
+ </New>
+ </Arg>
+ <Arg>
+ <New class="org.eclipse.jetty.monitor.jmx.ConsoleNotifier">
+ <Arg>%s</Arg>
+ </New>
+ </Arg>
+ <Arg type="java.lang.Long">500</Arg>
+ </New>
+ </Item>
+ </Array>
+ </Arg>
+ </Call>
+</Configure> \ 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 0000000000..49843770c8
--- /dev/null
+++ b/jetty-monitor/src/test/resources/org/eclipse/jetty/monitor/jetty-monitor-test.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+
+<!--
+// ========================================================================
+// 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.apache.org/licenses/LICENSE-2.0.txt
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+-->
+
+<Configure id="Monitor" class="org.eclipse.jetty.monitor.JMXMonitor">
+ <Call name="addActions">
+ <Arg>
+ <Array type="org.eclipse.jetty.monitor.jmx.MonitorAction">
+ <Item>
+ <New class="org.eclipse.jetty.monitor.jmx.SimpleAction">
+ <Arg>
+ <New class="org.eclipse.jetty.monitor.triggers.OrEventTrigger">
+ <Arg>
+ <Array type="org.eclipse.jetty.monitor.jmx.EventTrigger">
+ <Item>
+ <New
+ class="org.eclipse.jetty.monitor.triggers.LessThanOrEqualToAttrEventTrigger">
+ <Arg>org.eclipse.jetty.util.thread:type=queuedthreadpool,id=0
+ </Arg>
+ <Arg>idleThreads</Arg>
+ <Arg type="java.lang.Integer">4</Arg>
+ </New>
+ </Item>
+ <Item>
+ <New
+ class="org.eclipse.jetty.monitor.triggers.GreaterThanAttrEventTrigger">
+ <Arg>org.eclipse.jetty.util.thread:type=queuedthreadpool,id=0
+ </Arg>
+ <Arg>idleThreads</Arg>
+ <Arg type="java.lang.Integer">7</Arg>
+ </New>
+ </Item>
+ </Array>
+ </Arg>
+ </New>
+ </Arg>
+ <Arg>
+ <New class="org.eclipse.jetty.monitor.jmx.ConsoleNotifier">
+ <Arg>%s</Arg>
+ </New>
+ </Arg>
+ <Arg type="java.lang.Long">500</Arg>
+ </New>
+ </Item>
+ </Array>
+ </Arg>
+ </Call>
+</Configure> \ 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 0000000000..1685bfd612
--- /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

Back to the top