Merge remote-tracking branch 'origin/master' into servlet-3.1-api
Conflicts: jetty-jsp/pom.xml
This commit is contained in:
commit
7523c5234c
|
@ -0,0 +1,4 @@
|
|||
language: java
|
||||
jdk:
|
||||
- openjdk7
|
||||
- oraclejdk7
|
|
@ -1,5 +1,6 @@
|
|||
This is a source checkout of the Jetty webserver.
|
||||
|
||||
|
||||
To build, use:
|
||||
|
||||
mvn clean install
|
||||
|
|
|
@ -150,11 +150,11 @@ public class LikeJettyXml
|
|||
|
||||
// === jetty-requestlog.xml ===
|
||||
NCSARequestLog requestLog = new NCSARequestLog();
|
||||
requestLog.setFilename(jetty_home + "/logs/jetty-yyyy_mm_dd.log");
|
||||
requestLog.setFilename(jetty_home + "/logs/yyyy_mm_dd.request.log");
|
||||
requestLog.setFilenameDateFormat("yyyy_MM_dd");
|
||||
requestLog.setRetainDays(90);
|
||||
requestLog.setAppend(true);
|
||||
requestLog.setExtended(false);
|
||||
requestLog.setExtended(true);
|
||||
requestLog.setLogCookies(false);
|
||||
requestLog.setLogTimeZone("GMT");
|
||||
RequestLogHandler requestLogHandler = new RequestLogHandler();
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.eclipse.jetty.deploy.providers.WebAppProvider;
|
|||
import org.eclipse.jetty.jmx.MBeanContainer;
|
||||
import org.eclipse.jetty.security.HashLoginService;
|
||||
import org.eclipse.jetty.server.ForwardedRequestCustomizer;
|
||||
import org.eclipse.jetty.server.AsyncNCSARequestLog;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
|
@ -152,7 +153,8 @@ public class SpdyServer
|
|||
login.setConfig(jetty_home + "/etc/realm.properties");
|
||||
server.addBean(login);
|
||||
|
||||
NCSARequestLog requestLog = new NCSARequestLog(jetty_home + "/logs/jetty-yyyy_mm_dd.log");
|
||||
NCSARequestLog requestLog = new AsyncNCSARequestLog();
|
||||
requestLog.setFilename(jetty_home + "/logs/jetty-yyyy_mm_dd.log");
|
||||
requestLog.setExtended(false);
|
||||
requestLogHandler.setRequestLog(requestLog);
|
||||
|
||||
|
|
|
@ -326,10 +326,10 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
|
||||
//Convert from Resource to URI
|
||||
ArrayList<URI> containerUris = new ArrayList<URI>();
|
||||
for (Resource r : context.getMetaData().getOrderedContainerJars())
|
||||
for (Resource r : context.getMetaData().getContainerResources())
|
||||
{
|
||||
URI uri = r.getURI();
|
||||
containerUris.add(uri);
|
||||
containerUris.add(uri);
|
||||
}
|
||||
|
||||
parser.parse (containerUris.toArray(new URI[containerUris.size()]),
|
||||
|
@ -457,24 +457,23 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
parser.registerHandler(_classInheritanceHandler);
|
||||
parser.registerHandlers(_containerInitializerAnnotationHandlers);
|
||||
|
||||
parser.parse(classesDir,
|
||||
new ClassNameResolver()
|
||||
{
|
||||
public boolean isExcluded (String name)
|
||||
{
|
||||
if (context.isSystemClass(name)) return true;
|
||||
if (context.isServerClass(name)) return false;
|
||||
return false;
|
||||
}
|
||||
parser.parseDir(classesDir,
|
||||
new ClassNameResolver()
|
||||
{
|
||||
public boolean isExcluded (String name)
|
||||
{
|
||||
if (context.isSystemClass(name)) return true;
|
||||
if (context.isServerClass(name)) return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean shouldOverride (String name)
|
||||
{
|
||||
//looking at webapp classpath, found already-parsed class of same name - did it come from system or duplicate in webapp?
|
||||
if (context.isParentLoaderPriority())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
public boolean shouldOverride (String name)
|
||||
{
|
||||
//looking at webapp classpath, found already-parsed class of same name - did it come from system or duplicate in webapp?
|
||||
if (context.isParentLoaderPriority()) return false;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarInputStream;
|
||||
|
||||
import org.eclipse.jetty.util.Loader;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
|
@ -749,13 +750,14 @@ public class AnnotationParser
|
|||
* @param resolver
|
||||
* @throws Exception
|
||||
*/
|
||||
public void parse (Resource dir, ClassNameResolver resolver)
|
||||
public void parseDir (Resource dir, ClassNameResolver resolver)
|
||||
throws Exception
|
||||
{
|
||||
if (!dir.isDirectory() || !dir.exists())
|
||||
return;
|
||||
|
||||
|
||||
if (LOG.isDebugEnabled()) {LOG.debug("Scanning dir {}", dir);};
|
||||
|
||||
String[] files=dir.list();
|
||||
for (int f=0;files!=null && f<files.length;f++)
|
||||
{
|
||||
|
@ -763,13 +765,14 @@ public class AnnotationParser
|
|||
{
|
||||
Resource res = dir.addPath(files[f]);
|
||||
if (res.isDirectory())
|
||||
parse(res, resolver);
|
||||
parseDir(res, resolver);
|
||||
String name = res.getName();
|
||||
if (name.endsWith(".class"))
|
||||
{
|
||||
if ((resolver == null)|| (!resolver.isExcluded(name) && (!isParsed(name) || resolver.shouldOverride(name))))
|
||||
{
|
||||
Resource r = Resource.newResource(res.getURL());
|
||||
if (LOG.isDebugEnabled()) {LOG.debug("Scanning class {}", r);};
|
||||
scanClass(r.getInputStream());
|
||||
}
|
||||
|
||||
|
@ -836,7 +839,7 @@ public class AnnotationParser
|
|||
|
||||
|
||||
/**
|
||||
* Parse classes in the supplied url of jar files.
|
||||
* Parse classes in the supplied uris.
|
||||
*
|
||||
* @param uris
|
||||
* @param resolver
|
||||
|
@ -848,36 +851,18 @@ public class AnnotationParser
|
|||
if (uris==null)
|
||||
return;
|
||||
|
||||
JarScanner scanner = new JarScanner()
|
||||
for (URI uri:uris)
|
||||
{
|
||||
@Override
|
||||
public void processEntry(URI jarUri, JarEntry entry)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
String name = entry.getName();
|
||||
if (name.toLowerCase(Locale.ENGLISH).endsWith(".class"))
|
||||
{
|
||||
String shortName = name.replace('/', '.').substring(0,name.length()-6);
|
||||
|
||||
if ((resolver == null)
|
||||
||
|
||||
(!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
|
||||
{
|
||||
Resource clazz = Resource.newResource("jar:"+jarUri+"!/"+name);
|
||||
scanClass(clazz.getInputStream());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Problem processing jar entry "+entry, e);
|
||||
}
|
||||
parse(uri, resolver);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Problem parsing classes from {}", uri);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
scanner.scan(null, uris, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -891,10 +876,91 @@ public class AnnotationParser
|
|||
{
|
||||
if (uri == null)
|
||||
return;
|
||||
URI[] uris = {uri};
|
||||
parse(uris, resolver);
|
||||
|
||||
Resource r = Resource.newResource(uri);
|
||||
if (r.exists() && r.isDirectory())
|
||||
{
|
||||
parseDir(r, resolver);
|
||||
return;
|
||||
}
|
||||
|
||||
String fullname = r.toString();
|
||||
if (fullname.endsWith(".jar"))
|
||||
{
|
||||
parseJar(r, resolver);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fullname.endsWith(".class"))
|
||||
{
|
||||
scanClass(r.getInputStream());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Parse a resource that is a jar file.
|
||||
*
|
||||
* @param jarResource
|
||||
* @param resolver
|
||||
* @throws Exception
|
||||
*/
|
||||
public void parseJar (Resource jarResource, final ClassNameResolver resolver)
|
||||
throws Exception
|
||||
{
|
||||
if (jarResource == null)
|
||||
return;
|
||||
|
||||
URI uri = jarResource.getURI();
|
||||
|
||||
if (jarResource.toString().endsWith(".jar"))
|
||||
{
|
||||
if (LOG.isDebugEnabled()) {LOG.debug("Scanning jar {}", jarResource);};
|
||||
|
||||
//treat it as a jar that we need to open and scan all entries from
|
||||
InputStream in = jarResource.getInputStream();
|
||||
if (in==null)
|
||||
return;
|
||||
|
||||
JarInputStream jar_in = new JarInputStream(in);
|
||||
try
|
||||
{
|
||||
JarEntry entry = jar_in.getNextJarEntry();
|
||||
while (entry!=null)
|
||||
{
|
||||
try
|
||||
{
|
||||
String name = entry.getName();
|
||||
if (name.toLowerCase(Locale.ENGLISH).endsWith(".class"))
|
||||
{
|
||||
String shortName = name.replace('/', '.').substring(0,name.length()-6);
|
||||
|
||||
if ((resolver == null)
|
||||
||
|
||||
(!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
|
||||
{
|
||||
Resource clazz = Resource.newResource("jar:"+uri+"!/"+name);
|
||||
if (LOG.isDebugEnabled()) {LOG.debug("Scanning class from jar {}", clazz);};
|
||||
scanClass(clazz.getInputStream());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Problem processing jar entry "+entry, e);
|
||||
}
|
||||
|
||||
entry = jar_in.getNextJarEntry();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
jar_in.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -66,7 +66,7 @@ public class AntWebInfConfiguration extends WebInfConfiguration
|
|||
{
|
||||
public void matched(URI uri) throws Exception
|
||||
{
|
||||
context.getMetaData().addContainerJar(Resource.newResource(uri));
|
||||
context.getMetaData().addContainerResource(Resource.newResource(uri));
|
||||
}
|
||||
};
|
||||
ClassLoader loader = context.getClassLoader();
|
||||
|
|
|
@ -35,33 +35,34 @@ import org.eclipse.jetty.http.HttpHeader;
|
|||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
public class AuthenticationProtocolHandler implements ProtocolHandler
|
||||
public abstract class AuthenticationProtocolHandler implements ProtocolHandler
|
||||
{
|
||||
public static final int DEFAULT_MAX_CONTENT_LENGTH = 4096;
|
||||
public static final Logger LOG = Log.getLogger(AuthenticationProtocolHandler.class);
|
||||
private static final Pattern WWW_AUTHENTICATE_PATTERN = Pattern.compile("([^\\s]+)\\s+realm=\"([^\"]+)\".*", Pattern.CASE_INSENSITIVE);
|
||||
private static final Pattern AUTHENTICATE_PATTERN = Pattern.compile("([^\\s]+)\\s+realm=\"([^\"]+)\"(.*)", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private final HttpClient client;
|
||||
private final int maxContentLength;
|
||||
private final ResponseNotifier notifier;
|
||||
|
||||
public AuthenticationProtocolHandler(HttpClient client)
|
||||
{
|
||||
this(client, 4096);
|
||||
}
|
||||
|
||||
public AuthenticationProtocolHandler(HttpClient client, int maxContentLength)
|
||||
protected AuthenticationProtocolHandler(HttpClient client, int maxContentLength)
|
||||
{
|
||||
this.client = client;
|
||||
this.maxContentLength = maxContentLength;
|
||||
this.notifier = new ResponseNotifier(client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(Request request, Response response)
|
||||
protected HttpClient getHttpClient()
|
||||
{
|
||||
return response.getStatus() == 401;
|
||||
return client;
|
||||
}
|
||||
|
||||
protected abstract HttpHeader getAuthenticateHeader();
|
||||
|
||||
protected abstract HttpHeader getAuthorizationHeader();
|
||||
|
||||
protected abstract URI getAuthenticationURI(Request request);
|
||||
|
||||
@Override
|
||||
public Response.Listener getResponseListener()
|
||||
{
|
||||
|
@ -89,23 +90,24 @@ public class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
return;
|
||||
}
|
||||
|
||||
List<WWWAuthenticate> wwwAuthenticates = parseWWWAuthenticate(response);
|
||||
if (wwwAuthenticates.isEmpty())
|
||||
HttpHeader header = getAuthenticateHeader();
|
||||
List<Authentication.HeaderInfo> headerInfos = parseAuthenticateHeader(response, header);
|
||||
if (headerInfos.isEmpty())
|
||||
{
|
||||
LOG.debug("Authentication challenge without WWW-Authenticate header");
|
||||
forwardFailureComplete(request, null, response, new HttpResponseException("HTTP protocol violation: 401 without WWW-Authenticate header", response));
|
||||
LOG.debug("Authentication challenge without {} header", header);
|
||||
forwardFailureComplete(request, null, response, new HttpResponseException("HTTP protocol violation: Authentication challenge without " + header + " header", response));
|
||||
return;
|
||||
}
|
||||
|
||||
final URI uri = request.getURI();
|
||||
URI uri = getAuthenticationURI(request);
|
||||
Authentication authentication = null;
|
||||
WWWAuthenticate wwwAuthenticate = null;
|
||||
for (WWWAuthenticate wwwAuthn : wwwAuthenticates)
|
||||
Authentication.HeaderInfo headerInfo = null;
|
||||
for (Authentication.HeaderInfo element : headerInfos)
|
||||
{
|
||||
authentication = client.getAuthenticationStore().findAuthentication(wwwAuthn.type, uri, wwwAuthn.realm);
|
||||
authentication = client.getAuthenticationStore().findAuthentication(element.getType(), uri, element.getRealm());
|
||||
if (authentication != null)
|
||||
{
|
||||
wwwAuthenticate = wwwAuthn;
|
||||
headerInfo = element;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -117,7 +119,7 @@ public class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
}
|
||||
|
||||
HttpConversation conversation = client.getConversation(request.getConversationID(), false);
|
||||
final Authentication.Result authnResult = authentication.authenticate(request, response, wwwAuthenticate.value, conversation);
|
||||
final Authentication.Result authnResult = authentication.authenticate(request, response, headerInfo, conversation);
|
||||
LOG.debug("Authentication result {}", authnResult);
|
||||
if (authnResult == null)
|
||||
{
|
||||
|
@ -125,7 +127,7 @@ public class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
return;
|
||||
}
|
||||
|
||||
Request newRequest = client.copyRequest(request, uri);
|
||||
Request newRequest = client.copyRequest(request, request.getURI());
|
||||
authnResult.apply(newRequest);
|
||||
newRequest.onResponseSuccess(new Response.SuccessListener()
|
||||
{
|
||||
|
@ -151,37 +153,24 @@ public class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
notifier.forwardFailureComplete(conversation.getResponseListeners(), request, requestFailure, response, responseFailure);
|
||||
}
|
||||
|
||||
private List<WWWAuthenticate> parseWWWAuthenticate(Response response)
|
||||
private List<Authentication.HeaderInfo> parseAuthenticateHeader(Response response, HttpHeader header)
|
||||
{
|
||||
// TODO: these should be ordered by strength
|
||||
List<WWWAuthenticate> result = new ArrayList<>();
|
||||
List<String> values = Collections.list(response.getHeaders().getValues(HttpHeader.WWW_AUTHENTICATE.asString()));
|
||||
List<Authentication.HeaderInfo> result = new ArrayList<>();
|
||||
List<String> values = Collections.list(response.getHeaders().getValues(header.asString()));
|
||||
for (String value : values)
|
||||
{
|
||||
Matcher matcher = WWW_AUTHENTICATE_PATTERN.matcher(value);
|
||||
Matcher matcher = AUTHENTICATE_PATTERN.matcher(value);
|
||||
if (matcher.matches())
|
||||
{
|
||||
String type = matcher.group(1);
|
||||
String realm = matcher.group(2);
|
||||
WWWAuthenticate wwwAuthenticate = new WWWAuthenticate(value, type, realm);
|
||||
result.add(wwwAuthenticate);
|
||||
String params = matcher.group(3);
|
||||
Authentication.HeaderInfo headerInfo = new Authentication.HeaderInfo(type, realm, params, getAuthorizationHeader());
|
||||
result.add(headerInfo);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private class WWWAuthenticate
|
||||
{
|
||||
private final String value;
|
||||
private final String type;
|
||||
private final String realm;
|
||||
|
||||
public WWWAuthenticate(String value, String type, String realm)
|
||||
{
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
this.realm = realm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,6 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
|
@ -61,7 +60,6 @@ import org.eclipse.jetty.io.MappedByteBufferPool;
|
|||
import org.eclipse.jetty.io.SelectChannelEndPoint;
|
||||
import org.eclipse.jetty.io.SelectorManager;
|
||||
import org.eclipse.jetty.io.ssl.SslConnection;
|
||||
import org.eclipse.jetty.util.FuturePromise;
|
||||
import org.eclipse.jetty.util.Jetty;
|
||||
import org.eclipse.jetty.util.Promise;
|
||||
import org.eclipse.jetty.util.SocketAddressResolver;
|
||||
|
@ -209,7 +207,8 @@ public class HttpClient extends ContainerLifeCycle
|
|||
|
||||
handlers.add(new ContinueProtocolHandler(this));
|
||||
handlers.add(new RedirectProtocolHandler(this));
|
||||
handlers.add(new AuthenticationProtocolHandler(this));
|
||||
handlers.add(new WWWAuthenticationProtocolHandler(this));
|
||||
handlers.add(new ProxyAuthenticationProtocolHandler(this));
|
||||
|
||||
decoderFactories.add(new GZIPContentDecoder.Factory());
|
||||
|
||||
|
@ -502,8 +501,8 @@ public class HttpClient extends ContainerLifeCycle
|
|||
channel.configureBlocking(false);
|
||||
channel.connect(socketAddress);
|
||||
|
||||
Future<Connection> futureConnection = new ConnectionCallback(destination, promise);
|
||||
selectorManager.connect(channel, futureConnection);
|
||||
ConnectionCallback callback = new ConnectionCallback(destination, promise);
|
||||
selectorManager.connect(channel, callback);
|
||||
}
|
||||
// Must catch all exceptions, since some like
|
||||
// UnresolvedAddressException are not IOExceptions.
|
||||
|
@ -971,7 +970,7 @@ public class HttpClient extends ContainerLifeCycle
|
|||
HttpConnection connection = newHttpConnection(HttpClient.this, appEndPoint, destination);
|
||||
|
||||
appEndPoint.setConnection(connection);
|
||||
callback.promise.succeeded(connection);
|
||||
callback.succeeded(connection);
|
||||
|
||||
return sslConnection;
|
||||
}
|
||||
|
@ -979,7 +978,7 @@ public class HttpClient extends ContainerLifeCycle
|
|||
else
|
||||
{
|
||||
HttpConnection connection = newHttpConnection(HttpClient.this, endPoint, destination);
|
||||
callback.promise.succeeded(connection);
|
||||
callback.succeeded(connection);
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
|
@ -988,11 +987,11 @@ public class HttpClient extends ContainerLifeCycle
|
|||
protected void connectionFailed(SocketChannel channel, Throwable ex, Object attachment)
|
||||
{
|
||||
ConnectionCallback callback = (ConnectionCallback)attachment;
|
||||
callback.promise.failed(ex);
|
||||
callback.failed(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private class ConnectionCallback extends FuturePromise<Connection>
|
||||
private class ConnectionCallback implements Promise<Connection>
|
||||
{
|
||||
private final HttpDestination destination;
|
||||
private final Promise<Connection> promise;
|
||||
|
@ -1002,6 +1001,18 @@ public class HttpClient extends ContainerLifeCycle
|
|||
this.destination = destination;
|
||||
this.promise = promise;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void succeeded(Connection result)
|
||||
{
|
||||
promise.succeeded(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
{
|
||||
promise.failed(x);
|
||||
}
|
||||
}
|
||||
|
||||
private class ContentDecoderFactorySet implements Set<ContentDecoder.Factory>
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.eclipse.jetty.client;
|
|||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.HttpCookie;
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.UnsupportedCharsetException;
|
||||
import java.util.ArrayList;
|
||||
|
@ -251,7 +252,8 @@ public class HttpConnection extends AbstractConnection implements Connection
|
|||
request.header(HttpHeader.COOKIE.asString(), cookieString.toString());
|
||||
|
||||
// Authorization
|
||||
Authentication.Result authnResult = client.getAuthenticationStore().findAuthenticationResult(request.getURI());
|
||||
URI authenticationURI = destination.isProxied() ? destination.getProxyURI() : request.getURI();
|
||||
Authentication.Result authnResult = client.getAuthenticationStore().findAuthenticationResult(authenticationURI);
|
||||
if (authnResult != null)
|
||||
authnResult.apply(request);
|
||||
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
|
||||
package org.eclipse.jetty.client;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.channels.AsynchronousCloseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -45,7 +47,7 @@ import org.eclipse.jetty.util.component.Dumpable;
|
|||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
public class HttpDestination implements Destination, AutoCloseable, Dumpable
|
||||
public class HttpDestination implements Destination, Closeable, Dumpable
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(HttpDestination.class);
|
||||
|
||||
|
@ -140,6 +142,15 @@ public class HttpDestination implements Destination, AutoCloseable, Dumpable
|
|||
return proxyAddress != null;
|
||||
}
|
||||
|
||||
public URI getProxyURI()
|
||||
{
|
||||
ProxyConfiguration proxyConfiguration = client.getProxyConfiguration();
|
||||
String uri = getScheme() + "://" + proxyConfiguration.getHost();
|
||||
if (!client.isDefaultPort(getScheme(), proxyConfiguration.getPort()))
|
||||
uri += ":" + proxyConfiguration.getPort();
|
||||
return URI.create(uri);
|
||||
}
|
||||
|
||||
public HttpField getHostField()
|
||||
{
|
||||
return hostField;
|
||||
|
|
|
@ -300,8 +300,7 @@ public class HttpSender implements AsyncContentProvider.Listener
|
|||
}
|
||||
case SHUTDOWN_OUT:
|
||||
{
|
||||
EndPoint endPoint = connection.getEndPoint();
|
||||
endPoint.shutdownOutput();
|
||||
shutdownOutput();
|
||||
break;
|
||||
}
|
||||
case CONTINUE:
|
||||
|
@ -524,6 +523,8 @@ public class HttpSender implements AsyncContentProvider.Listener
|
|||
break;
|
||||
}
|
||||
|
||||
shutdownOutput();
|
||||
|
||||
exchange.terminateRequest();
|
||||
|
||||
HttpDestination destination = connection.getDestination();
|
||||
|
@ -551,6 +552,11 @@ public class HttpSender implements AsyncContentProvider.Listener
|
|||
return true;
|
||||
}
|
||||
|
||||
private void shutdownOutput()
|
||||
{
|
||||
connection.getEndPoint().shutdownOutput();
|
||||
}
|
||||
|
||||
public boolean abort(Throwable cause)
|
||||
{
|
||||
State current = state.get();
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.client;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
|
||||
public class ProxyAuthenticationProtocolHandler extends AuthenticationProtocolHandler
|
||||
{
|
||||
public ProxyAuthenticationProtocolHandler(HttpClient client)
|
||||
{
|
||||
this(client, DEFAULT_MAX_CONTENT_LENGTH);
|
||||
}
|
||||
|
||||
public ProxyAuthenticationProtocolHandler(HttpClient client, int maxContentLength)
|
||||
{
|
||||
super(client, maxContentLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(Request request, Response response)
|
||||
{
|
||||
return response.getStatus() == HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpHeader getAuthenticateHeader()
|
||||
{
|
||||
return HttpHeader.PROXY_AUTHENTICATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpHeader getAuthorizationHeader()
|
||||
{
|
||||
return HttpHeader.PROXY_AUTHORIZATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected URI getAuthenticationURI(Request request)
|
||||
{
|
||||
HttpDestination destination = getHttpClient().destinationFor(request.getScheme(), request.getHost(), request.getPort());
|
||||
return destination.isProxied() ? destination.getProxyURI() : request.getURI();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.client;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
|
||||
public class WWWAuthenticationProtocolHandler extends AuthenticationProtocolHandler
|
||||
{
|
||||
public WWWAuthenticationProtocolHandler(HttpClient client)
|
||||
{
|
||||
this(client, DEFAULT_MAX_CONTENT_LENGTH);
|
||||
}
|
||||
|
||||
public WWWAuthenticationProtocolHandler(HttpClient client, int maxContentLength)
|
||||
{
|
||||
super(client, maxContentLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(Request request, Response response)
|
||||
{
|
||||
return response.getStatus() == HttpStatus.UNAUTHORIZED_401;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpHeader getAuthenticateHeader()
|
||||
{
|
||||
return HttpHeader.WWW_AUTHENTICATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpHeader getAuthorizationHeader()
|
||||
{
|
||||
return HttpHeader.AUTHORIZATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected URI getAuthenticationURI(Request request)
|
||||
{
|
||||
return request.getURI();
|
||||
}
|
||||
}
|
|
@ -20,18 +20,19 @@ package org.eclipse.jetty.client.api;
|
|||
|
||||
import java.net.URI;
|
||||
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.util.Attributes;
|
||||
|
||||
/**
|
||||
* {@link Authentication} represents a mechanism to authenticate requests for protected resources.
|
||||
* <p />
|
||||
* {@link Authentication}s are added to an {@link AuthenticationStore}, which is then
|
||||
* {@link #matches(String, String, String) queried} to find the right
|
||||
* {@link #matches(String, URI, String) queried} to find the right
|
||||
* {@link Authentication} mechanism to use based on its type, URI and realm, as returned by
|
||||
* {@code WWW-Authenticate} response headers.
|
||||
* <p />
|
||||
* If an {@link Authentication} mechanism is found, it is then
|
||||
* {@link #authenticate(Request, ContentResponse, String, Attributes) executed} for the given request,
|
||||
* {@link #authenticate(Request, ContentResponse, HeaderInfo, Attributes) executed} for the given request,
|
||||
* returning an {@link Authentication.Result}, which is then stored in the {@link AuthenticationStore}
|
||||
* so that subsequent requests can be preemptively authenticated.
|
||||
*/
|
||||
|
@ -56,13 +57,64 @@ public interface Authentication
|
|||
*
|
||||
* @param request the request to execute the authentication mechanism for
|
||||
* @param response the 401 response obtained in the previous attempt to request the protected resource
|
||||
* @param wwwAuthenticate the {@code WWW-Authenticate} header chosen for this authentication
|
||||
* (among the many that the response may contain)
|
||||
* @param headerInfo the {@code WWW-Authenticate} (or {@code Proxy-Authenticate}) header chosen for this
|
||||
* authentication (among the many that the response may contain)
|
||||
* @param context the conversation context in case the authentication needs multiple exchanges
|
||||
* to be completed and information needs to be stored across exchanges
|
||||
* @return the authentication result, or null if the authentication could not be performed
|
||||
*/
|
||||
Result authenticate(Request request, ContentResponse response, String wwwAuthenticate, Attributes context);
|
||||
Result authenticate(Request request, ContentResponse response, HeaderInfo headerInfo, Attributes context);
|
||||
|
||||
/**
|
||||
* Structure holding information about the {@code WWW-Authenticate} (or {@code Proxy-Authenticate}) header.
|
||||
*/
|
||||
public static class HeaderInfo
|
||||
{
|
||||
private final String type;
|
||||
private final String realm;
|
||||
private final String params;
|
||||
private final HttpHeader header;
|
||||
|
||||
public HeaderInfo(String type, String realm, String params, HttpHeader header)
|
||||
{
|
||||
this.type = type;
|
||||
this.realm = realm;
|
||||
this.params = params;
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the authentication type (for example "Basic" or "Digest")
|
||||
*/
|
||||
public String getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the realm name
|
||||
*/
|
||||
public String getRealm()
|
||||
{
|
||||
return realm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return additional authentication parameters
|
||||
*/
|
||||
public String getParameters()
|
||||
{
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@code Authorization} (or {@code Proxy-Authorization}) header
|
||||
*/
|
||||
public HttpHeader getHeader()
|
||||
{
|
||||
return header;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Result} holds the information needed to authenticate a {@link Request} via {@link #apply(Request)}.
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
package org.eclipse.jetty.client.api;
|
||||
|
||||
import java.io.Closeable;
|
||||
|
||||
import org.eclipse.jetty.util.Promise;
|
||||
|
||||
/**
|
||||
|
@ -28,7 +30,7 @@ import org.eclipse.jetty.util.Promise;
|
|||
* may be created by applications that want to do their own connection management via
|
||||
* {@link Destination#newConnection(Promise)} and {@link Connection#close()}.
|
||||
*/
|
||||
public interface Connection extends AutoCloseable
|
||||
public interface Connection extends Closeable
|
||||
{
|
||||
/**
|
||||
* Sends a request with an associated response listener.
|
||||
|
|
|
@ -84,5 +84,22 @@ public interface Destination
|
|||
{
|
||||
return port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj) return true;
|
||||
if (obj == null || getClass() != obj.getClass()) return false;
|
||||
Address that = (Address)obj;
|
||||
return host.equals(that.host) && port == that.port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int result = host.hashCode();
|
||||
result = 31 * result + port;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,20 +71,22 @@ public class BasicAuthentication implements Authentication
|
|||
}
|
||||
|
||||
@Override
|
||||
public Result authenticate(Request request, ContentResponse response, String wwwAuthenticate, Attributes context)
|
||||
public Result authenticate(Request request, ContentResponse response, HeaderInfo headerInfo, Attributes context)
|
||||
{
|
||||
String encoding = StringUtil.__ISO_8859_1;
|
||||
String value = "Basic " + B64Code.encode(user + ":" + password, encoding);
|
||||
return new BasicResult(request.getURI(), value);
|
||||
return new BasicResult(headerInfo.getHeader(), uri, value);
|
||||
}
|
||||
|
||||
private static class BasicResult implements Result
|
||||
{
|
||||
private final HttpHeader header;
|
||||
private final URI uri;
|
||||
private final String value;
|
||||
|
||||
public BasicResult(URI uri, String value)
|
||||
public BasicResult(HttpHeader header, URI uri, String value)
|
||||
{
|
||||
this.header = header;
|
||||
this.uri = uri;
|
||||
this.value = value;
|
||||
}
|
||||
|
@ -98,8 +100,7 @@ public class BasicAuthentication implements Authentication
|
|||
@Override
|
||||
public void apply(Request request)
|
||||
{
|
||||
if (request.getURI().toString().startsWith(uri.toString()))
|
||||
request.header(HttpHeader.AUTHORIZATION, value);
|
||||
request.header(header, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
package org.eclipse.jetty.client.util;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
@ -77,7 +78,7 @@ import org.eclipse.jetty.client.api.Response;
|
|||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public class DeferredContentProvider implements AsyncContentProvider, AutoCloseable
|
||||
public class DeferredContentProvider implements AsyncContentProvider, Closeable
|
||||
{
|
||||
private static final ByteBuffer CLOSE = ByteBuffer.allocate(0);
|
||||
|
||||
|
|
|
@ -85,13 +85,9 @@ public class DigestAuthentication implements Authentication
|
|||
}
|
||||
|
||||
@Override
|
||||
public Result authenticate(Request request, ContentResponse response, String wwwAuthenticate, Attributes context)
|
||||
public Result authenticate(Request request, ContentResponse response, HeaderInfo headerInfo, Attributes context)
|
||||
{
|
||||
// Avoid case sensitivity problems on the 'D' character
|
||||
String type = "igest";
|
||||
wwwAuthenticate = wwwAuthenticate.substring(wwwAuthenticate.indexOf(type) + type.length());
|
||||
|
||||
Map<String, String> params = parseParams(wwwAuthenticate);
|
||||
Map<String, String> params = parseParameters(headerInfo.getParameters());
|
||||
String nonce = params.get("nonce");
|
||||
if (nonce == null || nonce.length() == 0)
|
||||
return null;
|
||||
|
@ -113,10 +109,10 @@ public class DigestAuthentication implements Authentication
|
|||
clientQOP = "auth-int";
|
||||
}
|
||||
|
||||
return new DigestResult(request.getURI(), response.getContent(), realm, user, password, algorithm, nonce, clientQOP, opaque);
|
||||
return new DigestResult(headerInfo.getHeader(), uri, response.getContent(), realm, user, password, algorithm, nonce, clientQOP, opaque);
|
||||
}
|
||||
|
||||
private Map<String, String> parseParams(String wwwAuthenticate)
|
||||
private Map<String, String> parseParameters(String wwwAuthenticate)
|
||||
{
|
||||
Map<String, String> result = new HashMap<>();
|
||||
List<String> parts = splitParams(wwwAuthenticate);
|
||||
|
@ -154,7 +150,9 @@ public class DigestAuthentication implements Authentication
|
|||
case ',':
|
||||
if (quotes % 2 == 0)
|
||||
{
|
||||
result.add(paramString.substring(start, i).trim());
|
||||
String element = paramString.substring(start, i).trim();
|
||||
if (element.length() > 0)
|
||||
result.add(element);
|
||||
start = i + 1;
|
||||
}
|
||||
break;
|
||||
|
@ -181,6 +179,7 @@ public class DigestAuthentication implements Authentication
|
|||
private class DigestResult implements Result
|
||||
{
|
||||
private final AtomicInteger nonceCount = new AtomicInteger();
|
||||
private final HttpHeader header;
|
||||
private final URI uri;
|
||||
private final byte[] content;
|
||||
private final String realm;
|
||||
|
@ -191,8 +190,9 @@ public class DigestAuthentication implements Authentication
|
|||
private final String qop;
|
||||
private final String opaque;
|
||||
|
||||
public DigestResult(URI uri, byte[] content, String realm, String user, String password, String algorithm, String nonce, String qop, String opaque)
|
||||
public DigestResult(HttpHeader header, URI uri, byte[] content, String realm, String user, String password, String algorithm, String nonce, String qop, String opaque)
|
||||
{
|
||||
this.header = header;
|
||||
this.uri = uri;
|
||||
this.content = content;
|
||||
this.realm = realm;
|
||||
|
@ -213,9 +213,6 @@ public class DigestAuthentication implements Authentication
|
|||
@Override
|
||||
public void apply(Request request)
|
||||
{
|
||||
if (!request.getURI().toString().startsWith(uri.toString()))
|
||||
return;
|
||||
|
||||
MessageDigest digester = getMessageDigest(algorithm);
|
||||
if (digester == null)
|
||||
return;
|
||||
|
@ -262,7 +259,7 @@ public class DigestAuthentication implements Authentication
|
|||
}
|
||||
value.append(", response=\"").append(hashA3).append("\"");
|
||||
|
||||
request.header(HttpHeader.AUTHORIZATION, value.toString());
|
||||
request.header(header, value.toString());
|
||||
}
|
||||
|
||||
private String nextNonceCount()
|
||||
|
|
|
@ -120,6 +120,8 @@ public class InputStreamContentProvider implements ContentProvider
|
|||
if (failure == null)
|
||||
{
|
||||
failure = x;
|
||||
// Signal we have more content to cause a call to
|
||||
// next() which will throw NoSuchElementException.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.eclipse.jetty.client;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.NetworkConnector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
|
@ -54,7 +55,7 @@ public abstract class AbstractHttpClientServerTest
|
|||
public AbstractHttpClientServerTest(SslContextFactory sslContextFactory)
|
||||
{
|
||||
this.sslContextFactory = sslContextFactory;
|
||||
this.scheme = sslContextFactory == null ? "http" : "https";
|
||||
this.scheme = (sslContextFactory == null ? HttpScheme.HTTP : HttpScheme.HTTPS).asString();
|
||||
}
|
||||
|
||||
public void start(Handler handler) throws Exception
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.ProxyConfiguration;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.util.BasicAuthentication;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.util.B64Code;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class HttpClientProxyTest extends AbstractHttpClientServerTest
|
||||
{
|
||||
public HttpClientProxyTest(SslContextFactory sslContextFactory)
|
||||
{
|
||||
// Avoid TLS otherwise CONNECT requests are sent instead of proxied requests
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProxiedRequest() throws Exception
|
||||
{
|
||||
final String serverHost = "server";
|
||||
final int status = HttpStatus.NO_CONTENT_204;
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
if (serverHost.equals(request.getServerName()))
|
||||
response.setStatus(status);
|
||||
}
|
||||
});
|
||||
|
||||
int proxyPort = connector.getLocalPort();
|
||||
int serverPort = proxyPort + 1; // Any port will do for these tests - just not the same as the proxy
|
||||
client.setProxyConfiguration(new ProxyConfiguration("localhost", proxyPort));
|
||||
|
||||
ContentResponse response = client.newRequest(serverHost, serverPort)
|
||||
.scheme(scheme)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertEquals(status, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthenticatedProxiedRequest() throws Exception
|
||||
{
|
||||
final String user = "foo";
|
||||
final String password = "bar";
|
||||
final String credentials = B64Code.encode(user + ":" + password, "ISO-8859-1");
|
||||
final String serverHost = "server";
|
||||
final String realm = "test_realm";
|
||||
final int status = HttpStatus.NO_CONTENT_204;
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
String authorization = request.getHeader(HttpHeader.PROXY_AUTHORIZATION.asString());
|
||||
if (authorization == null)
|
||||
{
|
||||
response.setStatus(HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407);
|
||||
response.setHeader(HttpHeader.PROXY_AUTHENTICATE.asString(), "Basic realm=\"" + realm + "\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
String prefix = "Basic ";
|
||||
if (authorization.startsWith(prefix))
|
||||
{
|
||||
String attempt = authorization.substring(prefix.length());
|
||||
if (credentials.equals(attempt))
|
||||
response.setStatus(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
String proxyHost = "localhost";
|
||||
int proxyPort = connector.getLocalPort();
|
||||
int serverPort = proxyPort + 1; // Any port will do for these tests - just not the same as the proxy
|
||||
client.setProxyConfiguration(new ProxyConfiguration(proxyHost, proxyPort));
|
||||
|
||||
ContentResponse response1 = client.newRequest(serverHost, serverPort)
|
||||
.scheme(scheme)
|
||||
.timeout(555, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
// No Authentication available => 407
|
||||
Assert.assertEquals(HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407, response1.getStatus());
|
||||
|
||||
// Add authentication...
|
||||
URI uri = URI.create(scheme + "://" + proxyHost + ":" + proxyPort);
|
||||
client.getAuthenticationStore().addAuthentication(new BasicAuthentication(uri, realm, user, password));
|
||||
final AtomicInteger requests = new AtomicInteger();
|
||||
client.getRequestListeners().add(new Request.Listener.Empty()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess(Request request)
|
||||
{
|
||||
requests.incrementAndGet();
|
||||
}
|
||||
});
|
||||
// ...and perform the request again => 407 + 204
|
||||
ContentResponse response2 = client.newRequest(serverHost, serverPort)
|
||||
.scheme(scheme)
|
||||
.timeout(555, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertEquals(status, response2.getStatus());
|
||||
Assert.assertEquals(2, requests.get());
|
||||
|
||||
// Now the authentication result is cached => 204
|
||||
requests.set(0);
|
||||
ContentResponse response3 = client.newRequest(serverHost, serverPort)
|
||||
.scheme(scheme)
|
||||
.timeout(555, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertEquals(status, response3.getStatus());
|
||||
Assert.assertEquals(1, requests.get());
|
||||
}
|
||||
}
|
|
@ -32,6 +32,7 @@ import java.nio.file.Paths;
|
|||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
@ -48,6 +49,7 @@ import org.eclipse.jetty.client.api.Result;
|
|||
import org.eclipse.jetty.client.util.BufferingResponseListener;
|
||||
import org.eclipse.jetty.client.util.BytesContentProvider;
|
||||
import org.eclipse.jetty.client.util.DeferredContentProvider;
|
||||
import org.eclipse.jetty.client.util.InputStreamContentProvider;
|
||||
import org.eclipse.jetty.client.util.InputStreamResponseListener;
|
||||
import org.eclipse.jetty.client.util.OutputStreamContentProvider;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
|
@ -443,6 +445,37 @@ public class HttpClientStreamTest extends AbstractHttpClientServerTest
|
|||
Assert.assertNull(failure.get());
|
||||
}
|
||||
|
||||
@Test(expected = ExecutionException.class)
|
||||
public void testInputStreamContentProviderThrowingWhileReading() throws Exception
|
||||
{
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
IO.copy(request.getInputStream(), response.getOutputStream());
|
||||
}
|
||||
});
|
||||
|
||||
final byte[] data = new byte[]{0, 1, 2, 3};
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.content(new InputStreamContentProvider(new InputStream()
|
||||
{
|
||||
private int index = 0;
|
||||
|
||||
@Override
|
||||
public int read() throws IOException
|
||||
{
|
||||
// Will eventually throw ArrayIndexOutOfBounds
|
||||
return data[index++];
|
||||
}
|
||||
}, data.length / 2))
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
}
|
||||
|
||||
@Test(expected = AsynchronousCloseException.class)
|
||||
public void testDownloadWithCloseBeforeContent() throws Exception
|
||||
{
|
||||
|
|
|
@ -219,7 +219,25 @@
|
|||
<outputDirectory>${assembly-directory}/lib/websocket</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
<execution>
|
||||
<id>copy-lib-monitor-deps</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-monitor</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>jar</type>
|
||||
<overWrite>true</overWrite>
|
||||
<outputDirectory>${assembly-directory}/lib/monitor</outputDirectory>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-orbit-servlet-api-deps</id>
|
||||
<phase>generate-resources</phase>
|
||||
|
@ -421,6 +439,11 @@
|
|||
<artifactId>jetty-jmx</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-monitor</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-start</artifactId>
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
#
|
||||
# <Arg><Property name="jetty.home" default="."/>/webapps/jetty.war</Arg>
|
||||
#
|
||||
# JETTY_PORT
|
||||
# JETTY_PORT (Deprecated - use JETTY_ARGS)
|
||||
# Override the default port for Jetty servers. If not set then the
|
||||
# default value in the xml configuration file will be used. The java
|
||||
# system property "jetty.port" will be set to this value for use in
|
||||
|
@ -76,6 +76,8 @@
|
|||
#
|
||||
# JETTY_ARGS
|
||||
# The default arguments to pass to jetty.
|
||||
# For example
|
||||
# JETTY_ARGS=jetty.port=8080 jetty.spdy.port=8443 jetty.secure.port=443
|
||||
#
|
||||
# JETTY_USER
|
||||
# if set, then used as a username to run the server as
|
||||
|
@ -106,7 +108,7 @@ findDirectory()
|
|||
|
||||
running()
|
||||
{
|
||||
local PID=$(head -n 1 "$1" 2>/dev/null) || return 1
|
||||
local PID=$(cat "$1" 2>/dev/null) || return 1
|
||||
kill -0 "$PID" 2>/dev/null
|
||||
}
|
||||
|
||||
|
@ -119,7 +121,7 @@ started()
|
|||
[ -z "$(grep STARTED $1)" ] || return 0
|
||||
[ -z "$(grep STOPPED $1)" ] || return 1
|
||||
[ -z "$(grep FAILED $1)" ] || return 1
|
||||
local PID=$(head -n 1 "$1" 2>/dev/null) || return 1
|
||||
local PID=$(cat "$2" 2>/dev/null) || return 1
|
||||
kill -0 "$PID" 2>/dev/null || return 1
|
||||
echo -n ". "
|
||||
done
|
||||
|
@ -342,7 +344,9 @@ if [ -z "$JETTY_PID" ]
|
|||
then
|
||||
JETTY_PID="$JETTY_RUN/jetty.pid"
|
||||
fi
|
||||
JAVA_OPTIONS+=("-Djetty.pid=$JETTY_PID")
|
||||
JETTY_STATE=$(dirname $JETTY_PID)/jetty.state
|
||||
JAVA_OPTIONS+=("-Djetty.state=$JETTY_STATE")
|
||||
rm -f $JETTY_STATE
|
||||
|
||||
##################################################
|
||||
# Setup JAVA if unset
|
||||
|
@ -473,11 +477,16 @@ case "$ACTION" in
|
|||
|
||||
fi
|
||||
|
||||
if started "$JETTY_PID"
|
||||
if expr "${CONFIGS[*]}" : '.*etc/jetty-started.xml.*' >/dev/null
|
||||
then
|
||||
echo "OK `date`"
|
||||
if started "$JETTY_STATE" "$JETTY_PID"
|
||||
then
|
||||
echo "OK `date`"
|
||||
else
|
||||
echo "FAILED `date`"
|
||||
fi
|
||||
else
|
||||
echo "FAILED `date`"
|
||||
echo "ok `date`"
|
||||
fi
|
||||
|
||||
;;
|
||||
|
@ -499,7 +508,7 @@ case "$ACTION" in
|
|||
rm -f "$JETTY_PID"
|
||||
echo OK
|
||||
else
|
||||
PID=$(head -n 1 "$JETTY_PID" 2>/dev/null)
|
||||
PID=$(cat "$JETTY_PID" 2>/dev/null)
|
||||
kill "$PID" 2>/dev/null
|
||||
|
||||
TIMEOUT=30
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<Call name="addLifeCycleListener">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.util.component.FileNoticeLifeCycleListener">
|
||||
<Arg><SystemProperty name="jetty.pid" default="./jetty.pid"/></Arg>
|
||||
<Arg><SystemProperty name="jetty.state" default="./jetty.state"/></Arg>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
|
|
|
@ -55,10 +55,6 @@
|
|||
# jetty.home=.
|
||||
# jetty.logs=./logs
|
||||
# jetty.host=0.0.0.0
|
||||
# jetty.port=8080
|
||||
# jetty.tls.port=8443
|
||||
# jetty.jmxrmihost=localhost
|
||||
# jetty.jmxrmiport=1099
|
||||
#===========================================================
|
||||
|
||||
|
||||
|
@ -89,11 +85,9 @@
|
|||
# Add the contents of the resources directory to the classpath
|
||||
# Add jars discovered in lib/ext to the classpath
|
||||
# Include the core jetty configuration file
|
||||
# Lookup additional ini files in start.d
|
||||
#-----------------------------------------------------------
|
||||
OPTIONS=Server,websocket,resources,ext
|
||||
etc/jetty.xml
|
||||
start.d/
|
||||
#===========================================================
|
||||
|
||||
#===========================================================
|
||||
|
@ -123,6 +117,8 @@ start.d/
|
|||
# enable --exec or use --exec-print (see above)
|
||||
#-----------------------------------------------------------
|
||||
OPTIONS=jmx
|
||||
# jetty.jmxrmihost=localhost
|
||||
# jetty.jmxrmiport=1099
|
||||
# -Dcom.sun.management.jmxremote
|
||||
etc/jetty-jmx.xml
|
||||
#===========================================================
|
||||
|
@ -146,38 +142,51 @@ OPTIONS=jsp
|
|||
#===========================================================
|
||||
# HTTP Connector
|
||||
#-----------------------------------------------------------
|
||||
# jetty.port=8080
|
||||
etc/jetty-http.xml
|
||||
#===========================================================
|
||||
|
||||
#===========================================================
|
||||
# SSL Context
|
||||
# For use by HTTPS and SPDY
|
||||
#-----------------------------------------------------------
|
||||
# jetty.keystore=etc/keystore
|
||||
# jetty.keystore.password=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
|
||||
# jetty.keymanager.password=OBF:1u2u1wml1z7s1z7a1wnl1u2g
|
||||
# jetty.truststore=etc/keystore
|
||||
# jetty.truststore.password=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
|
||||
# jetty.secure.port=8443
|
||||
# etc/jetty-ssl.xml
|
||||
#===========================================================
|
||||
|
||||
#===========================================================
|
||||
# HTTPS Connector
|
||||
#-----------------------------------------------------------
|
||||
# jetty.https.port=8443
|
||||
# etc/jetty-https.xml
|
||||
#===========================================================
|
||||
|
||||
#===========================================================
|
||||
# SPDY Connector
|
||||
#
|
||||
# SPDY should not be used with HTTPS. Use HTTPS or SPDY, but
|
||||
# not both, as SPDY also provides HTTPS support.
|
||||
# SPDY requires the NPN jar which must be separately downloaded:
|
||||
#
|
||||
# SPDY requires the NPN jar iwhich must be separately downloaded:
|
||||
# http://repo1.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.5.v20130313/npn-boot-1.1.5.v20130313.jar
|
||||
#
|
||||
# http://repo1.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.2.v20130305/npn-boot-1.1.2.v20130305.jar
|
||||
#
|
||||
# Which should be saved in lib/npn-boot-1.1.2.v20130305.jar
|
||||
# Which should be saved in lib/npn-boot-1.1.5.v20130313.jar
|
||||
#
|
||||
# To include the NPN jar on the boot path, you must either:
|
||||
#
|
||||
# a) enable --exec above and uncomment the -Xbootclass line
|
||||
# below
|
||||
#
|
||||
# b) Add -Xbootclasspath/p:lib/npn-boot-1.1.2.v20130305.jar
|
||||
# b) Add -Xbootclasspath/p:lib/npn-boot-1.1.5.v20130313.jar
|
||||
# to the command line when running jetty.
|
||||
#
|
||||
#-----------------------------------------------------------
|
||||
# OPTIONS=spdy
|
||||
# -Xbootclasspath/p:lib/npn-boot-1.1.2.v20130305.jar
|
||||
# -Xbootclasspath/p:lib/npn-boot-1.1.5.v20130313.jar
|
||||
# jetty.spdy.port=8443
|
||||
# etc/jetty-spdy.xml
|
||||
#===========================================================
|
||||
|
||||
|
@ -204,3 +213,9 @@ etc/jetty-requestlog.xml
|
|||
# etc/jetty-ipaccess.xml
|
||||
# etc/jetty-lowresources.xml
|
||||
#===========================================================
|
||||
|
||||
#===========================================================
|
||||
# Lookup additional ini files in start.d
|
||||
#-----------------------------------------------------------
|
||||
start.d/
|
||||
#===========================================================
|
||||
|
|
|
@ -39,12 +39,14 @@ import java.util.Set;
|
|||
import java.util.StringTokenizer;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.eclipse.jetty.util.ArrayTernaryTrie;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.DateCache;
|
||||
import org.eclipse.jetty.util.LazyList;
|
||||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||
import org.eclipse.jetty.util.StringMap;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.Trie;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
@ -1007,7 +1009,7 @@ public class HttpFields implements Iterable<HttpField>
|
|||
|
||||
private static final Float __one = new Float("1.0");
|
||||
private static final Float __zero = new Float("0.0");
|
||||
private static final StringMap<Float> __qualities = new StringMap<>();
|
||||
private static final Trie<Float> __qualities = new ArrayTernaryTrie<>();
|
||||
static
|
||||
{
|
||||
__qualities.put("*", __one);
|
||||
|
|
|
@ -22,11 +22,11 @@ import java.util.Collections;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.eclipse.jetty.util.ArrayTernaryTrie;
|
||||
import org.eclipse.jetty.util.LazyList;
|
||||
import org.eclipse.jetty.util.StringMap;
|
||||
import org.eclipse.jetty.util.Trie;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -78,14 +78,13 @@ public class PathMap<O> extends HashMap<String,O>
|
|||
}
|
||||
|
||||
/* --------------------------------------------------------------- */
|
||||
final StringMap<MappedEntry<O>> _prefixMap=new StringMap<>();
|
||||
final StringMap<MappedEntry<O>> _suffixMap=new StringMap<>();
|
||||
final StringMap<MappedEntry<O>> _exactMap=new StringMap<>();
|
||||
Trie<MappedEntry<O>> _prefixMap=new ArrayTernaryTrie<>(false);
|
||||
Trie<MappedEntry<O>> _suffixMap=new ArrayTernaryTrie<>(false);
|
||||
final Map<String,MappedEntry<O>> _exactMap=new HashMap<>();
|
||||
|
||||
List _defaultSingletonList=null;
|
||||
List<MappedEntry<O>> _defaultSingletonList=null;
|
||||
MappedEntry<O> _prefixDefault=null;
|
||||
MappedEntry<O> _default=null;
|
||||
final Set _entrySet;
|
||||
boolean _nodefault=false;
|
||||
|
||||
/* --------------------------------------------------------------- */
|
||||
|
@ -111,7 +110,6 @@ public class PathMap<O> extends HashMap<String,O>
|
|||
{
|
||||
super(capacity);
|
||||
_nodefault=noDefault;
|
||||
_entrySet=entrySet();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------- */
|
||||
|
@ -120,7 +118,6 @@ public class PathMap<O> extends HashMap<String,O>
|
|||
public PathMap(Map<String, ? extends O> m)
|
||||
{
|
||||
putAll(m);
|
||||
_entrySet=entrySet();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------- */
|
||||
|
@ -163,12 +160,15 @@ public class PathMap<O> extends HashMap<String,O>
|
|||
{
|
||||
String mapped=spec.substring(0,spec.length()-2);
|
||||
entry.setMapped(mapped);
|
||||
_prefixMap.put(mapped,entry);
|
||||
_exactMap.put(mapped,entry);
|
||||
_exactMap.put(spec.substring(0,spec.length()-1),entry);
|
||||
while (!_prefixMap.put(mapped,entry))
|
||||
_prefixMap=new ArrayTernaryTrie<>((ArrayTernaryTrie<MappedEntry<O>>)_prefixMap,1.5);
|
||||
}
|
||||
else if (spec.startsWith("*."))
|
||||
_suffixMap.put(spec.substring(2),entry);
|
||||
{
|
||||
String suffix=spec.substring(2);
|
||||
while(!_suffixMap.put(suffix,entry))
|
||||
_suffixMap=new ArrayTernaryTrie<>((ArrayTernaryTrie<MappedEntry<O>>)_suffixMap,1.5);
|
||||
}
|
||||
else if (spec.equals(URIUtil.SLASH))
|
||||
{
|
||||
if (_nodefault)
|
||||
|
@ -176,8 +176,7 @@ public class PathMap<O> extends HashMap<String,O>
|
|||
else
|
||||
{
|
||||
_default=entry;
|
||||
_defaultSingletonList=
|
||||
Collections.singletonList(_default);
|
||||
_defaultSingletonList=Collections.singletonList(_default);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -228,17 +227,22 @@ public class PathMap<O> extends HashMap<String,O>
|
|||
}
|
||||
|
||||
// try exact match
|
||||
entry=_exactMap.get(path,0,l);
|
||||
entry=_exactMap.get(path);
|
||||
if (entry!=null)
|
||||
return entry;
|
||||
|
||||
// prefix search
|
||||
int i=l;
|
||||
while((i=path.lastIndexOf('/',i-1))>=0)
|
||||
final Trie<PathMap.MappedEntry<O>> prefix_map=_prefixMap;
|
||||
while(i>=0)
|
||||
{
|
||||
entry=_prefixMap.get(path,0,i);
|
||||
if (entry!=null)
|
||||
entry=prefix_map.getBest(path,0,i);
|
||||
if (entry==null)
|
||||
break;
|
||||
String key = entry.getKey();
|
||||
if (key.length()-2>=path.length() || path.charAt(key.length()-2)=='/')
|
||||
return entry;
|
||||
i=key.length()-3;
|
||||
}
|
||||
|
||||
// Prefix Default
|
||||
|
@ -247,9 +251,10 @@ public class PathMap<O> extends HashMap<String,O>
|
|||
|
||||
// Extension search
|
||||
i=0;
|
||||
final Trie<PathMap.MappedEntry<O>> suffix_map=_suffixMap;
|
||||
while ((i=path.indexOf('.',i+1))>0)
|
||||
{
|
||||
entry=_suffixMap.get(path,i+1,l-i-1);
|
||||
entry=suffix_map.get(path,i+1,l-i-1);
|
||||
if (entry!=null)
|
||||
return entry;
|
||||
}
|
||||
|
@ -266,26 +271,31 @@ public class PathMap<O> extends HashMap<String,O>
|
|||
*/
|
||||
public Object getLazyMatches(String path)
|
||||
{
|
||||
MappedEntry entry;
|
||||
MappedEntry<O> entry;
|
||||
Object entries=null;
|
||||
|
||||
if (path==null)
|
||||
return LazyList.getList(entries);
|
||||
|
||||
int l=path.length();
|
||||
|
||||
// try exact match
|
||||
entry=_exactMap.get(path,0,l);
|
||||
entry=_exactMap.get(path);
|
||||
if (entry!=null)
|
||||
entries=LazyList.add(entries,entry);
|
||||
|
||||
// prefix search
|
||||
int i=l-1;
|
||||
while((i=path.lastIndexOf('/',i-1))>=0)
|
||||
int l=path.length();
|
||||
int i=l;
|
||||
final Trie<PathMap.MappedEntry<O>> prefix_map=_prefixMap;
|
||||
while(i>=0)
|
||||
{
|
||||
entry=_prefixMap.get(path,0,i);
|
||||
if (entry!=null)
|
||||
entry=prefix_map.getBest(path,0,i);
|
||||
if (entry==null)
|
||||
break;
|
||||
String key = entry.getKey();
|
||||
if (key.length()-2>=path.length() || path.charAt(key.length()-2)=='/')
|
||||
entries=LazyList.add(entries,entry);
|
||||
|
||||
i=key.length()-3;
|
||||
}
|
||||
|
||||
// Prefix Default
|
||||
|
@ -294,9 +304,10 @@ public class PathMap<O> extends HashMap<String,O>
|
|||
|
||||
// Extension search
|
||||
i=0;
|
||||
final Trie<PathMap.MappedEntry<O>> suffix_map=_suffixMap;
|
||||
while ((i=path.indexOf('.',i+1))>0)
|
||||
{
|
||||
entry=_suffixMap.get(path,i+1,l-i-1);
|
||||
entry=suffix_map.get(path,i+1,l-i-1);
|
||||
if (entry!=null)
|
||||
entries=LazyList.add(entries,entry);
|
||||
}
|
||||
|
@ -320,7 +331,7 @@ public class PathMap<O> extends HashMap<String,O>
|
|||
* @param path Path to match
|
||||
* @return List of Map.Entry instances key=pathSpec
|
||||
*/
|
||||
public List getMatches(String path)
|
||||
public List<Map.Entry<String,O>> getMatches(String path)
|
||||
{
|
||||
return LazyList.getList(getLazyMatches(path));
|
||||
}
|
||||
|
@ -333,7 +344,7 @@ public class PathMap<O> extends HashMap<String,O>
|
|||
*/
|
||||
public boolean containsMatch(String path)
|
||||
{
|
||||
MappedEntry match = getMatch(path);
|
||||
MappedEntry<?> match = getMatch(path);
|
||||
return match!=null && !match.equals(_default);
|
||||
}
|
||||
|
||||
|
@ -347,11 +358,7 @@ public class PathMap<O> extends HashMap<String,O>
|
|||
if (spec.equals("/*"))
|
||||
_prefixDefault=null;
|
||||
else if (spec.endsWith("/*"))
|
||||
{
|
||||
_prefixMap.remove(spec.substring(0,spec.length()-2));
|
||||
_exactMap.remove(spec.substring(0,spec.length()-1));
|
||||
_exactMap.remove(spec.substring(0,spec.length()-2));
|
||||
}
|
||||
else if (spec.startsWith("*."))
|
||||
_suffixMap.remove(spec.substring(2));
|
||||
else if (spec.equals(URIUtil.SLASH))
|
||||
|
@ -370,8 +377,8 @@ public class PathMap<O> extends HashMap<String,O>
|
|||
public void clear()
|
||||
{
|
||||
_exactMap.clear();
|
||||
_prefixMap.clear();
|
||||
_suffixMap.clear();
|
||||
_prefixMap=new ArrayTernaryTrie<>(false);
|
||||
_suffixMap=new ArrayTernaryTrie<>(false);
|
||||
_default=null;
|
||||
_defaultSingletonList=null;
|
||||
super.clear();
|
||||
|
@ -382,18 +389,18 @@ public class PathMap<O> extends HashMap<String,O>
|
|||
* @return true if match.
|
||||
*/
|
||||
public static boolean match(String pathSpec, String path)
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
return match(pathSpec, path, false);
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------- */
|
||||
/**
|
||||
* @return true if match.
|
||||
*/
|
||||
public static boolean match(String pathSpec, String path, boolean noDefault)
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
char c = pathSpec.charAt(0);
|
||||
if (c=='/')
|
||||
{
|
||||
|
@ -407,7 +414,7 @@ public class PathMap<O> extends HashMap<String,O>
|
|||
return path.regionMatches(path.length()-pathSpec.length()+1,
|
||||
pathSpec,1,pathSpec.length()-1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------- */
|
||||
private static boolean isPathWildcardMatch(String pathSpec, String path)
|
||||
|
|
|
@ -44,6 +44,7 @@ public class PathMapTest
|
|||
p.put("/", "8");
|
||||
p.put("/XXX:/YYY", "9");
|
||||
p.put("", "10");
|
||||
p.put("/\u20ACuro/*", "11");
|
||||
|
||||
String[][] tests = {
|
||||
{ "/abs/path", "1"},
|
||||
|
@ -62,7 +63,9 @@ public class PathMapTest
|
|||
{ "/suffix/path.tar.gz", "6"},
|
||||
{ "/suffix/path.gz", "7"},
|
||||
{ "/animal/path.gz", "5"},
|
||||
{ "/Other/path", "8"},};
|
||||
{ "/Other/path", "8"},
|
||||
{ "/\u20ACuro/path", "11"},
|
||||
};
|
||||
|
||||
for (String[] test : tests)
|
||||
{
|
||||
|
|
|
@ -41,6 +41,7 @@ import java.util.concurrent.Executor;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.eclipse.jetty.util.ConcurrentArrayQueue;
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||
|
@ -93,7 +94,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
|
||||
/**
|
||||
* Get the connect timeout
|
||||
*
|
||||
*
|
||||
* @return the connect timeout (in milliseconds)
|
||||
*/
|
||||
public long getConnectTimeout()
|
||||
|
@ -103,7 +104,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
|
||||
/**
|
||||
* Set the connect timeout (in milliseconds)
|
||||
*
|
||||
*
|
||||
* @param milliseconds the number of milliseconds for the timeout
|
||||
*/
|
||||
public void setConnectTimeout(long milliseconds)
|
||||
|
@ -317,7 +318,8 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
*/
|
||||
public class ManagedSelector extends AbstractLifeCycle implements Runnable, Dumpable
|
||||
{
|
||||
private final Queue<Runnable> _changes = new ConcurrentLinkedQueue<>();
|
||||
private final Queue<Runnable> _changes = new ConcurrentArrayQueue<>();
|
||||
|
||||
private final int _id;
|
||||
private Selector _selector;
|
||||
private volatile Thread _thread;
|
||||
|
@ -364,7 +366,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
if (_runningChanges)
|
||||
_changes.offer(change);
|
||||
else
|
||||
{
|
||||
{
|
||||
// Otherwise we run the queued changes
|
||||
runChanges();
|
||||
// and then directly run the passed change
|
||||
|
|
|
@ -538,8 +538,10 @@ public class SslConnection extends AbstractConnection
|
|||
case NOT_HANDSHAKING:
|
||||
// we just didn't read anything.
|
||||
if (net_filled < 0)
|
||||
_sslEngine.closeInbound();
|
||||
|
||||
{
|
||||
closeInbound();
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case NEED_TASK:
|
||||
|
@ -568,14 +570,8 @@ public class SslConnection extends AbstractConnection
|
|||
// if we just filled some net data
|
||||
if (net_filled < 0)
|
||||
{
|
||||
// If we call closeInbound() before having read the SSL close
|
||||
// message an exception will be thrown (truncation attack).
|
||||
// The TLS specification says that the sender of the SSL close
|
||||
// message may just close and avoid to read the response.
|
||||
// If that is the case, we avoid calling closeInbound() because
|
||||
// will throw the truncation attack exception for nothing.
|
||||
if (isOpen())
|
||||
_sslEngine.closeInbound();
|
||||
closeInbound();
|
||||
return -1;
|
||||
}
|
||||
else if (net_filled > 0)
|
||||
{
|
||||
|
@ -625,6 +621,18 @@ public class SslConnection extends AbstractConnection
|
|||
}
|
||||
}
|
||||
|
||||
private void closeInbound()
|
||||
{
|
||||
try
|
||||
{
|
||||
_sslEngine.closeInbound();
|
||||
}
|
||||
catch (SSLException x)
|
||||
{
|
||||
LOG.ignore(x);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean flush(ByteBuffer... appOuts) throws IOException
|
||||
{
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.nio.channels.SocketChannel;
|
|||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLEngineResult;
|
||||
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
|
||||
import org.eclipse.jetty.io.ssl.SslConnection;
|
||||
|
@ -197,10 +198,28 @@ public class SelectChannelEndPointSslTest extends SelectChannelEndPointTest
|
|||
|
||||
Assert.assertEquals("HelloWorld",reply);
|
||||
|
||||
if (debug) System.err.println("Shutting down output");
|
||||
client.socket().shutdownOutput();
|
||||
|
||||
filled=client.read(sslIn);
|
||||
if (debug) System.err.println("in="+filled);
|
||||
sslIn.flip();
|
||||
try
|
||||
{
|
||||
// Since the client closed abruptly, the server is sending a close alert with a failure
|
||||
engine.unwrap(sslIn, appIn);
|
||||
Assert.fail();
|
||||
}
|
||||
catch (SSLException x)
|
||||
{
|
||||
// Expected
|
||||
}
|
||||
|
||||
sslIn.clear();
|
||||
filled=client.read(sslIn);
|
||||
Assert.assertEquals(-1,filled);
|
||||
|
||||
Assert.assertFalse(server.isOpen());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.el</artifactId>
|
||||
<version>2.2.0.v201108011116</version>
|
||||
<version>2.2.0.v201303151357</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
|
@ -76,7 +76,7 @@
|
|||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>com.sun.el</artifactId>
|
||||
<version>2.2.0.v201108011116</version>
|
||||
<version>2.2.0.v201303151357</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
|
@ -88,7 +88,7 @@
|
|||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>org.eclipse.jdt.core</artifactId>
|
||||
<version>3.7.1</version>
|
||||
<version>3.8.2.v20130121</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
|
|
|
@ -88,7 +88,7 @@ public class MavenAnnotationConfiguration extends AnnotationConfiguration
|
|||
public void doParse (final WebAppContext context, final AnnotationParser parser, Resource resource)
|
||||
throws Exception
|
||||
{
|
||||
parser.parse(resource, new ClassNameResolver()
|
||||
parser.parseDir(resource, new ClassNameResolver()
|
||||
{
|
||||
public boolean isExcluded (String name)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
The ThreadMonitor is distributed as part of the jetty-monitor module.
|
||||
|
||||
In order to start ThreadMonitor when server starts up, the following command line should be used.
|
||||
|
||||
java -jar start.jar OPTIONS=monitor jetty-monitor.xml
|
||||
|
||||
To run ThreadMonitor on a Jetty installation that doesn't include jetty-monitor module, the jetty-monitor-[version].jar file needs to be copied into ${jetty.home}/lib/ext directory, and jetty-monitor.xml configuration file needs to be copied into ${jetty.home}/etc directory. Subsequently, the following command line should be used.
|
||||
|
||||
java -jar start.jar etc/jetty-monitor.xml
|
||||
|
||||
If running Jetty on Java VM version 1.5, the -Dcom.sun.management.jmxremote option should be added to the command lines above in order to enable the JMX agent.
|
||||
|
||||
In order to log CPU utilization for threads that are above specified threshold, you need to follow instructions inside jetty-monitor.xml configuration file.
|
|
@ -0,0 +1,145 @@
|
|||
<!--
|
||||
// ========================================================================
|
||||
// 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>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-monitor</artifactId>
|
||||
<name>Jetty :: Monitoring</name>
|
||||
<description>Performance monitoring artifact for jetty.</description>
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.monitor</bundle-symbolic-name>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>manifest</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Import-Package>javax.management.*,*</Import-Package>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<!--
|
||||
Required for OSGI
|
||||
-->
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>config</descriptorRef>
|
||||
</descriptorRefs>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<forkMode>always</forkMode>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<onlyAnalyze>org.eclipse.jetty.monitor.*</onlyAnalyze>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<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-websocket</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency-->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<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>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
|
||||
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
<!-- Create Thread Monitor, and add to the Server as a lifecycle -->
|
||||
<Call name="addBean">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.monitor.ThreadMonitor">
|
||||
<Set name="scanInterval">2000</Set>
|
||||
<Set name="busyThreshold">90</Set>
|
||||
<Set name="stackDepth">3</Set>
|
||||
<Set name="trailLength">2</Set>
|
||||
<!-- To enable logging CPU utilization for threads above specified threshold, -->
|
||||
<!-- uncomment the following lines, changing log interval (in milliseconds) -->
|
||||
<!-- and log threshold (in percent) as desired. -->
|
||||
<!--
|
||||
<Set name="logInterval">10000</Arg>
|
||||
<Set name="logThreshold">1</Arg>
|
||||
-->
|
||||
|
||||
<!-- To enable detail dump of the server whenever a thread is detected as spinning, -->
|
||||
<!-- uncomment the following lines. -->
|
||||
<!--
|
||||
<Set name="dumpable"><Ref id="Server"/></Set>
|
||||
-->
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
</Configure>
|
||||
|
|
@ -0,0 +1,194 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,592 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.lang.management.ManagementFactory;
|
||||
import java.lang.management.ThreadMXBean;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
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.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
@ManagedObject("Busy Thread Monitor")
|
||||
public class ThreadMonitor extends AbstractLifeCycle implements Runnable
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(ThreadMonitor.class);
|
||||
|
||||
private int _scanInterval;
|
||||
private int _logInterval;
|
||||
private int _busyThreshold;
|
||||
private int _logThreshold;
|
||||
private int _stackDepth;
|
||||
private int _trailLength;
|
||||
|
||||
private ThreadMXBean _threadBean;
|
||||
|
||||
private Thread _runner;
|
||||
private Logger _logger;
|
||||
private volatile boolean _done = true;
|
||||
private Dumpable _dumpable;
|
||||
|
||||
private Map<Long,ThreadMonitorInfo> _monitorInfo;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Instantiates a new thread monitor.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public ThreadMonitor() throws Exception
|
||||
{
|
||||
this(5000);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Instantiates a new thread monitor.
|
||||
*
|
||||
* @param intervalMs scan interval
|
||||
* @throws Exception
|
||||
*/
|
||||
public ThreadMonitor(int intervalMs) throws Exception
|
||||
{
|
||||
this(intervalMs, 95);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Instantiates a new thread monitor.
|
||||
*
|
||||
* @param intervalMs scan interval
|
||||
* @param threshold busy threshold
|
||||
* @throws Exception
|
||||
*/
|
||||
public ThreadMonitor(int intervalMs, int threshold) throws Exception
|
||||
{
|
||||
this(intervalMs, threshold, 3);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Instantiates a new thread monitor.
|
||||
*
|
||||
* @param intervalMs scan interval
|
||||
* @param threshold busy threshold
|
||||
* @param depth stack compare depth
|
||||
* @throws Exception
|
||||
*/
|
||||
public ThreadMonitor(int intervalMs, int threshold, int depth) throws Exception
|
||||
{
|
||||
this(intervalMs, threshold, depth, 3);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Instantiates a new thread monitor.
|
||||
*
|
||||
* @param intervalMs scan interval
|
||||
* @param threshold busy threshold
|
||||
* @param depth stack compare depth
|
||||
* @param trail length of stack trail
|
||||
* @throws Exception
|
||||
*/
|
||||
public ThreadMonitor(int intervalMs, int threshold, int depth, int trail) throws Exception
|
||||
{
|
||||
_scanInterval = intervalMs;
|
||||
_busyThreshold = threshold;
|
||||
_stackDepth = depth;
|
||||
_trailLength = trail;
|
||||
|
||||
_logger = Log.getLogger(ThreadMonitor.class.getName());
|
||||
_monitorInfo = new HashMap<Long, ThreadMonitorInfo>();
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Gets the scan interval.
|
||||
*
|
||||
* @return the scan interval
|
||||
*/
|
||||
public int getScanInterval()
|
||||
{
|
||||
return _scanInterval;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Sets the scan interval.
|
||||
*
|
||||
* @param ms the new scan interval
|
||||
*/
|
||||
public void setScanInterval(int ms)
|
||||
{
|
||||
_scanInterval = ms;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Gets the log interval.
|
||||
*
|
||||
* @return the log interval
|
||||
*/
|
||||
public int getLogInterval()
|
||||
{
|
||||
return _logInterval;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Sets the log interval.
|
||||
*
|
||||
* @param ms the new log interval
|
||||
*/
|
||||
public void setLogInterval(int ms)
|
||||
{
|
||||
_logInterval = ms;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Gets the busy threshold.
|
||||
*
|
||||
* @return the busy threshold
|
||||
*/
|
||||
public int getBusyThreshold()
|
||||
{
|
||||
return _busyThreshold;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Sets the busy threshold.
|
||||
*
|
||||
* @param percent the new busy threshold
|
||||
*/
|
||||
public void setBusyThreshold(int percent)
|
||||
{
|
||||
_busyThreshold = percent;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Gets the log threshold.
|
||||
*
|
||||
* @return the log threshold
|
||||
*/
|
||||
public int getLogThreshold()
|
||||
{
|
||||
return _logThreshold;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Sets the log threshold.
|
||||
*
|
||||
* @param percent the new log threshold
|
||||
*/
|
||||
public void setLogThreshold(int percent)
|
||||
{
|
||||
_logThreshold = percent;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Gets the stack depth.
|
||||
*
|
||||
* @return the stack depth
|
||||
*/
|
||||
public int getStackDepth()
|
||||
{
|
||||
return _stackDepth;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Sets the stack depth.
|
||||
*
|
||||
* @param stackDepth the new stack depth
|
||||
*/
|
||||
public void setStackDepth(int stackDepth)
|
||||
{
|
||||
_stackDepth = stackDepth;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Sets the stack trace trail length.
|
||||
*
|
||||
* @param trailLength the new trail length
|
||||
*/
|
||||
public void setTrailLength(int trailLength)
|
||||
{
|
||||
_trailLength = trailLength;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Gets the stack trace trail length.
|
||||
*
|
||||
* @return the trail length
|
||||
*/
|
||||
public int getTrailLength()
|
||||
{
|
||||
return _trailLength;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Enable logging of CPU usage.
|
||||
*
|
||||
* @param frequencyMs the logging frequency
|
||||
* @param thresholdPercent the logging threshold
|
||||
*/
|
||||
public void logCpuUsage(int frequencyMs, int thresholdPercent)
|
||||
{
|
||||
setLogInterval(frequencyMs);
|
||||
setLogThreshold(thresholdPercent);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @return A {@link Dumpable} that is dumped whenever spinning threads are detected
|
||||
*/
|
||||
public Dumpable getDumpable()
|
||||
{
|
||||
return _dumpable;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param dumpable A {@link Dumpable} that is dumped whenever spinning threads are detected
|
||||
*/
|
||||
public void setDumpable(Dumpable dumpable)
|
||||
{
|
||||
_dumpable = dumpable;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
|
||||
*/
|
||||
public void doStart()
|
||||
{
|
||||
_done = false;
|
||||
|
||||
_runner = new Thread(this);
|
||||
_runner.setDaemon(true);
|
||||
_runner.start();
|
||||
|
||||
LOG.info("Thread Monitor started successfully");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
|
||||
*/
|
||||
public void doStop()
|
||||
{
|
||||
if (_runner != null)
|
||||
{
|
||||
_done = true;
|
||||
try
|
||||
{
|
||||
_runner.join();
|
||||
}
|
||||
catch (InterruptedException ex) {}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Retrieve all avaliable thread ids
|
||||
*
|
||||
* @return array of thread ids
|
||||
*/
|
||||
protected long[] getAllThreadIds()
|
||||
{
|
||||
return _threadBean.getAllThreadIds();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Retrieve the cpu time for specified thread.
|
||||
*
|
||||
* @param id thread id
|
||||
* @return cpu time of the thread
|
||||
*/
|
||||
protected long getThreadCpuTime(long id)
|
||||
{
|
||||
return _threadBean.getThreadCpuTime(id);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Initialize JMX objects.
|
||||
*/
|
||||
protected void init()
|
||||
{
|
||||
_threadBean = ManagementFactory.getThreadMXBean();
|
||||
if (_threadBean.isThreadCpuTimeSupported())
|
||||
{
|
||||
_threadBean.setThreadCpuTimeEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see java.lang.Runnable#run()
|
||||
*/
|
||||
public void run()
|
||||
{
|
||||
// Initialize repeat flag
|
||||
boolean repeat = false;
|
||||
boolean scanNow, logNow;
|
||||
|
||||
// Set next scan time and log time
|
||||
long nextScanTime = System.currentTimeMillis();
|
||||
long nextLogTime = nextScanTime + _logInterval;
|
||||
|
||||
while (!_done)
|
||||
{
|
||||
long currTime = System.currentTimeMillis();
|
||||
scanNow = (currTime > nextScanTime);
|
||||
logNow = (_logInterval > 0 && currTime > nextLogTime);
|
||||
if (repeat || scanNow || logNow)
|
||||
{
|
||||
repeat = collectThreadInfo();
|
||||
logThreadInfo(logNow);
|
||||
|
||||
if (scanNow)
|
||||
{
|
||||
nextScanTime = System.currentTimeMillis() + _scanInterval;
|
||||
}
|
||||
if (logNow)
|
||||
{
|
||||
nextLogTime = System.currentTimeMillis() + _logInterval;
|
||||
}
|
||||
}
|
||||
|
||||
// Sleep only if not going to repeat scanning immediately
|
||||
if (!repeat)
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep(100);
|
||||
}
|
||||
catch (InterruptedException ex)
|
||||
{
|
||||
LOG.ignore(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Collect thread info.
|
||||
*/
|
||||
private boolean collectThreadInfo()
|
||||
{
|
||||
boolean repeat = false;
|
||||
try
|
||||
{
|
||||
// Retrieve stack traces for all threads at once as it
|
||||
// was proven to be an order of magnitude faster when
|
||||
// retrieving a single thread stack trace.
|
||||
Map<Thread,StackTraceElement[]> all = Thread.getAllStackTraces();
|
||||
|
||||
for (Map.Entry<Thread,StackTraceElement[]> entry : all.entrySet())
|
||||
{
|
||||
Thread thread = entry.getKey();
|
||||
long threadId = thread.getId();
|
||||
|
||||
// Skip our own runner thread
|
||||
if (threadId == _runner.getId())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ThreadMonitorInfo currMonitorInfo = _monitorInfo.get(Long.valueOf(threadId));
|
||||
if (currMonitorInfo == null)
|
||||
{
|
||||
// Create thread info object for a new thread
|
||||
currMonitorInfo = new ThreadMonitorInfo(thread);
|
||||
currMonitorInfo.setStackTrace(entry.getValue());
|
||||
currMonitorInfo.setCpuTime(getThreadCpuTime(threadId));
|
||||
currMonitorInfo.setSampleTime(System.nanoTime());
|
||||
_monitorInfo.put(Long.valueOf(threadId), currMonitorInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update the existing thread info object
|
||||
currMonitorInfo.setStackTrace(entry.getValue());
|
||||
currMonitorInfo.setCpuTime(getThreadCpuTime(threadId));
|
||||
currMonitorInfo.setSampleTime(System.nanoTime());
|
||||
|
||||
// Stack trace count holds logging state
|
||||
int count = currMonitorInfo.getTraceCount();
|
||||
if (count >= 0 && currMonitorInfo.isSpinning())
|
||||
{
|
||||
// Thread was spinning and was logged before
|
||||
if (count < _trailLength)
|
||||
{
|
||||
// Log another stack trace
|
||||
currMonitorInfo.setTraceCount(count+1);
|
||||
repeat = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reset spin flag and trace count
|
||||
currMonitorInfo.setSpinning(false);
|
||||
currMonitorInfo.setTraceCount(-1);
|
||||
}
|
||||
if (currMonitorInfo.getCpuUtilization() > _busyThreshold)
|
||||
{
|
||||
// Thread is busy
|
||||
StackTraceElement[] lastStackTrace = currMonitorInfo.getStackTrace();
|
||||
|
||||
if (lastStackTrace != null
|
||||
&& matchStackTraces(lastStackTrace, entry.getValue()))
|
||||
{
|
||||
// Thread is spinning
|
||||
currMonitorInfo.setSpinning(true);
|
||||
if (count < 0)
|
||||
{
|
||||
// Enable logging of spin status and stack traces
|
||||
// only if the incoming trace count is negative
|
||||
// that indicates a new scan for this thread
|
||||
currMonitorInfo.setTraceCount(0);
|
||||
repeat = (_trailLength > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LOG.debug(ex);
|
||||
}
|
||||
return repeat;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected void logThreadInfo(boolean logAll)
|
||||
{
|
||||
if (_monitorInfo.size() > 0)
|
||||
{
|
||||
// Select thread objects for all live threads
|
||||
long[] running = getAllThreadIds();
|
||||
List<ThreadMonitorInfo> all = new ArrayList<ThreadMonitorInfo>();
|
||||
for (int idx=0; idx<running.length; idx++)
|
||||
{
|
||||
ThreadMonitorInfo info = _monitorInfo.get(running[idx]);
|
||||
if (info != null)
|
||||
{
|
||||
all.add(info);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort selected thread objects by their CPU utilization
|
||||
Collections.sort(all, new Comparator<ThreadMonitorInfo>()
|
||||
{
|
||||
/* ------------------------------------------------------------ */
|
||||
public int compare(ThreadMonitorInfo info1, ThreadMonitorInfo info2)
|
||||
{
|
||||
return (int)Math.signum(info2.getCpuUtilization()-info1.getCpuUtilization());
|
||||
}
|
||||
});
|
||||
|
||||
String format = "Thread '%2$s'[%3$s,id:%1$d,cpu:%4$.2f%%]%5$s";
|
||||
|
||||
// Log thread information for threads that exceed logging threshold
|
||||
// or log spinning threads if their trace count is zero
|
||||
boolean spinning=false;
|
||||
for (ThreadMonitorInfo info : all)
|
||||
{
|
||||
if (logAll && info.getCpuUtilization() > _logThreshold
|
||||
|| info.isSpinning() && info.getTraceCount() == 0)
|
||||
{
|
||||
String message = String.format(format,
|
||||
info.getThreadId(), info.getThreadName(),
|
||||
info.getThreadState(), info.getCpuUtilization(),
|
||||
info.isSpinning() ? " SPINNING" : "");
|
||||
_logger.info(message);
|
||||
spinning=true;
|
||||
}
|
||||
}
|
||||
|
||||
// Dump info
|
||||
if (spinning && _dumpable!=null)
|
||||
{
|
||||
System.err.println(_dumpable.dump());
|
||||
}
|
||||
|
||||
// Log stack traces for spinning threads with positive trace count
|
||||
for (ThreadMonitorInfo info : all)
|
||||
{
|
||||
if (info.isSpinning() && info.getTraceCount() >= 0)
|
||||
{
|
||||
String message = String.format(format,
|
||||
info.getThreadId(), info.getThreadName(),
|
||||
info.getThreadState(), info.getCpuUtilization(),
|
||||
" STACK TRACE");
|
||||
_logger.warn(new ThreadMonitorException(message, info.getStackTrace()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Match stack traces.
|
||||
*
|
||||
* @param lastStackTrace last stack trace
|
||||
* @param stackTrace current stack trace
|
||||
* @return true, if successful
|
||||
*/
|
||||
private boolean matchStackTraces(StackTraceElement[] lastStackTrace, StackTraceElement[] stackTrace)
|
||||
{
|
||||
boolean match = true;
|
||||
int count = Math.min(_stackDepth, Math.min(lastStackTrace.length, stackTrace.length));
|
||||
|
||||
for (int idx=0; idx < count; idx++)
|
||||
{
|
||||
if (!stackTrace[idx].equals(lastStackTrace[idx]))
|
||||
{
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,419 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.HttpClient;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.util.BytesContentProvider;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
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;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
*/
|
||||
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;
|
||||
|
||||
QueuedThreadPool executor = new QueuedThreadPool();
|
||||
executor.setName(executor.getName() + "-monitor");
|
||||
_client = new HttpClient();
|
||||
_client.setExecutor(executor);
|
||||
|
||||
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 {
|
||||
reqStream = new ByteArrayOutputStream();
|
||||
request.storeToXML(reqStream,null);
|
||||
|
||||
ContentResponse r3sponse = _client.POST(_url)
|
||||
.header("Connection","close")
|
||||
.content(new BytesContentProvider(reqStream.toByteArray()))
|
||||
.send();
|
||||
|
||||
|
||||
if (r3sponse.getStatus() == HttpStatus.OK_200)
|
||||
{
|
||||
response = new Properties();
|
||||
resStream = new ByteArrayInputStream(r3sponse.getContent());
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,289 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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;
|
||||
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.annotation.ManagedOperation;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* 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>
|
||||
*/
|
||||
@ManagedObject("Java Monitoring Tools")
|
||||
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;
|
||||
}
|
||||
@ManagedOperation(value="Detailed report on the deadlocked threads.", impact="ACTION_INFO")
|
||||
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>();
|
||||
|
||||
@ManagedOperation(value="Number of Blocked Threads")
|
||||
public int getThreadsBlocked()
|
||||
{
|
||||
sampleThreads();
|
||||
|
||||
return states.get(Thread.State.BLOCKED);
|
||||
}
|
||||
|
||||
@ManagedOperation(value="Number of New Threads", impact="ACTION_INFO")
|
||||
public int getThreadsNew()
|
||||
{
|
||||
sampleThreads();
|
||||
|
||||
return states.get(Thread.State.NEW);
|
||||
}
|
||||
|
||||
@ManagedOperation(value="Number of Terminated Threads", impact="ACTION_INFO")
|
||||
public int getThreadsTerminated()
|
||||
{
|
||||
sampleThreads();
|
||||
|
||||
return states.get(Thread.State.TERMINATED);
|
||||
}
|
||||
|
||||
@ManagedOperation(value="Number of Sleeping and Waiting threads")
|
||||
public int getThreadsTimedWaiting()
|
||||
{
|
||||
sampleThreads();
|
||||
|
||||
return states.get(Thread.State.TIMED_WAITING);
|
||||
}
|
||||
|
||||
@ManagedOperation(value="Number of Waiting Threads", impact="ACTION_INFO")
|
||||
public int getThreadsWaiting()
|
||||
{
|
||||
sampleThreads();
|
||||
|
||||
return states.get(Thread.State.WAITING);
|
||||
}
|
||||
|
||||
@ManagedOperation(value="Number of Runnable Threads", impact="ACTION_INFO")
|
||||
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";
|
||||
|
||||
@ManagedOperation(value="Amount of time successful DNS queries are cached for.")
|
||||
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();
|
||||
}
|
||||
|
||||
@ManagedOperation(value="Amount of time failed DNS queries are cached for")
|
||||
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";
|
||||
|
||||
@ManagedOperation(value="Cache policy for successful DNS lookups was changed from the hard-coded default")
|
||||
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;
|
||||
}
|
||||
|
||||
@ManagedOperation(value="Cache policy for failed DNS lookups was changed from the hard-coded 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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);
|
||||
}
|
|
@ -0,0 +1,207 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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);
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.execute(new Runnable() {
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
if(trigger.match(timestamp))
|
||||
_action.doExecute(timestamp);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LOG.debug(ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.thread;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
*/
|
||||
public class ThreadMonitorException extends Exception
|
||||
{
|
||||
private static final long serialVersionUID = -4345223166315716918L;
|
||||
|
||||
public ThreadMonitorException(String message, StackTraceElement[] stackTrace)
|
||||
{
|
||||
super(message);
|
||||
setStackTrace(stackTrace);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.thread;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
*/
|
||||
public class ThreadMonitorInfo
|
||||
{
|
||||
private Thread _thread;
|
||||
private StackTraceElement[] _stackTrace;
|
||||
|
||||
private boolean _threadSpinning = false;
|
||||
private int _traceCount = -1;
|
||||
|
||||
private long _prevCpuTime;
|
||||
private long _prevSampleTime;
|
||||
private long _currCpuTime;
|
||||
private long _currSampleTime;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Instantiates a new thread monitor info.
|
||||
*
|
||||
* @param thread the thread this object is created for
|
||||
*/
|
||||
public ThreadMonitorInfo(Thread thread)
|
||||
{
|
||||
_thread = thread;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @return Id of the thread
|
||||
*/
|
||||
public long getThreadId()
|
||||
{
|
||||
return _thread.getId();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Gets the thread name.
|
||||
*
|
||||
* @return the thread name
|
||||
*/
|
||||
public String getThreadName()
|
||||
{
|
||||
return _thread.getName();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Gets the thread state.
|
||||
*
|
||||
* @return the thread state
|
||||
*/
|
||||
public String getThreadState()
|
||||
{
|
||||
return _thread.getState().toString();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Gets the stack trace.
|
||||
*
|
||||
* @return the stack trace
|
||||
*/
|
||||
public StackTraceElement[] getStackTrace()
|
||||
{
|
||||
return _stackTrace;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Sets the stack trace.
|
||||
*
|
||||
* @param stackTrace the new stack trace
|
||||
*/
|
||||
public void setStackTrace(StackTraceElement[] stackTrace)
|
||||
{
|
||||
_stackTrace = stackTrace;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Checks if is spinning.
|
||||
*
|
||||
* @return true, if is spinning
|
||||
*/
|
||||
public boolean isSpinning()
|
||||
{
|
||||
return _threadSpinning;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Sets the spinning flag.
|
||||
*
|
||||
* @param value the new value
|
||||
*/
|
||||
public void setSpinning(boolean value)
|
||||
{
|
||||
_threadSpinning = value;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Sets the trace count.
|
||||
*
|
||||
* @param traceCount the new trace count
|
||||
*/
|
||||
public void setTraceCount(int traceCount)
|
||||
{
|
||||
_traceCount = traceCount;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Gets the trace count.
|
||||
*
|
||||
* @return the trace count
|
||||
*/
|
||||
public int getTraceCount()
|
||||
{
|
||||
return _traceCount;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @return the CPU time of the thread
|
||||
*/
|
||||
public long getCpuTime()
|
||||
{
|
||||
return _currCpuTime;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Set the CPU time.
|
||||
*
|
||||
* @param ns new CPU time
|
||||
*/
|
||||
public void setCpuTime(long ns)
|
||||
{
|
||||
_prevCpuTime = _currCpuTime;
|
||||
_currCpuTime = ns;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @return the time of sample
|
||||
*/
|
||||
public long getSampleTime()
|
||||
{
|
||||
return _currSampleTime;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Sets the sample time.
|
||||
*
|
||||
* @param ns the time of sample
|
||||
*/
|
||||
public void setSampleTime(long ns)
|
||||
{
|
||||
_prevSampleTime = _currSampleTime;
|
||||
_currSampleTime = ns;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Gets the CPU utilization.
|
||||
*
|
||||
* @return the CPU utilization percentage
|
||||
*/
|
||||
public float getCpuUtilization()
|
||||
{
|
||||
long elapsedCpuTime = _currCpuTime - _prevCpuTime;
|
||||
long elapsedNanoTime = _currSampleTime - _prevSampleTime;
|
||||
|
||||
return elapsedNanoTime > 0 ? Math.min((elapsedCpuTime * 100.0f) / elapsedNanoTime, 100.0f) : 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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));
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,525 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.HttpClient;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.jmx.MBeanContainer;
|
||||
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.EventState.TriggerState;
|
||||
import org.eclipse.jetty.monitor.jmx.EventTrigger;
|
||||
import org.eclipse.jetty.monitor.jmx.MonitorAction;
|
||||
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.Connector;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
*/
|
||||
public class AttrEventTriggerTest
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(AttrEventTriggerTest.class);
|
||||
|
||||
private Server _server;
|
||||
private TestHandler _handler;
|
||||
private RequestCounter _counter;
|
||||
private JMXMonitor _monitor;
|
||||
private HttpClient _client;
|
||||
private String _requestUrl;
|
||||
private MBeanContainer _mBeanContainer;
|
||||
|
||||
@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();
|
||||
|
||||
ServerConnector connector = new ServerConnector(_server);
|
||||
connector.setPort(0);
|
||||
_server.setConnectors(new Connector[] {connector});
|
||||
|
||||
_handler = new TestHandler();
|
||||
_server.setHandler(_handler);
|
||||
|
||||
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
|
||||
_mBeanContainer = new MBeanContainer(mBeanServer);
|
||||
_server.addBean(_mBeanContainer,true);
|
||||
_server.addBean(Log.getLog());
|
||||
|
||||
_counter = _handler.getRequestCounter();
|
||||
_server.addBean(_counter);
|
||||
|
||||
_server.start();
|
||||
|
||||
startClient();
|
||||
|
||||
_monitor = new JMXMonitor();
|
||||
|
||||
int port = connector.getLocalPort();
|
||||
_requestUrl = "http://localhost:"+port+ "/";
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown()
|
||||
throws Exception
|
||||
{
|
||||
stopClient();
|
||||
|
||||
_mBeanContainer.destroy();
|
||||
|
||||
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
|
||||
{
|
||||
//LOG.debug("Request: %s", _requestUrl);
|
||||
ContentResponse r3sponse = _client.GET(_requestUrl);
|
||||
|
||||
//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 (r3sponse.getStatus() == HttpStatus.OK_200)
|
||||
{
|
||||
content = r3sponse.getContentAsString();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.info("response status", r3sponse.getStatus());
|
||||
}
|
||||
|
||||
assertEquals(HttpStatus.OK_200,r3sponse.getStatus());
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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 org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.annotation.ManagedOperation;
|
||||
|
||||
|
||||
@ManagedObject("TEST: Request Counter")
|
||||
public class RequestCounter
|
||||
{
|
||||
public long _counter;
|
||||
|
||||
@ManagedAttribute("Get the value of the counter")
|
||||
public synchronized long getCounter()
|
||||
{
|
||||
return _counter;
|
||||
}
|
||||
|
||||
@ManagedOperation("Increment the value of the counter")
|
||||
public synchronized void increment()
|
||||
{
|
||||
_counter++;
|
||||
}
|
||||
|
||||
@ManagedOperation("Reset the counter")
|
||||
public synchronized void reset()
|
||||
{
|
||||
_counter = 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.atomic.AtomicInteger;
|
||||
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.StdErrLog;
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
*/
|
||||
public class ThreadMonitorTest
|
||||
{
|
||||
public final static int DURATION=4000;
|
||||
|
||||
@Test
|
||||
public void monitorTest() throws Exception
|
||||
{
|
||||
((StdErrLog)Log.getLogger(ThreadMonitor.class.getName())).setHideStacks(true);
|
||||
((StdErrLog)Log.getLogger(ThreadMonitor.class.getName())).setSource(false);
|
||||
|
||||
final AtomicInteger countLogs=new AtomicInteger(0);
|
||||
final AtomicInteger countSpin=new AtomicInteger(0);
|
||||
|
||||
ThreadMonitor monitor = new ThreadMonitor(1000,50,1,1)
|
||||
{
|
||||
@Override
|
||||
protected void logThreadInfo(boolean logAll)
|
||||
{
|
||||
if (logAll)
|
||||
countLogs.incrementAndGet();
|
||||
else
|
||||
countSpin.incrementAndGet();
|
||||
super.logThreadInfo(logAll);
|
||||
}
|
||||
};
|
||||
monitor.setDumpable(new Dumpable()
|
||||
{
|
||||
public void dump(Appendable out, String indent) throws IOException
|
||||
{
|
||||
out.append(dump());
|
||||
}
|
||||
|
||||
public String dump()
|
||||
{
|
||||
return "Dump Spinning";
|
||||
}
|
||||
});
|
||||
|
||||
monitor.logCpuUsage(2000,0);
|
||||
monitor.start();
|
||||
|
||||
Random rnd = new Random();
|
||||
for (long cnt=0; cnt<100; cnt++)
|
||||
{
|
||||
long value = rnd.nextLong() % 50 + 50;
|
||||
Sleeper sleeper = new Sleeper(value);
|
||||
Thread runner = new Thread(sleeper);
|
||||
runner.setDaemon(true);
|
||||
runner.start();
|
||||
}
|
||||
|
||||
Spinner spinner = new Spinner();
|
||||
Thread runner = new Thread(spinner);
|
||||
runner.start();
|
||||
|
||||
Thread.sleep(DURATION);
|
||||
|
||||
spinner.setDone();
|
||||
monitor.stop();
|
||||
|
||||
assertTrue(countLogs.get() >= 1);
|
||||
assertTrue(countSpin.get() >= 2);
|
||||
}
|
||||
|
||||
|
||||
private class Spinner implements Runnable
|
||||
{
|
||||
private volatile boolean done = false;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void setDone()
|
||||
{
|
||||
done = true;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void run()
|
||||
{
|
||||
spin();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void spin()
|
||||
{
|
||||
long result=-1;
|
||||
long end=System.currentTimeMillis()+DURATION+1000;
|
||||
while (!done && System.currentTimeMillis()<end)
|
||||
{
|
||||
for (int i=0;i<1000000000;i++)
|
||||
result^=i;
|
||||
}
|
||||
|
||||
if (result==42)
|
||||
System.err.println("Bingo!");
|
||||
}
|
||||
}
|
||||
|
||||
private class Sleeper implements Runnable
|
||||
{
|
||||
private long _value;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Sleeper(long value)
|
||||
{
|
||||
_value = value;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
fn(_value);
|
||||
}
|
||||
catch (InterruptedException e) {}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public long fn(long value) throws InterruptedException
|
||||
{
|
||||
long result = value > 1 ? fn(value-1) : 1;
|
||||
|
||||
Thread.sleep(50);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||
#org.eclipse.jetty.LEVEL=DEBUG
|
||||
org.eclipse.jetty.monitor.LEVEL=DEBUG
|
|
@ -115,7 +115,7 @@ public class OSGiWebInfConfiguration extends WebInfConfiguration
|
|||
|
||||
for (Resource r:matchingResources)
|
||||
{
|
||||
context.getMetaData().addContainerJar(r);
|
||||
context.getMetaData().addContainerResource(r);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,9 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpHeaderValue;
|
||||
import org.eclipse.jetty.util.ArrayTernaryTrie;
|
||||
import org.eclipse.jetty.util.StringMap;
|
||||
import org.eclipse.jetty.util.Trie;
|
||||
|
||||
/**
|
||||
* MSIE (Microsoft Internet Explorer) SSL Rule.
|
||||
|
@ -38,7 +40,7 @@ public class MsieSslRule extends Rule
|
|||
{
|
||||
private static final int IEv5 = '5';
|
||||
private static final int IEv6 = '6';
|
||||
private static StringMap __IE6_BadOS = new StringMap();
|
||||
private static Trie<Boolean> __IE6_BadOS = new ArrayTernaryTrie<>();
|
||||
{
|
||||
__IE6_BadOS.put("NT 5.01", Boolean.TRUE);
|
||||
__IE6_BadOS.put("NT 5.0",Boolean.TRUE);
|
||||
|
|
|
@ -2,64 +2,12 @@
|
|||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
|
||||
|
||||
<!-- ============================================================= -->
|
||||
<!-- Configure the Jetty Server instance with an ID "Server" -->
|
||||
<!-- by adding a HTTPS connector. -->
|
||||
<!-- Configure a HTTPS connector. -->
|
||||
<!-- This configuration must be used in conjunction with jetty.xml -->
|
||||
<!-- It should not be used with jetty-spdy.xml which can provide -->
|
||||
<!-- both HTTPS and SPDY connections -->
|
||||
<!-- and jetty-ssl.xml. -->
|
||||
<!-- ============================================================= -->
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Setup the SSL Context factory used to establish all TLS -->
|
||||
<!-- Connections and session. -->
|
||||
<!-- -->
|
||||
<!-- Consult the javadoc of o.e.j.util.ssl.SslContextFactory -->
|
||||
<!-- o.e.j.server.HttpConnectionFactory for all configuration -->
|
||||
<!-- that may be set here. -->
|
||||
<!-- -->
|
||||
<!-- The keyManagerPassword is passed as the password arg to -->
|
||||
<!-- KeyManagerFactory.init(...) -->
|
||||
<!-- If there is no keymanagerpassword, then the -->
|
||||
<!-- keystorepassword is used instead. -->
|
||||
<!-- If there is no trustmanager set, then the keystore is used -->
|
||||
<!-- as the trust store and the keystorepassword is used as the -->
|
||||
<!-- truststore password. -->
|
||||
<!-- =========================================================== -->
|
||||
<New id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
|
||||
<Set name="KeyStorePath"><Property name="jetty.home" default="." />/etc/keystore</Set>
|
||||
<Set name="KeyStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
|
||||
<Set name="KeyManagerPassword">OBF:1u2u1wml1z7s1z7a1wnl1u2g</Set>
|
||||
<Set name="TrustStorePath"><Property name="jetty.home" default="." />/etc/keystore</Set>
|
||||
<Set name="TrustStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
|
||||
<Set name="EndpointIdentificationAlgorithm"></Set>
|
||||
<Set name="ExcludeCipherSuites">
|
||||
<Array type="String">
|
||||
<Item>SSL_RSA_WITH_DES_CBC_SHA</Item>
|
||||
<Item>SSL_DHE_RSA_WITH_DES_CBC_SHA</Item>
|
||||
<Item>SSL_DHE_DSS_WITH_DES_CBC_SHA</Item>
|
||||
<Item>SSL_RSA_EXPORT_WITH_RC4_40_MD5</Item>
|
||||
<Item>SSL_RSA_EXPORT_WITH_DES40_CBC_SHA</Item>
|
||||
<Item>SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA</Item>
|
||||
<Item>SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA</Item>
|
||||
</Array>
|
||||
</Set>
|
||||
</New>
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Create a TLS specific HttpConfiguration based on the -->
|
||||
<!-- common HttpConfiguration defined in jetty.xml -->
|
||||
<!-- Add a SecureRequestCustomizer to extract certificate and -->
|
||||
<!-- session information -->
|
||||
<!-- =========================================================== -->
|
||||
<New id="sslHttpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
|
||||
<Arg><Ref refid="httpConfig"/></Arg>
|
||||
<Call name="addCustomizer">
|
||||
<Arg><New class="org.eclipse.jetty.server.SecureRequestCustomizer"/></Arg>
|
||||
</Call>
|
||||
</New>
|
||||
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Add a HTTPS Connector. -->
|
||||
<!-- Configure an o.e.j.server.ServerConnector with connection -->
|
||||
|
@ -71,7 +19,7 @@
|
|||
<!-- o.e.j.server.HttpConnectionFactory for all configuration -->
|
||||
<!-- that may be set here. -->
|
||||
<!-- =========================================================== -->
|
||||
<Call id="sslConnector" name="addConnector">
|
||||
<Call id="httpsConnector" name="addConnector">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.server.ServerConnector">
|
||||
<Arg name="server"><Ref refid="Server" /></Arg>
|
||||
|
@ -91,7 +39,7 @@
|
|||
</Array>
|
||||
</Arg>
|
||||
<Set name="host"><Property name="jetty.host" /></Set>
|
||||
<Set name="port"><Property name="jetty.tls.port" default="8443" /></Set>
|
||||
<Set name="port"><Property name="jetty.https.port" default="8443" /></Set>
|
||||
<Set name="idleTimeout">30000</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
|
|
|
@ -14,12 +14,12 @@
|
|||
<Arg>
|
||||
<New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler">
|
||||
<Set name="requestLog">
|
||||
<New id="RequestLogImpl" class="org.eclipse.jetty.server.NCSARequestLog">
|
||||
<New id="RequestLogImpl" class="org.eclipse.jetty.server.AsyncNCSARequestLog">
|
||||
<Set name="filename"><Property name="jetty.logs" default="./logs" />/yyyy_mm_dd.request.log</Set>
|
||||
<Set name="filenameDateFormat">yyyy_MM_dd</Set>
|
||||
<Set name="retainDays">90</Set>
|
||||
<Set name="append">true</Set>
|
||||
<Set name="extended">false</Set>
|
||||
<Set name="extended">true</Set>
|
||||
<Set name="logCookies">false</Set>
|
||||
<Set name="LogTimeZone">GMT</Set>
|
||||
</New>
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
|
||||
|
||||
<!-- ============================================================= -->
|
||||
<!-- Configure a TLS (SSL) Context Factory -->
|
||||
<!-- This configuration must be used in conjunction with jetty.xml -->
|
||||
<!-- and either jetty-https.xml or jetty-spdy.xml (but not both) -->
|
||||
<!-- ============================================================= -->
|
||||
<Configure id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
|
||||
<Set name="KeyStorePath"><Property name="jetty.home" default="." />/<Property name="jetty.keystore" default="etc/keystore"/></Set>
|
||||
<Set name="KeyStorePassword"><Property name="jetty.keystore.password" default="OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"/></Set>
|
||||
<Set name="KeyManagerPassword"><Property name="jetty.keymanager.password" default="OBF:1u2u1wml1z7s1z7a1wnl1u2g"/></Set>
|
||||
<Set name="TrustStorePath"><Property name="jetty.home" default="." />/<Property name="jetty.truststore" default="etc/keystore"/></Set>
|
||||
<Set name="TrustStorePassword"><Property name="jetty.truststore.password" default="OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"/></Set>
|
||||
<Set name="EndpointIdentificationAlgorithm"></Set>
|
||||
<Set name="ExcludeCipherSuites">
|
||||
<Array type="String">
|
||||
<Item>SSL_RSA_WITH_DES_CBC_SHA</Item>
|
||||
<Item>SSL_DHE_RSA_WITH_DES_CBC_SHA</Item>
|
||||
<Item>SSL_DHE_DSS_WITH_DES_CBC_SHA</Item>
|
||||
<Item>SSL_RSA_EXPORT_WITH_RC4_40_MD5</Item>
|
||||
<Item>SSL_RSA_EXPORT_WITH_DES40_CBC_SHA</Item>
|
||||
<Item>SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA</Item>
|
||||
<Item>SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA</Item>
|
||||
</Array>
|
||||
</Set>
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Create a TLS specific HttpConfiguration based on the -->
|
||||
<!-- common HttpConfiguration defined in jetty.xml -->
|
||||
<!-- Add a SecureRequestCustomizer to extract certificate and -->
|
||||
<!-- session information -->
|
||||
<!-- =========================================================== -->
|
||||
<New id="sslHttpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
|
||||
<Arg><Ref refid="httpConfig"/></Arg>
|
||||
<Call name="addCustomizer">
|
||||
<Arg><New class="org.eclipse.jetty.server.SecureRequestCustomizer"/></Arg>
|
||||
</Call>
|
||||
</New>
|
||||
|
||||
</Configure>
|
|
@ -44,8 +44,14 @@
|
|||
<!-- =========================================================== -->
|
||||
<Arg name="threadpool">
|
||||
<New id="threadpool" class="org.eclipse.jetty.util.thread.QueuedThreadPool">
|
||||
<Set name="minThreads">10</Set>
|
||||
<Set name="maxThreads">200</Set>
|
||||
<Arg name="minThreads" type="int">10</Arg>
|
||||
<Arg name="maxThreads" type="int">200</Arg>
|
||||
<Arg name="idleTimeout" type="int">60000</Arg>
|
||||
<!-- Arg >
|
||||
<New class="org.eclipse.jetty.util.ConcurrentArrayBlockingQueue$Unbounded">
|
||||
<Arg type='int'>32</Arg>
|
||||
</New>
|
||||
</Arg -->
|
||||
<Set name="detailedDump">false</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
|
@ -76,7 +82,7 @@
|
|||
<!-- =========================================================== -->
|
||||
<New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
|
||||
<Set name="secureScheme">https</Set>
|
||||
<Set name="securePort"><Property name="jetty.tls.port" default="8443" /></Set>
|
||||
<Set name="securePort"><Property name="jetty.secure.port" default="8443" /></Set>
|
||||
<Set name="outputBufferSize">32768</Set>
|
||||
<Set name="requestHeaderSize">8192</Set>
|
||||
<Set name="responseHeaderSize">8192</Set>
|
||||
|
|
|
@ -0,0 +1,501 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.PathMap;
|
||||
import org.eclipse.jetty.util.DateCache;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
/**
|
||||
* Base implementation of the {@link RequestLog} outputs logs in the pseudo-standard
|
||||
* NCSA common log format. Configuration options allow a choice between the
|
||||
* standard Common Log Format (as used in the 3 log format) and the Combined Log
|
||||
* Format (single log format). This log format can be output by most web
|
||||
* servers, and almost all web log analysis software can understand these
|
||||
* formats.
|
||||
*/
|
||||
public abstract class AbstractNCSARequestLog extends AbstractLifeCycle implements RequestLog
|
||||
{
|
||||
protected static final Logger LOG = Log.getLogger(AbstractNCSARequestLog.class);
|
||||
|
||||
private static ThreadLocal<StringBuilder> _buffers = new ThreadLocal<StringBuilder>()
|
||||
{
|
||||
@Override
|
||||
protected StringBuilder initialValue()
|
||||
{
|
||||
return new StringBuilder(256);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private String[] _ignorePaths;
|
||||
private boolean _extended;
|
||||
private transient PathMap<String> _ignorePathMap;
|
||||
private boolean _logLatency = false;
|
||||
private boolean _logCookies = false;
|
||||
private boolean _logServer = false;
|
||||
private boolean _logDispatch = false;
|
||||
private boolean _preferProxiedForAddress;
|
||||
private transient DateCache _logDateCache;
|
||||
private String _logDateFormat = "dd/MMM/yyyy:HH:mm:ss Z";
|
||||
private Locale _logLocale = Locale.getDefault();
|
||||
private String _logTimeZone = "GMT";
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Is logging enabled
|
||||
*/
|
||||
protected abstract boolean isEnabled();
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Write requestEntry out. (to disk or slf4j log)
|
||||
*/
|
||||
public abstract void write(String requestEntry) throws IOException;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Writes the request and response information to the output stream.
|
||||
*
|
||||
* @see org.eclipse.jetty.server.RequestLog#log(org.eclipse.jetty.server.Request, org.eclipse.jetty.server.Response)
|
||||
*/
|
||||
@Override
|
||||
public void log(Request request, Response response)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_ignorePathMap != null && _ignorePathMap.getMatch(request.getRequestURI()) != null)
|
||||
return;
|
||||
|
||||
if (!isEnabled())
|
||||
return;
|
||||
|
||||
StringBuilder buf= _buffers.get();
|
||||
buf.setLength(0);
|
||||
|
||||
if (_logServer)
|
||||
{
|
||||
buf.append(request.getServerName());
|
||||
buf.append(' ');
|
||||
}
|
||||
|
||||
String addr = null;
|
||||
if (_preferProxiedForAddress)
|
||||
{
|
||||
addr = request.getHeader(HttpHeader.X_FORWARDED_FOR.toString());
|
||||
}
|
||||
|
||||
if (addr == null)
|
||||
addr = request.getRemoteAddr();
|
||||
|
||||
buf.append(addr);
|
||||
buf.append(" - ");
|
||||
Authentication authentication=request.getAuthentication();
|
||||
if (authentication instanceof Authentication.User)
|
||||
buf.append(((Authentication.User)authentication).getUserIdentity().getUserPrincipal().getName());
|
||||
else
|
||||
buf.append(" - ");
|
||||
|
||||
buf.append(" [");
|
||||
if (_logDateCache != null)
|
||||
buf.append(_logDateCache.format(request.getTimeStamp()));
|
||||
else
|
||||
buf.append(request.getTimeStamp());
|
||||
|
||||
buf.append("] \"");
|
||||
buf.append(request.getMethod());
|
||||
buf.append(' ');
|
||||
buf.append(request.getUri().toString());
|
||||
buf.append(' ');
|
||||
buf.append(request.getProtocol());
|
||||
buf.append("\" ");
|
||||
if (request.getHttpChannelState().isInitial())
|
||||
{
|
||||
int status = response.getStatus();
|
||||
if (status <= 0)
|
||||
status = 404;
|
||||
buf.append((char)('0' + ((status / 100) % 10)));
|
||||
buf.append((char)('0' + ((status / 10) % 10)));
|
||||
buf.append((char)('0' + (status % 10)));
|
||||
}
|
||||
else
|
||||
buf.append("Async");
|
||||
|
||||
long responseLength = response.getLongContentLength();
|
||||
if (responseLength >= 0)
|
||||
{
|
||||
buf.append(' ');
|
||||
if (responseLength > 99999)
|
||||
buf.append(responseLength);
|
||||
else
|
||||
{
|
||||
if (responseLength > 9999)
|
||||
buf.append((char)('0' + ((responseLength / 10000) % 10)));
|
||||
if (responseLength > 999)
|
||||
buf.append((char)('0' + ((responseLength / 1000) % 10)));
|
||||
if (responseLength > 99)
|
||||
buf.append((char)('0' + ((responseLength / 100) % 10)));
|
||||
if (responseLength > 9)
|
||||
buf.append((char)('0' + ((responseLength / 10) % 10)));
|
||||
buf.append((char)('0' + (responseLength) % 10));
|
||||
}
|
||||
buf.append(' ');
|
||||
}
|
||||
else
|
||||
buf.append(" - ");
|
||||
|
||||
|
||||
if (_extended)
|
||||
logExtended(request, response, buf);
|
||||
|
||||
if (_logCookies)
|
||||
{
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if (cookies == null || cookies.length == 0)
|
||||
buf.append(" -");
|
||||
else
|
||||
{
|
||||
buf.append(" \"");
|
||||
for (int i = 0; i < cookies.length; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
buf.append(';');
|
||||
buf.append(cookies[i].getName());
|
||||
buf.append('=');
|
||||
buf.append(cookies[i].getValue());
|
||||
}
|
||||
buf.append('\"');
|
||||
}
|
||||
}
|
||||
|
||||
if (_logDispatch || _logLatency)
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
if (_logDispatch)
|
||||
{
|
||||
long d = request.getDispatchTime();
|
||||
buf.append(' ');
|
||||
buf.append(now - (d==0 ? request.getTimeStamp():d));
|
||||
}
|
||||
|
||||
if (_logLatency)
|
||||
{
|
||||
buf.append(' ');
|
||||
buf.append(now - request.getTimeStamp());
|
||||
}
|
||||
}
|
||||
|
||||
String log = buf.toString();
|
||||
write(log);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Writes extended request and response information to the output stream.
|
||||
*
|
||||
* @param request request object
|
||||
* @param response response object
|
||||
* @param b StringBuilder to write to
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void logExtended(Request request,
|
||||
Response response,
|
||||
StringBuilder b) throws IOException
|
||||
{
|
||||
String referer = request.getHeader(HttpHeader.REFERER.toString());
|
||||
if (referer == null)
|
||||
b.append("\"-\" ");
|
||||
else
|
||||
{
|
||||
b.append('"');
|
||||
b.append(referer);
|
||||
b.append("\" ");
|
||||
}
|
||||
|
||||
String agent = request.getHeader(HttpHeader.USER_AGENT.toString());
|
||||
if (agent == null)
|
||||
b.append("\"-\" ");
|
||||
else
|
||||
{
|
||||
b.append('"');
|
||||
b.append(agent);
|
||||
b.append('"');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set request paths that will not be logged.
|
||||
*
|
||||
* @param ignorePaths array of request paths
|
||||
*/
|
||||
public void setIgnorePaths(String[] ignorePaths)
|
||||
{
|
||||
_ignorePaths = ignorePaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the request paths that will not be logged.
|
||||
*
|
||||
* @return array of request paths
|
||||
*/
|
||||
public String[] getIgnorePaths()
|
||||
{
|
||||
return _ignorePaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls logging of the request cookies.
|
||||
*
|
||||
* @param logCookies true - values of request cookies will be logged,
|
||||
* false - values of request cookies will not be logged
|
||||
*/
|
||||
public void setLogCookies(boolean logCookies)
|
||||
{
|
||||
_logCookies = logCookies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve log cookies flag
|
||||
*
|
||||
* @return value of the flag
|
||||
*/
|
||||
public boolean getLogCookies()
|
||||
{
|
||||
return _logCookies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls logging of the request hostname.
|
||||
*
|
||||
* @param logServer true - request hostname will be logged,
|
||||
* false - request hostname will not be logged
|
||||
*/
|
||||
public void setLogServer(boolean logServer)
|
||||
{
|
||||
_logServer = logServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve log hostname flag.
|
||||
*
|
||||
* @return value of the flag
|
||||
*/
|
||||
public boolean getLogServer()
|
||||
{
|
||||
return _logServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls logging of request processing time.
|
||||
*
|
||||
* @param logLatency true - request processing time will be logged
|
||||
* false - request processing time will not be logged
|
||||
*/
|
||||
public void setLogLatency(boolean logLatency)
|
||||
{
|
||||
_logLatency = logLatency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve log request processing time flag.
|
||||
*
|
||||
* @return value of the flag
|
||||
*/
|
||||
public boolean getLogLatency()
|
||||
{
|
||||
return _logLatency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls logging of the request dispatch time
|
||||
*
|
||||
* @param value true - request dispatch time will be logged
|
||||
* false - request dispatch time will not be logged
|
||||
*/
|
||||
public void setLogDispatch(boolean value)
|
||||
{
|
||||
_logDispatch = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve request dispatch time logging flag
|
||||
*
|
||||
* @return value of the flag
|
||||
*/
|
||||
public boolean isLogDispatch()
|
||||
{
|
||||
return _logDispatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls whether the actual IP address of the connection or
|
||||
* the IP address from the X-Forwarded-For header will be logged.
|
||||
*
|
||||
* @param preferProxiedForAddress true - IP address from header will be logged,
|
||||
* false - IP address from the connection will be logged
|
||||
*/
|
||||
public void setPreferProxiedForAddress(boolean preferProxiedForAddress)
|
||||
{
|
||||
_preferProxiedForAddress = preferProxiedForAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieved log X-Forwarded-For IP address flag.
|
||||
*
|
||||
* @return value of the flag
|
||||
*/
|
||||
public boolean getPreferProxiedForAddress()
|
||||
{
|
||||
return _preferProxiedForAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the extended request log format flag.
|
||||
*
|
||||
* @param extended true - log the extended request information,
|
||||
* false - do not log the extended request information
|
||||
*/
|
||||
public void setExtended(boolean extended)
|
||||
{
|
||||
_extended = extended;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the extended request log format flag.
|
||||
*
|
||||
* @return value of the flag
|
||||
*/
|
||||
@ManagedAttribute("use extended NCSA format")
|
||||
public boolean isExtended()
|
||||
{
|
||||
return _extended;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up request logging and open log file.
|
||||
*
|
||||
* @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
|
||||
*/
|
||||
@Override
|
||||
protected synchronized void doStart() throws Exception
|
||||
{
|
||||
if (_logDateFormat != null)
|
||||
{
|
||||
_logDateCache = new DateCache(_logDateFormat,_logLocale);
|
||||
_logDateCache.setTimeZoneID(_logTimeZone);
|
||||
}
|
||||
|
||||
if (_ignorePaths != null && _ignorePaths.length > 0)
|
||||
{
|
||||
_ignorePathMap = new PathMap<>();
|
||||
for (int i = 0; i < _ignorePaths.length; i++)
|
||||
_ignorePathMap.put(_ignorePaths[i],_ignorePaths[i]);
|
||||
}
|
||||
else
|
||||
_ignorePathMap = null;
|
||||
|
||||
super.doStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception
|
||||
{
|
||||
_logDateCache = null;
|
||||
super.doStop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the timestamp format for request log entries in the file.
|
||||
* If this is not set, the pre-formated request timestamp is used.
|
||||
*
|
||||
* @param format timestamp format string
|
||||
*/
|
||||
public void setLogDateFormat(String format)
|
||||
{
|
||||
_logDateFormat = format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the timestamp format string for request log entries.
|
||||
*
|
||||
* @return timestamp format string.
|
||||
*/
|
||||
public String getLogDateFormat()
|
||||
{
|
||||
return _logDateFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the locale of the request log.
|
||||
*
|
||||
* @param logLocale locale object
|
||||
*/
|
||||
public void setLogLocale(Locale logLocale)
|
||||
{
|
||||
_logLocale = logLocale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the locale of the request log.
|
||||
*
|
||||
* @return locale object
|
||||
*/
|
||||
public Locale getLogLocale()
|
||||
{
|
||||
return _logLocale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the timezone of the request log.
|
||||
*
|
||||
* @param tz timezone string
|
||||
*/
|
||||
public void setLogTimeZone(String tz)
|
||||
{
|
||||
_logTimeZone = tz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the timezone of the request log.
|
||||
*
|
||||
* @return timezone string
|
||||
*/
|
||||
@ManagedAttribute("the timezone")
|
||||
public String getLogTimeZone()
|
||||
{
|
||||
return _logTimeZone;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.util.ConcurrentArrayBlockingQueue;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* An asynchronously writing NCSA Request Log
|
||||
*/
|
||||
public class AsyncNCSARequestLog extends NCSARequestLog
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(AsyncNCSARequestLog.class);
|
||||
private final BlockingQueue<String> _queue;
|
||||
private transient WriterThread _thread;
|
||||
private boolean _warnedFull;
|
||||
|
||||
public AsyncNCSARequestLog()
|
||||
{
|
||||
this(null,null);
|
||||
}
|
||||
|
||||
public AsyncNCSARequestLog(BlockingQueue<String> queue)
|
||||
{
|
||||
this(null,queue);
|
||||
}
|
||||
|
||||
public AsyncNCSARequestLog(String filename)
|
||||
{
|
||||
this(filename,null);
|
||||
}
|
||||
|
||||
public AsyncNCSARequestLog(String filename,BlockingQueue<String> queue)
|
||||
{
|
||||
super(filename);
|
||||
if (queue==null)
|
||||
queue=new ConcurrentArrayBlockingQueue.Bounded<String>(1024);
|
||||
_queue=queue;
|
||||
}
|
||||
|
||||
private class WriterThread extends Thread
|
||||
{
|
||||
WriterThread()
|
||||
{
|
||||
setName("AsyncNCSARequestLog@"+Integer.toString(AsyncNCSARequestLog.this.hashCode(),16));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
while (isRunning())
|
||||
{
|
||||
try
|
||||
{
|
||||
String log = _queue.poll(10,TimeUnit.SECONDS);
|
||||
if (log!=null)
|
||||
AsyncNCSARequestLog.super.write(log);
|
||||
|
||||
while(!_queue.isEmpty())
|
||||
{
|
||||
log=_queue.poll();
|
||||
if (log!=null)
|
||||
AsyncNCSARequestLog.super.write(log);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
LOG.ignore(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void doStart() throws Exception
|
||||
{
|
||||
super.doStart();
|
||||
_thread = new WriterThread();
|
||||
_thread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception
|
||||
{
|
||||
_thread.interrupt();
|
||||
_thread.join();
|
||||
super.doStop();
|
||||
_thread=null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(String log) throws IOException
|
||||
{
|
||||
if (!_queue.offer(log))
|
||||
{
|
||||
if (_warnedFull)
|
||||
LOG.warn("Log Queue overflow");
|
||||
_warnedFull=true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -22,21 +22,12 @@ import java.io.IOException;
|
|||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.PathMap;
|
||||
import org.eclipse.jetty.util.DateCache;
|
||||
import org.eclipse.jetty.util.RolloverFileOutputStream;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
/**
|
||||
* This {@link RequestLog} implementation outputs logs in the pseudo-standard
|
||||
|
@ -45,37 +36,17 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
* Format (single log format). This log format can be output by most web
|
||||
* servers, and almost all web log analysis software can understand these
|
||||
* formats.
|
||||
*
|
||||
*/
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
*/
|
||||
@ManagedObject("NCSA standard format request log")
|
||||
public class NCSARequestLog extends AbstractLifeCycle implements RequestLog
|
||||
public class NCSARequestLog extends AbstractNCSARequestLog implements RequestLog
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(NCSARequestLog.class);
|
||||
|
||||
private String _filename;
|
||||
private boolean _extended;
|
||||
private boolean _append;
|
||||
private int _retainDays;
|
||||
private boolean _closeOut;
|
||||
private boolean _preferProxiedForAddress;
|
||||
private String _logDateFormat = "dd/MMM/yyyy:HH:mm:ss Z";
|
||||
private String _filenameDateFormat = null;
|
||||
private Locale _logLocale = Locale.getDefault();
|
||||
private String _logTimeZone = "GMT";
|
||||
private String[] _ignorePaths;
|
||||
private boolean _logLatency = false;
|
||||
private boolean _logCookies = false;
|
||||
private boolean _logServer = false;
|
||||
private boolean _logDispatch = false;
|
||||
|
||||
private transient OutputStream _out;
|
||||
private transient OutputStream _fileOut;
|
||||
private transient DateCache _logDateCache;
|
||||
private transient PathMap _ignorePathMap;
|
||||
private transient Writer _writer;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -84,7 +55,7 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog
|
|||
*/
|
||||
public NCSARequestLog()
|
||||
{
|
||||
_extended = true;
|
||||
setExtended(true);
|
||||
_append = true;
|
||||
_retainDays = 31;
|
||||
}
|
||||
|
@ -99,7 +70,7 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog
|
|||
*/
|
||||
public NCSARequestLog(String filename)
|
||||
{
|
||||
_extended = true;
|
||||
setExtended(true);
|
||||
_append = true;
|
||||
_retainDays = 31;
|
||||
setFilename(filename);
|
||||
|
@ -136,7 +107,7 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog
|
|||
{
|
||||
return _filename;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Retrieve the file name of the request log with the expanded
|
||||
|
@ -153,71 +124,10 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Set the timestamp format for request log entries in the file.
|
||||
* If this is not set, the pre-formated request timestamp is used.
|
||||
*
|
||||
* @param format timestamp format string
|
||||
*/
|
||||
public void setLogDateFormat(String format)
|
||||
@Override
|
||||
protected boolean isEnabled()
|
||||
{
|
||||
_logDateFormat = format;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Retrieve the timestamp format string for request log entries.
|
||||
*
|
||||
* @return timestamp format string.
|
||||
*/
|
||||
public String getLogDateFormat()
|
||||
{
|
||||
return _logDateFormat;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Set the locale of the request log.
|
||||
*
|
||||
* @param logLocale locale object
|
||||
*/
|
||||
public void setLogLocale(Locale logLocale)
|
||||
{
|
||||
_logLocale = logLocale;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Retrieve the locale of the request log.
|
||||
*
|
||||
* @return locale object
|
||||
*/
|
||||
public Locale getLogLocale()
|
||||
{
|
||||
return _logLocale;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Set the timezone of the request log.
|
||||
*
|
||||
* @param tz timezone string
|
||||
*/
|
||||
public void setLogTimeZone(String tz)
|
||||
{
|
||||
_logTimeZone = tz;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Retrieve the timezone of the request log.
|
||||
*
|
||||
* @return timezone string
|
||||
*/
|
||||
@ManagedAttribute("the timezone")
|
||||
public String getLogTimeZone()
|
||||
{
|
||||
return _logTimeZone;
|
||||
return (_fileOut != null);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -243,30 +153,6 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog
|
|||
return _retainDays;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Set the extended request log format flag.
|
||||
*
|
||||
* @param extended true - log the extended request information,
|
||||
* false - do not log the extended request information
|
||||
*/
|
||||
public void setExtended(boolean extended)
|
||||
{
|
||||
_extended = extended;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Retrieve the extended request log format flag.
|
||||
*
|
||||
* @return value of the flag
|
||||
*/
|
||||
@ManagedAttribute("use extended NCSA format")
|
||||
public boolean isExtended()
|
||||
{
|
||||
return _extended;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Set append to log flag.
|
||||
|
@ -291,121 +177,6 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog
|
|||
return _append;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Set request paths that will not be logged.
|
||||
*
|
||||
* @param ignorePaths array of request paths
|
||||
*/
|
||||
public void setIgnorePaths(String[] ignorePaths)
|
||||
{
|
||||
_ignorePaths = ignorePaths;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Retrieve the request paths that will not be logged.
|
||||
*
|
||||
* @return array of request paths
|
||||
*/
|
||||
public String[] getIgnorePaths()
|
||||
{
|
||||
return _ignorePaths;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Controls logging of the request cookies.
|
||||
*
|
||||
* @param logCookies true - values of request cookies will be logged,
|
||||
* false - values of request cookies will not be logged
|
||||
*/
|
||||
public void setLogCookies(boolean logCookies)
|
||||
{
|
||||
_logCookies = logCookies;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Retrieve log cookies flag
|
||||
*
|
||||
* @return value of the flag
|
||||
*/
|
||||
public boolean getLogCookies()
|
||||
{
|
||||
return _logCookies;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Controls logging of the request hostname.
|
||||
*
|
||||
* @param logServer true - request hostname will be logged,
|
||||
* false - request hostname will not be logged
|
||||
*/
|
||||
public void setLogServer(boolean logServer)
|
||||
{
|
||||
_logServer = logServer;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Retrieve log hostname flag.
|
||||
*
|
||||
* @return value of the flag
|
||||
*/
|
||||
public boolean getLogServer()
|
||||
{
|
||||
return _logServer;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Controls logging of request processing time.
|
||||
*
|
||||
* @param logLatency true - request processing time will be logged
|
||||
* false - request processing time will not be logged
|
||||
*/
|
||||
public void setLogLatency(boolean logLatency)
|
||||
{
|
||||
_logLatency = logLatency;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Retrieve log request processing time flag.
|
||||
*
|
||||
* @return value of the flag
|
||||
*/
|
||||
public boolean getLogLatency()
|
||||
{
|
||||
return _logLatency;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Controls whether the actual IP address of the connection or
|
||||
* the IP address from the X-Forwarded-For header will be logged.
|
||||
*
|
||||
* @param preferProxiedForAddress true - IP address from header will be logged,
|
||||
* false - IP address from the connection will be logged
|
||||
*/
|
||||
public void setPreferProxiedForAddress(boolean preferProxiedForAddress)
|
||||
{
|
||||
_preferProxiedForAddress = preferProxiedForAddress;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Retrieved log X-Forwarded-For IP address flag.
|
||||
*
|
||||
* @return value of the flag
|
||||
*/
|
||||
public boolean getPreferProxiedForAddress()
|
||||
{
|
||||
return _preferProxiedForAddress;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Set the log file name date format.
|
||||
|
@ -430,210 +201,19 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Controls logging of the request dispatch time
|
||||
*
|
||||
* @param value true - request dispatch time will be logged
|
||||
* false - request dispatch time will not be logged
|
||||
*/
|
||||
public void setLogDispatch(boolean value)
|
||||
@Override
|
||||
public void write(String requestEntry) throws IOException
|
||||
{
|
||||
_logDispatch = value;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Retrieve request dispatch time logging flag
|
||||
*
|
||||
* @return value of the flag
|
||||
*/
|
||||
public boolean isLogDispatch()
|
||||
{
|
||||
return _logDispatch;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Writes the request and response information to the output stream.
|
||||
*
|
||||
* @see org.eclipse.jetty.server.RequestLog#log(org.eclipse.jetty.server.Request, org.eclipse.jetty.server.Response)
|
||||
*/
|
||||
public void log(Request request, Response response)
|
||||
{
|
||||
try
|
||||
synchronized(this)
|
||||
{
|
||||
if (_ignorePathMap != null && _ignorePathMap.getMatch(request.getRequestURI()) != null)
|
||||
if (_writer==null)
|
||||
return;
|
||||
|
||||
if (_fileOut == null)
|
||||
return;
|
||||
|
||||
StringBuilder buf= new StringBuilder(256);
|
||||
|
||||
if (_logServer)
|
||||
{
|
||||
buf.append(request.getServerName());
|
||||
buf.append(' ');
|
||||
}
|
||||
|
||||
String addr = null;
|
||||
if (_preferProxiedForAddress)
|
||||
{
|
||||
addr = request.getHeader(HttpHeader.X_FORWARDED_FOR.toString());
|
||||
}
|
||||
|
||||
if (addr == null)
|
||||
addr = request.getRemoteAddr();
|
||||
|
||||
buf.append(addr);
|
||||
buf.append(" - ");
|
||||
Authentication authentication=request.getAuthentication();
|
||||
if (authentication instanceof Authentication.User)
|
||||
buf.append(((Authentication.User)authentication).getUserIdentity().getUserPrincipal().getName());
|
||||
else
|
||||
buf.append(" - ");
|
||||
|
||||
buf.append(" [");
|
||||
if (_logDateCache != null)
|
||||
buf.append(_logDateCache.format(request.getTimeStamp()));
|
||||
else
|
||||
buf.append(request.getTimeStamp());
|
||||
|
||||
buf.append("] \"");
|
||||
buf.append(request.getMethod());
|
||||
buf.append(' ');
|
||||
buf.append(request.getUri().toString());
|
||||
buf.append(' ');
|
||||
buf.append(request.getProtocol());
|
||||
buf.append("\" ");
|
||||
if (request.getHttpChannelState().isInitial())
|
||||
{
|
||||
int status = response.getStatus();
|
||||
if (status <= 0)
|
||||
status = 404;
|
||||
buf.append((char)('0' + ((status / 100) % 10)));
|
||||
buf.append((char)('0' + ((status / 10) % 10)));
|
||||
buf.append((char)('0' + (status % 10)));
|
||||
}
|
||||
else
|
||||
buf.append("Async");
|
||||
|
||||
long responseLength = response.getLongContentLength();
|
||||
if (responseLength >= 0)
|
||||
{
|
||||
buf.append(' ');
|
||||
if (responseLength > 99999)
|
||||
buf.append(responseLength);
|
||||
else
|
||||
{
|
||||
if (responseLength > 9999)
|
||||
buf.append((char)('0' + ((responseLength / 10000) % 10)));
|
||||
if (responseLength > 999)
|
||||
buf.append((char)('0' + ((responseLength / 1000) % 10)));
|
||||
if (responseLength > 99)
|
||||
buf.append((char)('0' + ((responseLength / 100) % 10)));
|
||||
if (responseLength > 9)
|
||||
buf.append((char)('0' + ((responseLength / 10) % 10)));
|
||||
buf.append((char)('0' + (responseLength) % 10));
|
||||
}
|
||||
buf.append(' ');
|
||||
}
|
||||
else
|
||||
buf.append(" - ");
|
||||
|
||||
|
||||
if (_extended)
|
||||
logExtended(request, response, buf);
|
||||
|
||||
if (_logCookies)
|
||||
{
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if (cookies == null || cookies.length == 0)
|
||||
buf.append(" -");
|
||||
else
|
||||
{
|
||||
buf.append(" \"");
|
||||
for (int i = 0; i < cookies.length; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
buf.append(';');
|
||||
buf.append(cookies[i].getName());
|
||||
buf.append('=');
|
||||
buf.append(cookies[i].getValue());
|
||||
}
|
||||
buf.append('\"');
|
||||
}
|
||||
}
|
||||
|
||||
if (_logDispatch || _logLatency)
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
if (_logDispatch)
|
||||
{
|
||||
long d = request.getDispatchTime();
|
||||
buf.append(' ');
|
||||
buf.append(now - (d==0 ? request.getTimeStamp():d));
|
||||
}
|
||||
|
||||
if (_logLatency)
|
||||
{
|
||||
buf.append(' ');
|
||||
buf.append(now - request.getTimeStamp());
|
||||
}
|
||||
}
|
||||
|
||||
buf.append(StringUtil.__LINE_SEPARATOR);
|
||||
String log = buf.toString();
|
||||
synchronized(this)
|
||||
{
|
||||
if (_writer==null)
|
||||
return;
|
||||
_writer.write(log);
|
||||
_writer.flush();
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Writes extended request and response information to the output stream.
|
||||
*
|
||||
* @param request request object
|
||||
* @param response response object
|
||||
* @param b StringBuilder to write to
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void logExtended(Request request,
|
||||
Response response,
|
||||
StringBuilder b) throws IOException
|
||||
{
|
||||
String referer = request.getHeader(HttpHeader.REFERER.toString());
|
||||
if (referer == null)
|
||||
b.append("\"-\" ");
|
||||
else
|
||||
{
|
||||
b.append('"');
|
||||
b.append(referer);
|
||||
b.append("\" ");
|
||||
}
|
||||
|
||||
String agent = request.getHeader(HttpHeader.USER_AGENT.toString());
|
||||
if (agent == null)
|
||||
b.append("\"-\" ");
|
||||
else
|
||||
{
|
||||
b.append('"');
|
||||
b.append(agent);
|
||||
b.append('"');
|
||||
_writer.write(requestEntry.toString());
|
||||
_writer.write(StringUtil.__LINE_SEPARATOR);
|
||||
_writer.flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Set up request logging and open log file.
|
||||
|
@ -643,15 +223,9 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog
|
|||
@Override
|
||||
protected synchronized void doStart() throws Exception
|
||||
{
|
||||
if (_logDateFormat != null)
|
||||
{
|
||||
_logDateCache = new DateCache(_logDateFormat,_logLocale);
|
||||
_logDateCache.setTimeZoneID(_logTimeZone);
|
||||
}
|
||||
|
||||
if (_filename != null)
|
||||
{
|
||||
_fileOut = new RolloverFileOutputStream(_filename,_append,_retainDays,TimeZone.getTimeZone(_logTimeZone),_filenameDateFormat,null);
|
||||
_fileOut = new RolloverFileOutputStream(_filename,_append,_retainDays,TimeZone.getTimeZone(getLogTimeZone()),_filenameDateFormat,null);
|
||||
_closeOut = true;
|
||||
LOG.info("Opened " + getDatedFilename());
|
||||
}
|
||||
|
@ -660,16 +234,10 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog
|
|||
|
||||
_out = _fileOut;
|
||||
|
||||
if (_ignorePaths != null && _ignorePaths.length > 0)
|
||||
synchronized(this)
|
||||
{
|
||||
_ignorePathMap = new PathMap();
|
||||
for (int i = 0; i < _ignorePaths.length; i++)
|
||||
_ignorePathMap.put(_ignorePaths[i],_ignorePaths[i]);
|
||||
_writer = new OutputStreamWriter(_out);
|
||||
}
|
||||
else
|
||||
_ignorePathMap = null;
|
||||
|
||||
_writer = new OutputStreamWriter(_out);
|
||||
super.doStart();
|
||||
}
|
||||
|
||||
|
@ -707,7 +275,6 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog
|
|||
_out = null;
|
||||
_fileOut = null;
|
||||
_closeOut = false;
|
||||
_logDateCache = null;
|
||||
_writer = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1779,6 +1779,9 @@ public class Request implements HttpServletRequest
|
|||
public void setHandled(boolean h)
|
||||
{
|
||||
_handled = h;
|
||||
Response r=getResponse();
|
||||
if (_handled && r.getStatus()==0)
|
||||
r.setStatus(200);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -280,10 +280,12 @@ public class Server extends HandlerWrapper implements Attributes
|
|||
@Override
|
||||
protected void doStart() throws Exception
|
||||
{
|
||||
if (getStopAtShutdown()) {
|
||||
ShutdownThread.register(this);
|
||||
ShutdownMonitor.getInstance().start(); // initialize
|
||||
if (getStopAtShutdown())
|
||||
{
|
||||
ShutdownThread.register(this);
|
||||
}
|
||||
|
||||
ShutdownMonitor.getInstance().start(); // initialize
|
||||
|
||||
LOG.info("jetty-"+getVersion());
|
||||
HttpGenerator.setServerVersion(getVersion());
|
||||
|
|
|
@ -40,7 +40,7 @@ import org.eclipse.jetty.util.thread.ShutdownThread;
|
|||
* <p>
|
||||
* Commands "stop" and "status" are currently supported.
|
||||
*/
|
||||
public class ShutdownMonitor extends Thread
|
||||
public class ShutdownMonitor
|
||||
{
|
||||
// Implementation of safe lazy init, using Initialization on Demand Holder technique.
|
||||
static class Holder
|
||||
|
@ -53,11 +53,165 @@ public class ShutdownMonitor extends Thread
|
|||
return Holder.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* ShutdownMonitorThread
|
||||
*
|
||||
* Thread for listening to STOP.PORT for command to stop Jetty.
|
||||
* If ShowndownMonitor.exitVm is true, then Sytem.exit will also be
|
||||
* called after the stop.
|
||||
*
|
||||
*/
|
||||
public class ShutdownMonitorThread extends Thread
|
||||
{
|
||||
|
||||
public ShutdownMonitorThread ()
|
||||
{
|
||||
setDaemon(true);
|
||||
setName("ShutdownMonitor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (serverSocket == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (serverSocket != null)
|
||||
{
|
||||
Socket socket = null;
|
||||
try
|
||||
{
|
||||
socket = serverSocket.accept();
|
||||
|
||||
LineNumberReader lin = new LineNumberReader(new InputStreamReader(socket.getInputStream()));
|
||||
String receivedKey = lin.readLine();
|
||||
if (!key.equals(receivedKey))
|
||||
{
|
||||
System.err.println("Ignoring command with incorrect key");
|
||||
continue;
|
||||
}
|
||||
|
||||
OutputStream out = socket.getOutputStream();
|
||||
|
||||
String cmd = lin.readLine();
|
||||
debug("command=%s",cmd);
|
||||
if ("stop".equals(cmd))
|
||||
{
|
||||
// Graceful Shutdown
|
||||
debug("Issuing graceful shutdown..");
|
||||
ShutdownThread.getInstance().run();
|
||||
|
||||
// Reply to client
|
||||
debug("Informing client that we are stopped.");
|
||||
out.write("Stopped\r\n".getBytes(StringUtil.__UTF8));
|
||||
out.flush();
|
||||
|
||||
// Shutdown Monitor
|
||||
debug("Shutting down monitor");
|
||||
close(socket);
|
||||
socket = null;
|
||||
close(serverSocket);
|
||||
serverSocket = null;
|
||||
|
||||
if (exitVm)
|
||||
{
|
||||
// Kill JVM
|
||||
debug("Killing JVM");
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
else if ("status".equals(cmd))
|
||||
{
|
||||
// Reply to client
|
||||
out.write("OK\r\n".getBytes(StringUtil.__UTF8));
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
debug(e);
|
||||
System.err.println(e.toString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
close(socket);
|
||||
socket = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void start()
|
||||
{
|
||||
if (isAlive())
|
||||
{
|
||||
if (DEBUG)
|
||||
System.err.printf("ShutdownMonitorThread already started");
|
||||
return; // cannot start it again
|
||||
}
|
||||
|
||||
startListenSocket();
|
||||
|
||||
if (serverSocket == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
System.err.println("Starting ShutdownMonitorThread");
|
||||
super.start();
|
||||
}
|
||||
|
||||
private void startListenSocket()
|
||||
{
|
||||
if (port < 0)
|
||||
{
|
||||
if (DEBUG)
|
||||
System.err.println("ShutdownMonitor not in use (port < 0): " + port);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
serverSocket = new ServerSocket(port,1,InetAddress.getByName("127.0.0.1"));
|
||||
if (port == 0)
|
||||
{
|
||||
// server assigned port in use
|
||||
port = serverSocket.getLocalPort();
|
||||
System.out.printf("STOP.PORT=%d%n",port);
|
||||
}
|
||||
|
||||
if (key == null)
|
||||
{
|
||||
// create random key
|
||||
key = Long.toString((long)(Long.MAX_VALUE * Math.random() + this.hashCode() + System.currentTimeMillis()),36);
|
||||
System.out.printf("STOP.KEY=%s%n",key);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
debug(e);
|
||||
System.err.println("Error binding monitor port " + port + ": " + e.toString());
|
||||
serverSocket = null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// establish the port and key that are in use
|
||||
debug("STOP.PORT=%d",port);
|
||||
debug("STOP.KEY=%s",key);
|
||||
debug("%s",serverSocket);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean DEBUG;
|
||||
private int port;
|
||||
private String key;
|
||||
private boolean exitVm;
|
||||
private ServerSocket serverSocket;
|
||||
private ShutdownMonitorThread thread;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create a ShutdownMonitor using configuration from the System properties.
|
||||
|
@ -75,7 +229,7 @@ public class ShutdownMonitor extends Thread
|
|||
|
||||
// Use values passed thru via /jetty-start/
|
||||
this.port = Integer.parseInt(props.getProperty("STOP.PORT","-1"));
|
||||
this.key = props.getProperty("STOP.KEY","eclipse");
|
||||
this.key = props.getProperty("STOP.KEY",null);
|
||||
this.exitVm = true;
|
||||
}
|
||||
|
||||
|
@ -149,77 +303,6 @@ public class ShutdownMonitor extends Thread
|
|||
return exitVm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (serverSocket == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (serverSocket != null)
|
||||
{
|
||||
Socket socket = null;
|
||||
try
|
||||
{
|
||||
socket = serverSocket.accept();
|
||||
|
||||
LineNumberReader lin = new LineNumberReader(new InputStreamReader(socket.getInputStream()));
|
||||
String key = lin.readLine();
|
||||
if (!this.key.equals(key))
|
||||
{
|
||||
System.err.println("Ignoring command with incorrect key");
|
||||
continue;
|
||||
}
|
||||
|
||||
OutputStream out = socket.getOutputStream();
|
||||
|
||||
String cmd = lin.readLine();
|
||||
debug("command=%s",cmd);
|
||||
if ("stop".equals(cmd))
|
||||
{
|
||||
// Graceful Shutdown
|
||||
debug("Issuing graceful shutdown..");
|
||||
ShutdownThread.getInstance().run();
|
||||
|
||||
// Reply to client
|
||||
debug("Informing client that we are stopped.");
|
||||
out.write("Stopped\r\n".getBytes(StringUtil.__UTF8));
|
||||
out.flush();
|
||||
|
||||
// Shutdown Monitor
|
||||
debug("Shutting down monitor");
|
||||
close(socket);
|
||||
socket = null;
|
||||
close(serverSocket);
|
||||
serverSocket = null;
|
||||
|
||||
if (exitVm)
|
||||
{
|
||||
// Kill JVM
|
||||
debug("Killing JVM");
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
else if ("status".equals(cmd))
|
||||
{
|
||||
// Reply to client
|
||||
out.write("OK\r\n".getBytes(StringUtil.__UTF8));
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
debug(e);
|
||||
System.err.println(e.toString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
close(socket);
|
||||
socket = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setDebug(boolean flag)
|
||||
{
|
||||
|
@ -228,90 +311,71 @@ public class ShutdownMonitor extends Thread
|
|||
|
||||
public void setExitVm(boolean exitVm)
|
||||
{
|
||||
if (isAlive())
|
||||
synchronized (this)
|
||||
{
|
||||
throw new IllegalStateException("ShutdownMonitor already started");
|
||||
if (thread != null && thread.isAlive())
|
||||
{
|
||||
throw new IllegalStateException("ShutdownMonitorThread already started");
|
||||
}
|
||||
this.exitVm = exitVm;
|
||||
}
|
||||
this.exitVm = exitVm;
|
||||
}
|
||||
|
||||
public void setKey(String key)
|
||||
{
|
||||
if (isAlive())
|
||||
synchronized (this)
|
||||
{
|
||||
throw new IllegalStateException("ShutdownMonitor already started");
|
||||
if (thread != null && thread.isAlive())
|
||||
{
|
||||
throw new IllegalStateException("ShutdownMonitorThread already started");
|
||||
}
|
||||
this.key = key;
|
||||
}
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public void setPort(int port)
|
||||
{
|
||||
if (isAlive())
|
||||
synchronized (this)
|
||||
{
|
||||
throw new IllegalStateException("ShutdownMonitor already started");
|
||||
}
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public void start()
|
||||
{
|
||||
if (isAlive())
|
||||
{
|
||||
System.err.printf("ShutdownMonitor already started");
|
||||
return; // cannot start it again
|
||||
}
|
||||
startListenSocket();
|
||||
if (serverSocket == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
super.start();
|
||||
}
|
||||
|
||||
private void startListenSocket()
|
||||
{
|
||||
if (this.port < 0)
|
||||
{
|
||||
if (DEBUG)
|
||||
System.err.println("ShutdownMonitor not in use (port < 0): " + port);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
setDaemon(true);
|
||||
setName("ShutdownMonitor");
|
||||
|
||||
this.serverSocket = new ServerSocket(this.port,1,InetAddress.getByName("127.0.0.1"));
|
||||
if (this.port == 0)
|
||||
if (thread != null && thread.isAlive())
|
||||
{
|
||||
// server assigned port in use
|
||||
this.port = serverSocket.getLocalPort();
|
||||
System.out.printf("STOP.PORT=%d%n",this.port);
|
||||
throw new IllegalStateException("ShutdownMonitorThread already started");
|
||||
}
|
||||
|
||||
if (this.key == null)
|
||||
{
|
||||
// create random key
|
||||
this.key = Long.toString((long)(Long.MAX_VALUE * Math.random() + this.hashCode() + System.currentTimeMillis()),36);
|
||||
System.out.printf("STOP.KEY=%s%n",this.key);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
debug(e);
|
||||
System.err.println("Error binding monitor port " + this.port + ": " + e.toString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
// establish the port and key that are in use
|
||||
debug("STOP.PORT=%d",this.port);
|
||||
debug("STOP.KEY=%s",this.key);
|
||||
debug("%s",serverSocket);
|
||||
this.port = port;
|
||||
}
|
||||
}
|
||||
|
||||
protected void start() throws Exception
|
||||
{
|
||||
ShutdownMonitorThread t = null;
|
||||
synchronized (this)
|
||||
{
|
||||
if (thread != null && thread.isAlive())
|
||||
{
|
||||
System.err.printf("ShutdownMonitorThread already started");
|
||||
return; // cannot start it again
|
||||
}
|
||||
|
||||
thread = new ShutdownMonitorThread();
|
||||
t = thread;
|
||||
}
|
||||
|
||||
if (t != null)
|
||||
t.start();
|
||||
}
|
||||
|
||||
|
||||
protected boolean isAlive ()
|
||||
{
|
||||
boolean result = false;
|
||||
synchronized (this)
|
||||
{
|
||||
result = (thread != null && thread.isAlive());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.server;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.log.Slf4jLog;
|
||||
|
||||
/**
|
||||
* Implementation of NCSARequestLog where output is sent as a SLF4J INFO Log message on the named logger "org.eclipse.jetty.server.RequestLog"
|
||||
*/
|
||||
@ManagedObject("NCSA standard format request log to slf4j bridge")
|
||||
public class Slf4jRequestLog extends AbstractNCSARequestLog implements RequestLog
|
||||
{
|
||||
private Slf4jLog logger;
|
||||
private String loggerName;
|
||||
|
||||
public Slf4jRequestLog()
|
||||
{
|
||||
// Default logger name (can be set)
|
||||
this.loggerName = "org.eclipse.jetty.server.RequestLog";
|
||||
}
|
||||
|
||||
public void setLoggerName(String loggerName)
|
||||
{
|
||||
this.loggerName = loggerName;
|
||||
}
|
||||
|
||||
public String getLoggerName()
|
||||
{
|
||||
return loggerName;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnabled()
|
||||
{
|
||||
return logger != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(String requestEntry) throws IOException
|
||||
{
|
||||
logger.info(requestEntry);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void doStart() throws Exception
|
||||
{
|
||||
logger = new Slf4jLog(loggerName);
|
||||
super.doStart();
|
||||
}
|
||||
}
|
|
@ -19,12 +19,11 @@
|
|||
package org.eclipse.jetty.server.session;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
@ -37,6 +36,7 @@ import javax.servlet.ServletContext;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
||||
|
@ -589,70 +589,56 @@ public class HashSessionManager extends AbstractSessionManager
|
|||
for (HashedSession session : _sessions.values())
|
||||
session.save(reactivate);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public HashedSession restoreSession (InputStream is, HashedSession session) throws Exception
|
||||
{
|
||||
/*
|
||||
* Take care of this class's fields first by calling
|
||||
* defaultReadObject
|
||||
*/
|
||||
DataInputStream in = new DataInputStream(is);
|
||||
String clusterId = in.readUTF();
|
||||
in.readUTF(); // nodeId
|
||||
long created = in.readLong();
|
||||
long accessed = in.readLong();
|
||||
int requests = in.readInt();
|
||||
DataInputStream di = new DataInputStream(is);
|
||||
|
||||
String clusterId = di.readUTF();
|
||||
di.readUTF(); // nodeId
|
||||
|
||||
long created = di.readLong();
|
||||
long accessed = di.readLong();
|
||||
int requests = di.readInt();
|
||||
|
||||
if (session == null)
|
||||
session = (HashedSession)newSession(created, accessed, clusterId);
|
||||
session.setRequests(requests);
|
||||
int size = in.readInt();
|
||||
|
||||
int size = di.readInt();
|
||||
|
||||
restoreSessionAttributes(di, size, session);
|
||||
|
||||
try
|
||||
{
|
||||
int maxIdle = di.readInt();
|
||||
session.setMaxInactiveInterval(maxIdle);
|
||||
}
|
||||
catch (EOFException e)
|
||||
{
|
||||
LOG.debug("No maxInactiveInterval persisted for session "+clusterId);
|
||||
LOG.ignore(e);
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
|
||||
private void restoreSessionAttributes (InputStream is, int size, HashedSession session)
|
||||
throws Exception
|
||||
{
|
||||
if (size>0)
|
||||
{
|
||||
ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(in);
|
||||
ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(is);
|
||||
|
||||
for (int i=0; i<size;i++)
|
||||
{
|
||||
String key = ois.readUTF();
|
||||
Object value = ois.readObject();
|
||||
session.setAttribute(key,value);
|
||||
}
|
||||
ois.close();
|
||||
}
|
||||
else
|
||||
in.close();
|
||||
return session;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
protected class ClassLoadingObjectInputStream extends ObjectInputStream
|
||||
{
|
||||
/* ------------------------------------------------------------ */
|
||||
public ClassLoadingObjectInputStream(java.io.InputStream in) throws IOException
|
||||
{
|
||||
super(in);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public ClassLoadingObjectInputStream () throws IOException
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public Class<?> resolveClass (java.io.ObjectStreamClass cl) throws IOException, ClassNotFoundException
|
||||
{
|
||||
try
|
||||
{
|
||||
return Class.forName(cl.getName(), false, Thread.currentThread().getContextClassLoader());
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
return super.resolveClass(cl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -182,7 +182,6 @@ public class HashedSession extends AbstractSession
|
|||
*/
|
||||
//out.writeBoolean(_invalid);
|
||||
//out.writeBoolean(_doInvalidate);
|
||||
//out.writeLong(_maxIdleMs);
|
||||
//out.writeBoolean( _newSession);
|
||||
out.writeInt(getRequests());
|
||||
out.writeInt(getAttributes());
|
||||
|
@ -194,7 +193,8 @@ public class HashedSession extends AbstractSession
|
|||
oos.writeUTF(key);
|
||||
oos.writeObject(doGet(key));
|
||||
}
|
||||
oos.close();
|
||||
|
||||
out.writeInt(getMaxInactiveInterval());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -36,6 +36,7 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
|
@ -74,6 +75,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
|
|||
protected String _sessionIdTable = "JettySessionIds";
|
||||
protected String _sessionTable = "JettySessions";
|
||||
protected String _sessionTableRowId = "rowId";
|
||||
protected int _deleteBlockSize = 10; //number of ids to include in where 'in' clause
|
||||
|
||||
protected Timer _timer; //scavenge timer
|
||||
protected TimerTask _task; //scavenge task
|
||||
|
@ -86,7 +88,6 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
|
|||
protected String _createSessionTable;
|
||||
|
||||
protected String _selectBoundedExpiredSessions;
|
||||
protected String _deleteOldExpiredSessions;
|
||||
|
||||
protected String _insertId;
|
||||
protected String _deleteId;
|
||||
|
@ -325,7 +326,17 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
|
|||
{
|
||||
this._longType = longType;
|
||||
}
|
||||
|
||||
public void setDeleteBlockSize (int bsize)
|
||||
{
|
||||
this._deleteBlockSize = bsize;
|
||||
}
|
||||
|
||||
public int getDeleteBlockSize ()
|
||||
{
|
||||
return this._deleteBlockSize;
|
||||
}
|
||||
|
||||
public void setScavengeInterval (long sec)
|
||||
{
|
||||
if (sec<=0)
|
||||
|
@ -430,7 +441,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
|
|||
synchronized (_sessionIds)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Removing session id="+id);
|
||||
LOG.debug("Removing sessionid="+id);
|
||||
try
|
||||
{
|
||||
_sessionIds.remove(id);
|
||||
|
@ -568,22 +579,15 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
|
|||
*/
|
||||
@Override
|
||||
public void doStart()
|
||||
throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
initializeDatabase();
|
||||
prepareTables();
|
||||
cleanExpiredSessions();
|
||||
super.doStart();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Scavenging interval = "+getScavengeInterval()+" sec");
|
||||
_timer=new Timer("JDBCSessionScavenger", true);
|
||||
setScavengeInterval(getScavengeInterval());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Problem initialising JettySessionIds table", e);
|
||||
}
|
||||
initializeDatabase();
|
||||
prepareTables();
|
||||
super.doStart();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Scavenging interval = "+getScavengeInterval()+" sec");
|
||||
_timer=new Timer("JDBCSessionScavenger", true);
|
||||
setScavengeInterval(getScavengeInterval());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -637,9 +641,8 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
|
|||
throws SQLException
|
||||
{
|
||||
_createSessionIdTable = "create table "+_sessionIdTable+" (id varchar(120), primary key(id))";
|
||||
_selectBoundedExpiredSessions = "select * from "+_sessionTable+" where expiryTime >= ? and expiryTime <= ?";
|
||||
_selectBoundedExpiredSessions = "select * from "+_sessionTable+" where lastNode = ? and expiryTime >= ? and expiryTime <= ?";
|
||||
_selectExpiredSessions = "select * from "+_sessionTable+" where expiryTime >0 and expiryTime <= ?";
|
||||
_deleteOldExpiredSessions = "delete from "+_sessionTable+" where expiryTime >0 and expiryTime <= ?";
|
||||
|
||||
_insertId = "insert into "+_sessionIdTable+" (id) values (?)";
|
||||
_deleteId = "delete from "+_sessionIdTable+" where id = ?";
|
||||
|
@ -857,57 +860,79 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
|
|||
private void scavenge ()
|
||||
{
|
||||
Connection connection = null;
|
||||
List<String> expiredSessionIds = new ArrayList<String>();
|
||||
Set<String> expiredSessionIds = new HashSet<String>();
|
||||
try
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Scavenge sweep started at "+System.currentTimeMillis());
|
||||
LOG.debug(getWorkerName()+"- Scavenge sweep started at "+System.currentTimeMillis());
|
||||
if (_lastScavengeTime > 0)
|
||||
{
|
||||
connection = getConnection();
|
||||
connection.setAutoCommit(true);
|
||||
//"select sessionId from JettySessions where expiryTime > (lastScavengeTime - scanInterval) and expiryTime < lastScavengeTime";
|
||||
|
||||
|
||||
//Pass 1: find sessions for which we were last managing node that have just expired since last pass
|
||||
PreparedStatement statement = connection.prepareStatement(_selectBoundedExpiredSessions);
|
||||
long lowerBound = (_lastScavengeTime - _scavengeIntervalMs);
|
||||
long upperBound = _lastScavengeTime;
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug (" Searching for sessions expired between "+lowerBound + " and "+upperBound);
|
||||
LOG.debug (getWorkerName()+"- Pass 1: Searching for sessions expired between "+lowerBound + " and "+upperBound);
|
||||
|
||||
statement.setLong(1, lowerBound);
|
||||
statement.setLong(2, upperBound);
|
||||
statement.setString(1, getWorkerName());
|
||||
statement.setLong(2, lowerBound);
|
||||
statement.setLong(3, upperBound);
|
||||
ResultSet result = statement.executeQuery();
|
||||
while (result.next())
|
||||
{
|
||||
String sessionId = result.getString("sessionId");
|
||||
expiredSessionIds.add(sessionId);
|
||||
if (LOG.isDebugEnabled()) LOG.debug (" Found expired sessionId="+sessionId);
|
||||
if (LOG.isDebugEnabled()) LOG.debug ("Found expired sessionId="+sessionId);
|
||||
}
|
||||
result.close();
|
||||
scavengeSessions(expiredSessionIds, false);
|
||||
|
||||
|
||||
//tell the SessionManagers to expire any sessions with a matching sessionId in memory
|
||||
Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
|
||||
for (int i=0; contexts!=null && i<contexts.length; i++)
|
||||
{
|
||||
|
||||
SessionHandler sessionHandler = (SessionHandler)((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
|
||||
if (sessionHandler != null)
|
||||
{
|
||||
SessionManager manager = sessionHandler.getSessionManager();
|
||||
if (manager != null && manager instanceof JDBCSessionManager)
|
||||
{
|
||||
((JDBCSessionManager)manager).expire(expiredSessionIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//find all sessions that have expired at least a couple of scanIntervals ago and just delete them
|
||||
//Pass 2: find sessions that have expired a while ago for which this node was their last manager
|
||||
PreparedStatement selectExpiredSessions = connection.prepareStatement(_selectExpiredSessions);
|
||||
expiredSessionIds.clear();
|
||||
upperBound = _lastScavengeTime - (2 * _scavengeIntervalMs);
|
||||
if (upperBound > 0)
|
||||
{
|
||||
if (LOG.isDebugEnabled()) LOG.debug(getWorkerName()+"- Pass 2: Searching for sessions expired before "+upperBound);
|
||||
selectExpiredSessions.setLong(1, upperBound);
|
||||
result = selectExpiredSessions.executeQuery();
|
||||
while (result.next())
|
||||
{
|
||||
String sessionId = result.getString("sessionId");
|
||||
String lastNode = result.getString("lastNode");
|
||||
if ((getWorkerName() == null && lastNode == null) || (getWorkerName() != null && getWorkerName().equals(lastNode)))
|
||||
expiredSessionIds.add(sessionId);
|
||||
if (LOG.isDebugEnabled()) LOG.debug ("Found expired sessionId="+sessionId+" last managed by "+getWorkerName());
|
||||
}
|
||||
result.close();
|
||||
scavengeSessions(expiredSessionIds, false);
|
||||
}
|
||||
|
||||
|
||||
//Pass 3:
|
||||
//find all sessions that have expired at least a couple of scanIntervals ago
|
||||
//if we did not succeed in loading them (eg their related context no longer exists, can't be loaded etc) then
|
||||
//they are simply deleted
|
||||
upperBound = _lastScavengeTime - (3 * _scavengeIntervalMs);
|
||||
expiredSessionIds.clear();
|
||||
if (upperBound > 0)
|
||||
{
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Deleting old expired sessions expired before "+upperBound);
|
||||
statement = connection.prepareStatement(_deleteOldExpiredSessions);
|
||||
statement.setLong(1, upperBound);
|
||||
int rows = statement.executeUpdate();
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Deleted "+rows+" rows of old sessions expired before "+upperBound);
|
||||
if (LOG.isDebugEnabled()) LOG.debug(getWorkerName()+"- Pass 3: searching for sessions expired before "+upperBound);
|
||||
selectExpiredSessions.setLong(1, upperBound);
|
||||
result = selectExpiredSessions.executeQuery();
|
||||
while (result.next())
|
||||
{
|
||||
String sessionId = result.getString("sessionId");
|
||||
expiredSessionIds.add(sessionId);
|
||||
if (LOG.isDebugEnabled()) LOG.debug ("Found expired sessionId="+sessionId);
|
||||
}
|
||||
result.close();
|
||||
scavengeSessions(expiredSessionIds, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -921,12 +946,12 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
|
|||
finally
|
||||
{
|
||||
_lastScavengeTime=System.currentTimeMillis();
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Scavenge sweep ended at "+_lastScavengeTime);
|
||||
if (LOG.isDebugEnabled()) LOG.debug(getWorkerName()+"- Scavenge sweep ended at "+_lastScavengeTime);
|
||||
if (connection != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
connection.close();
|
||||
connection.close();
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
|
@ -936,98 +961,133 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get rid of sessions and sessionids from sessions that have already expired
|
||||
* @throws Exception
|
||||
* @param expiredSessionIds
|
||||
*/
|
||||
private void cleanExpiredSessions ()
|
||||
throws Exception
|
||||
{
|
||||
Connection connection = null;
|
||||
List<String> expiredSessionIds = new ArrayList<String>();
|
||||
try
|
||||
{
|
||||
connection = getConnection();
|
||||
connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
|
||||
connection.setAutoCommit(false);
|
||||
|
||||
PreparedStatement statement = connection.prepareStatement(_selectExpiredSessions);
|
||||
long now = System.currentTimeMillis();
|
||||
if (LOG.isDebugEnabled()) LOG.debug ("Searching for sessions expired before {}", now);
|
||||
|
||||
statement.setLong(1, now);
|
||||
ResultSet result = statement.executeQuery();
|
||||
while (result.next())
|
||||
private void scavengeSessions (Set<String> expiredSessionIds, boolean forceDelete)
|
||||
{
|
||||
Set<String> remainingIds = new HashSet<String>(expiredSessionIds);
|
||||
Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
|
||||
for (int i=0; contexts!=null && i<contexts.length; i++)
|
||||
{
|
||||
SessionHandler sessionHandler = (SessionHandler)((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
|
||||
if (sessionHandler != null)
|
||||
{
|
||||
String sessionId = result.getString("sessionId");
|
||||
expiredSessionIds.add(sessionId);
|
||||
if (LOG.isDebugEnabled()) LOG.debug ("Found expired sessionId={}", sessionId);
|
||||
}
|
||||
|
||||
Statement sessionsTableStatement = null;
|
||||
Statement sessionIdsTableStatement = null;
|
||||
|
||||
if (!expiredSessionIds.isEmpty())
|
||||
{
|
||||
sessionsTableStatement = connection.createStatement();
|
||||
sessionsTableStatement.executeUpdate(createCleanExpiredSessionsSql("delete from "+_sessionTable+" where sessionId in ", expiredSessionIds));
|
||||
sessionIdsTableStatement = connection.createStatement();
|
||||
sessionIdsTableStatement.executeUpdate(createCleanExpiredSessionsSql("delete from "+_sessionIdTable+" where id in ", expiredSessionIds));
|
||||
}
|
||||
connection.commit();
|
||||
|
||||
synchronized (_sessionIds)
|
||||
{
|
||||
_sessionIds.removeAll(expiredSessionIds); //in case they were in our local cache of session ids
|
||||
SessionManager manager = sessionHandler.getSessionManager();
|
||||
if (manager != null && manager instanceof JDBCSessionManager)
|
||||
{
|
||||
Set<String> successfullyExpiredIds = ((JDBCSessionManager)manager).expire(expiredSessionIds);
|
||||
if (successfullyExpiredIds != null)
|
||||
remainingIds.removeAll(successfullyExpiredIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (connection != null)
|
||||
connection.rollback();
|
||||
throw e;
|
||||
}
|
||||
finally
|
||||
|
||||
//Any remaining ids are of those sessions that no context removed
|
||||
if (!remainingIds.isEmpty() && forceDelete)
|
||||
{
|
||||
LOG.info("Forcibly deleting unrecoverable expired sessions {}", remainingIds);
|
||||
try
|
||||
{
|
||||
if (connection != null)
|
||||
connection.close();
|
||||
//ensure they aren't in the local list of in-use session ids
|
||||
synchronized (_sessionIds)
|
||||
{
|
||||
_sessionIds.removeAll(remainingIds);
|
||||
}
|
||||
|
||||
cleanExpiredSessionIds(remainingIds);
|
||||
}
|
||||
catch (SQLException e)
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
LOG.warn("Error removing expired session ids", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void cleanExpiredSessionIds (Set<String> expiredIds)
|
||||
throws Exception
|
||||
{
|
||||
if (expiredIds == null || expiredIds.isEmpty())
|
||||
return;
|
||||
|
||||
String[] ids = expiredIds.toArray(new String[expiredIds.size()]);
|
||||
Connection con = null;
|
||||
try
|
||||
{
|
||||
con = getConnection();
|
||||
con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
|
||||
con.setAutoCommit(false);
|
||||
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
int blocksize = _deleteBlockSize;
|
||||
int block = 0;
|
||||
|
||||
while (end < ids.length)
|
||||
{
|
||||
start = block*blocksize;
|
||||
if ((ids.length - start) >= blocksize)
|
||||
end = start + blocksize;
|
||||
else
|
||||
end = ids.length;
|
||||
|
||||
Statement statement = con.createStatement();
|
||||
//take them out of the sessionIds table
|
||||
statement.executeUpdate(fillInClause("delete from "+_sessionIdTable+" where id in ", ids, start, end));
|
||||
//take them out of the sessions table
|
||||
statement.executeUpdate(fillInClause("delete from "+_sessionTable+" where sessionId in ", ids, start, end));
|
||||
block++;
|
||||
}
|
||||
con.commit();
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (con != null)
|
||||
{
|
||||
con.rollback();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (con != null)
|
||||
{
|
||||
con.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param sql
|
||||
* @param connection
|
||||
* @param expiredSessionIds
|
||||
* @param atoms
|
||||
* @throws Exception
|
||||
*/
|
||||
private String createCleanExpiredSessionsSql (String sql,Collection<String> expiredSessionIds)
|
||||
private String fillInClause (String sql, String[] literals, int start, int end)
|
||||
throws Exception
|
||||
{
|
||||
StringBuffer buff = new StringBuffer();
|
||||
buff.append(sql);
|
||||
buff.append("(");
|
||||
Iterator<String> itor = expiredSessionIds.iterator();
|
||||
while (itor.hasNext())
|
||||
for (int i=start; i<end; i++)
|
||||
{
|
||||
buff.append("'"+(itor.next())+"'");
|
||||
if (itor.hasNext())
|
||||
buff.append("'"+(literals[i])+"'");
|
||||
if (i+1<end)
|
||||
buff.append(",");
|
||||
}
|
||||
buff.append(")");
|
||||
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Cleaning expired sessions with: {}", buff);
|
||||
return buff.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void initializeDatabase ()
|
||||
throws Exception
|
||||
{
|
||||
|
|
|
@ -29,10 +29,10 @@ import java.sql.Connection;
|
|||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
|
@ -42,6 +42,7 @@ import javax.servlet.http.HttpSessionListener;
|
|||
|
||||
import org.eclipse.jetty.server.SessionIdManager;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
@ -369,6 +370,7 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
super.timeout();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString ()
|
||||
{
|
||||
|
@ -382,38 +384,6 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* ClassLoadingObjectInputStream
|
||||
*
|
||||
* Used to persist the session attribute map
|
||||
*/
|
||||
protected class ClassLoadingObjectInputStream extends ObjectInputStream
|
||||
{
|
||||
public ClassLoadingObjectInputStream(java.io.InputStream in) throws IOException
|
||||
{
|
||||
super(in);
|
||||
}
|
||||
|
||||
public ClassLoadingObjectInputStream () throws IOException
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> resolveClass (java.io.ObjectStreamClass cl) throws IOException, ClassNotFoundException
|
||||
{
|
||||
try
|
||||
{
|
||||
return Class.forName(cl.getName(), false, Thread.currentThread().getContextClassLoader());
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
return super.resolveClass(cl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the time in seconds which is the interval between
|
||||
* saving the session access time to the database.
|
||||
|
@ -552,7 +522,7 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
session.setLastNode(getSessionIdManager().getWorkerName());
|
||||
_sessions.put(idInCluster, session);
|
||||
|
||||
//update in db: if unable to update, session will be scavenged later
|
||||
//update in db
|
||||
try
|
||||
{
|
||||
updateSessionNode(session);
|
||||
|
@ -567,6 +537,9 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
else
|
||||
{
|
||||
LOG.debug("getSession ({}): Session has expired", idInCluster);
|
||||
//ensure that the session id for the expired session is deleted so that a new session with the
|
||||
//same id cannot be created (because the idInUse() test would succeed)
|
||||
_jdbcSessionIdMgr.removeSession(idInCluster);
|
||||
session=null;
|
||||
}
|
||||
|
||||
|
@ -583,6 +556,7 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
return session;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the number of sessions.
|
||||
|
@ -779,8 +753,8 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
|
||||
synchronized (this)
|
||||
{
|
||||
//take this session out of the map of sessions for this context
|
||||
if (getSession(session.getClusterId()) != null)
|
||||
//take this session out of the map of sessions for this context
|
||||
if (_sessions.containsKey(session.getClusterId()))
|
||||
{
|
||||
removed = true;
|
||||
removeSession(session.getClusterId());
|
||||
|
@ -815,19 +789,20 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
*
|
||||
* @param sessionIds
|
||||
*/
|
||||
protected void expire (List<?> sessionIds)
|
||||
protected Set<String> expire (Set<String> sessionIds)
|
||||
{
|
||||
//don't attempt to scavenge if we are shutting down
|
||||
if (isStopping() || isStopped())
|
||||
return;
|
||||
return null;
|
||||
|
||||
//Remove any sessions we already have in memory that match the ids
|
||||
|
||||
Thread thread=Thread.currentThread();
|
||||
ClassLoader old_loader=thread.getContextClassLoader();
|
||||
ListIterator<?> itor = sessionIds.listIterator();
|
||||
|
||||
|
||||
Set<String> successfullyExpiredIds = new HashSet<String>();
|
||||
try
|
||||
{
|
||||
Iterator<?> itor = sessionIds.iterator();
|
||||
while (itor.hasNext())
|
||||
{
|
||||
String sessionId = (String)itor.next();
|
||||
|
@ -835,29 +810,49 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
LOG.debug("Expiring session id "+sessionId);
|
||||
|
||||
Session session = (Session)_sessions.get(sessionId);
|
||||
|
||||
//if session is not in our memory, then fetch from db so we can call the usual listeners on it
|
||||
if (session == null)
|
||||
{
|
||||
if (LOG.isDebugEnabled())LOG.debug("Force loading session id "+sessionId);
|
||||
session = loadSession(sessionId, canonicalize(_context.getContextPath()), getVirtualHost(_context));
|
||||
if (session != null)
|
||||
{
|
||||
//loaded an expired session last managed on this node for this context, add it to the list so we can
|
||||
//treat it like a normal expired session
|
||||
synchronized (this)
|
||||
{
|
||||
_sessions.put(session.getClusterId(), session);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Unrecognized session id="+sessionId);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (session != null)
|
||||
{
|
||||
session.timeout();
|
||||
itor.remove();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Unrecognized session id="+sessionId);
|
||||
successfullyExpiredIds.add(session.getClusterId());
|
||||
}
|
||||
}
|
||||
return successfullyExpiredIds;
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOG.warn("Problem expiring sessions", t);
|
||||
return successfullyExpiredIds;
|
||||
}
|
||||
finally
|
||||
{
|
||||
thread.setContextClassLoader(old_loader);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Load a session from the database
|
||||
* @param id
|
||||
|
@ -913,6 +908,9 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("LOADED session "+session);
|
||||
}
|
||||
else
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Failed to load session "+id);
|
||||
_reference.set(session);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
@ -39,6 +39,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
||||
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
|
||||
import org.eclipse.jetty.toolchain.test.annotation.Stress;
|
||||
import org.eclipse.jetty.toolchain.test.PropertyFlag;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
@ -95,7 +96,14 @@ public class AsyncStressTest
|
|||
@Stress("High connection count")
|
||||
public void testAsync() throws Throwable
|
||||
{
|
||||
doConnections(1600,240);
|
||||
if (PropertyFlag.isEnabled("test.stress"))
|
||||
{
|
||||
doConnections(1600,240);
|
||||
}
|
||||
else
|
||||
{
|
||||
doConnections(80,80);
|
||||
}
|
||||
}
|
||||
|
||||
private void doConnections(int connections,final int loops) throws Throwable
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.server;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.junit.Test;
|
||||
|
||||
public class HalfCloseRaceTest
|
||||
{
|
||||
@Test
|
||||
public void testHalfCloseRace() throws Exception
|
||||
{
|
||||
Server server = new Server();
|
||||
ServerConnector connector = new ServerConnector(server);
|
||||
connector.setPort(0);
|
||||
connector.setIdleTimeout(500);
|
||||
server.addConnector(connector);
|
||||
TestHandler handler = new TestHandler();
|
||||
server.setHandler(handler);
|
||||
|
||||
server.start();
|
||||
|
||||
Socket client = new Socket("localhost",connector.getLocalPort());
|
||||
|
||||
int in = client.getInputStream().read();
|
||||
assertEquals(-1,in);
|
||||
|
||||
client.getOutputStream().write("GET / HTTP/1.0\r\n\r\n".getBytes());
|
||||
|
||||
Thread.sleep(200);
|
||||
assertEquals(0,handler.getHandled());
|
||||
|
||||
}
|
||||
|
||||
public static class TestHandler extends AbstractHandler
|
||||
{
|
||||
transient int handled;
|
||||
|
||||
public TestHandler()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
handled++;
|
||||
response.setContentType("text/html;charset=utf-8");
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
response.getWriter().println("<h1>Test</h1>");
|
||||
}
|
||||
|
||||
public int getHandled()
|
||||
{
|
||||
return handled;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -49,8 +49,13 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
|
|||
@Parameterized.Parameters
|
||||
public static Collection<Object[]> data()
|
||||
{
|
||||
Object[][] data = new Object[][]{{HttpVersion.HTTP_1_0.asString(), true}, {HttpVersion.HTTP_1_0.asString(),
|
||||
false}, {HttpVersion.HTTP_1_1.asString(), true}, {HttpVersion.HTTP_1_1.asString(), false}};
|
||||
Object[][] data = new Object[][]
|
||||
{
|
||||
{HttpVersion.HTTP_1_0.asString(), true},
|
||||
{HttpVersion.HTTP_1_1.asString(), true},
|
||||
{HttpVersion.HTTP_1_0.asString(), false},
|
||||
{HttpVersion.HTTP_1_1.asString(), false}
|
||||
};
|
||||
return Arrays.asList(data);
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
||||
import org.eclipse.jetty.toolchain.test.PropertyFlag;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
@ -42,7 +43,7 @@ import org.junit.Before;
|
|||
public class HttpServerTestFixture
|
||||
{ // Useful constants
|
||||
protected static final long PAUSE=10L;
|
||||
protected static final int LOOPS=50;
|
||||
protected static final int LOOPS=PropertyFlag.isEnabled("test.stress")?250:50;
|
||||
|
||||
protected Server _server;
|
||||
protected URI _serverURI;
|
||||
|
|
|
@ -234,27 +234,19 @@ public class PartialRFC2616Test
|
|||
}
|
||||
|
||||
@Test
|
||||
public void test3_9()
|
||||
public void test3_9() throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
HttpFields fields=new HttpFields();
|
||||
HttpFields fields=new HttpFields();
|
||||
|
||||
fields.put("Q","bbb;q=0.5,aaa,ccc;q=0.002,d;q=0,e;q=0.0001,ddd;q=0.001,aa2,abb;q=0.7");
|
||||
Enumeration<String> qualities=fields.getValues("Q",", \t");
|
||||
List<String> list=HttpFields.qualityList(qualities);
|
||||
assertEquals("Quality parameters","aaa",HttpFields.valueParameters(list.get(0),null));
|
||||
assertEquals("Quality parameters","aa2",HttpFields.valueParameters(list.get(1),null));
|
||||
assertEquals("Quality parameters","abb",HttpFields.valueParameters(list.get(2),null));
|
||||
assertEquals("Quality parameters","bbb",HttpFields.valueParameters(list.get(3),null));
|
||||
assertEquals("Quality parameters","ccc",HttpFields.valueParameters(list.get(4),null));
|
||||
assertEquals("Quality parameters","ddd",HttpFields.valueParameters(list.get(5),null));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
assertTrue(false);
|
||||
}
|
||||
fields.put("Q","bbb;q=0.5,aaa,ccc;q=0.002,d;q=0,e;q=0.0001,ddd;q=0.001,aa2,abb;q=0.7");
|
||||
Enumeration<String> qualities=fields.getValues("Q",", \t");
|
||||
List<String> list=HttpFields.qualityList(qualities);
|
||||
assertEquals("Quality parameters","aaa",HttpFields.valueParameters(list.get(0),null));
|
||||
assertEquals("Quality parameters","aa2",HttpFields.valueParameters(list.get(1),null));
|
||||
assertEquals("Quality parameters","abb",HttpFields.valueParameters(list.get(2),null));
|
||||
assertEquals("Quality parameters","bbb",HttpFields.valueParameters(list.get(3),null));
|
||||
assertEquals("Quality parameters","ccc",HttpFields.valueParameters(list.get(4),null));
|
||||
assertEquals("Quality parameters","ddd",HttpFields.valueParameters(list.get(5),null));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.server;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.LineNumberReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* ShutdownMonitorTest
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class ShutdownMonitorTest
|
||||
{
|
||||
|
||||
|
||||
@Test
|
||||
public void testShutdown ()
|
||||
throws Exception
|
||||
{
|
||||
|
||||
//test port and key assignment
|
||||
ShutdownMonitor.getInstance().setPort(0);
|
||||
ShutdownMonitor.getInstance().setExitVm(false);
|
||||
ShutdownMonitor.getInstance().start();
|
||||
String key = ShutdownMonitor.getInstance().getKey();
|
||||
int port = ShutdownMonitor.getInstance().getPort();
|
||||
|
||||
//try starting a 2nd time (should be ignored)
|
||||
ShutdownMonitor.getInstance().start();
|
||||
|
||||
|
||||
stop(port,key,true);
|
||||
assertTrue(!ShutdownMonitor.getInstance().isAlive());
|
||||
|
||||
//should be able to change port and key because it is stopped
|
||||
ShutdownMonitor.getInstance().setPort(0);
|
||||
ShutdownMonitor.getInstance().setKey("foo");
|
||||
ShutdownMonitor.getInstance().start();
|
||||
|
||||
key = ShutdownMonitor.getInstance().getKey();
|
||||
port = ShutdownMonitor.getInstance().getPort();
|
||||
assertTrue(ShutdownMonitor.getInstance().isAlive());
|
||||
|
||||
stop(port,key,true);
|
||||
assertTrue(!ShutdownMonitor.getInstance().isAlive());
|
||||
}
|
||||
|
||||
|
||||
public void stop (int port, String key, boolean check)
|
||||
throws Exception
|
||||
{
|
||||
Socket s = null;
|
||||
|
||||
try
|
||||
{
|
||||
//send stop command
|
||||
s = new Socket(InetAddress.getByName("127.0.0.1"),port);
|
||||
|
||||
OutputStream out = s.getOutputStream();
|
||||
out.write((key + "\r\nstop\r\n").getBytes());
|
||||
out.flush();
|
||||
|
||||
if (check)
|
||||
{
|
||||
//wait a little
|
||||
Thread.currentThread().sleep(600);
|
||||
|
||||
//check for stop confirmation
|
||||
LineNumberReader lin = new LineNumberReader(new InputStreamReader(s.getInputStream()));
|
||||
String response;
|
||||
if ((response = lin.readLine()) != null)
|
||||
{
|
||||
assertEquals("Stopped", response);
|
||||
}
|
||||
else
|
||||
throw new IllegalStateException("No stop confirmation");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (s != null) s.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -37,6 +37,7 @@ import org.eclipse.jetty.server.handler.HandlerWrapper;
|
|||
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
|
||||
import org.eclipse.jetty.toolchain.test.OS;
|
||||
import org.eclipse.jetty.toolchain.test.annotation.Stress;
|
||||
import org.eclipse.jetty.toolchain.test.PropertyFlag;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
@ -130,35 +131,37 @@ public class StressTest
|
|||
}
|
||||
|
||||
@Test
|
||||
@Stress("Much threading")
|
||||
public void testNonPersistent() throws Throwable
|
||||
{
|
||||
// TODO needs to be further investigated
|
||||
assumeTrue(!OS.IS_OSX);
|
||||
assumeTrue(!OS.IS_OSX || PropertyFlag.isEnabled("test.stress"));
|
||||
|
||||
doThreads(10,10,false);
|
||||
Thread.sleep(1000);
|
||||
doThreads(20,20,false);
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,10,false);
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,200,false);
|
||||
if (PropertyFlag.isEnabled("test.stress"))
|
||||
{
|
||||
doThreads(20,20,false);
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,10,false);
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,200,false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Stress("Much threading")
|
||||
public void testPersistent() throws Throwable
|
||||
{
|
||||
// TODO needs to be further investigated
|
||||
assumeTrue(!OS.IS_OSX);
|
||||
assumeTrue(!OS.IS_OSX || PropertyFlag.isEnabled("test.stress"));
|
||||
|
||||
doThreads(10,10,true);
|
||||
Thread.sleep(1000);
|
||||
doThreads(40,40,true);
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,10,true);
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,200,true);
|
||||
if (PropertyFlag.isEnabled("test.stress"))
|
||||
{
|
||||
doThreads(40,40,true);
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,10,true);
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,200,true);
|
||||
}
|
||||
}
|
||||
|
||||
private void doThreads(int threadCount, final int loops, final boolean persistent) throws Throwable
|
||||
|
|
|
@ -22,7 +22,7 @@ import java.io.File;
|
|||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eclipse.jetty.server.SessionManager;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.StdErrLog;
|
||||
|
@ -71,7 +71,7 @@ public class HashSessionManagerTest
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test
|
||||
public void testValidSessionIdRemoval() throws Exception
|
||||
{
|
||||
final HashSessionManager manager = new HashSessionManager();
|
||||
|
@ -90,4 +90,49 @@ public class HashSessionManagerTest
|
|||
Assert.assertTrue("File shouldn't exist!", !new File(testDir,"validFile.session").exists());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHashSession() throws Exception
|
||||
{
|
||||
File testDir = MavenTestingUtils.getTargetTestingDir("saved");
|
||||
testDir.mkdirs();
|
||||
HashSessionManager manager = new HashSessionManager();
|
||||
manager.setStoreDirectory(testDir);
|
||||
manager.setMaxInactiveInterval(5);
|
||||
Assert.assertTrue(testDir.exists());
|
||||
Assert.assertTrue(testDir.canWrite());
|
||||
|
||||
HashSessionIdManager idManager = new HashSessionIdManager();
|
||||
idManager.setWorkerName("foo");
|
||||
manager.setSessionIdManager(idManager);
|
||||
|
||||
idManager.start();
|
||||
manager.start();
|
||||
|
||||
HashedSession session = (HashedSession)manager.newHttpSession(new Request(null, null));
|
||||
String sessionId = session.getId();
|
||||
|
||||
session.setAttribute("one", new Integer(1));
|
||||
session.setAttribute("two", new Integer(2));
|
||||
|
||||
//stop will persist sessions
|
||||
idManager.stop();
|
||||
manager.setMaxInactiveInterval(30); //change max inactive interval for *new* sessions
|
||||
manager.stop();
|
||||
|
||||
Assert.assertTrue("File should exist!", new File(testDir, session.getId()).exists());
|
||||
|
||||
//start will restore sessions
|
||||
idManager.start();
|
||||
manager.start();
|
||||
|
||||
HashedSession restoredSession = (HashedSession)manager.getSession(sessionId);
|
||||
Assert.assertNotNull(restoredSession);
|
||||
|
||||
Object o = restoredSession.getAttribute("one");
|
||||
Assert.assertNotNull(o);
|
||||
|
||||
Assert.assertEquals(1, ((Integer)o).intValue());
|
||||
Assert.assertEquals(5, restoredSession.getMaxInactiveInterval());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,6 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
|
@ -866,7 +865,62 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
// Close the raw socket, this generates a truncation attack
|
||||
proxy.flushToServer(null);
|
||||
|
||||
// Expect raw close from server
|
||||
// Expect alert + raw close from server
|
||||
record = proxy.readFromServer();
|
||||
Assert.assertEquals(TLSRecord.Type.ALERT, record.getType());
|
||||
record = proxy.readFromServer();
|
||||
Assert.assertNull(String.valueOf(record), record);
|
||||
proxy.flushToClient(record);
|
||||
|
||||
// Check that we did not spin
|
||||
TimeUnit.MILLISECONDS.sleep(500);
|
||||
Assert.assertThat(sslFills.get(), Matchers.lessThan(20));
|
||||
Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20));
|
||||
Assert.assertThat(httpParses.get(), Matchers.lessThan(20));
|
||||
|
||||
client.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestWithImmediateRawClose() throws Exception
|
||||
{
|
||||
final SSLSocket client = newClient();
|
||||
|
||||
SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
|
||||
client.startHandshake();
|
||||
Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
|
||||
|
||||
Future<Object> request = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
{
|
||||
OutputStream clientOutput = client.getOutputStream();
|
||||
clientOutput.write(("" +
|
||||
"GET / HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"\r\n").getBytes("UTF-8"));
|
||||
clientOutput.flush();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Application data
|
||||
TLSRecord record = proxy.readFromClient();
|
||||
Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType());
|
||||
proxy.flushToServer(record, 0);
|
||||
// Close the raw socket, this generates a truncation attack
|
||||
proxy.flushToServer(null);
|
||||
Assert.assertNull(request.get(5, TimeUnit.SECONDS));
|
||||
|
||||
// Application data
|
||||
record = proxy.readFromServer();
|
||||
Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType());
|
||||
proxy.flushToClient(record);
|
||||
|
||||
// Expect alert + raw close from server
|
||||
record = proxy.readFromServer();
|
||||
Assert.assertEquals(TLSRecord.Type.ALERT, record.getType());
|
||||
record = proxy.readFromServer();
|
||||
Assert.assertNull(String.valueOf(record), record);
|
||||
proxy.flushToClient(record);
|
||||
|
@ -1634,9 +1688,6 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20));
|
||||
Assert.assertThat(httpParses.get(), Matchers.lessThan(50));
|
||||
|
||||
//System.err.println(((Dumpable)server.getConnectors()[0]).dump());
|
||||
Assert.assertThat(((Dumpable)server.getConnectors()[0]).dump(), Matchers.containsString("SCEP@"));
|
||||
|
||||
completeClose(client);
|
||||
|
||||
TimeUnit.MILLISECONDS.sleep(200);
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.servlet;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class RequestHeadersTest
|
||||
{
|
||||
@SuppressWarnings("serial")
|
||||
private static class RequestHeaderServlet extends HttpServlet
|
||||
{
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
|
||||
{
|
||||
resp.setContentType("text/plain");
|
||||
PrintWriter out = resp.getWriter();
|
||||
out.printf("X-Camel-Type = %s",req.getHeader("X-Camel-Type"));
|
||||
}
|
||||
}
|
||||
|
||||
private static Server server;
|
||||
private static ServerConnector connector;
|
||||
private static URI serverUri;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
// Configure Server
|
||||
server = new Server();
|
||||
connector = new ServerConnector(server);
|
||||
server.addConnector(connector);
|
||||
|
||||
ServletContextHandler context = new ServletContextHandler();
|
||||
context.setContextPath("/");
|
||||
server.setHandler(context);
|
||||
|
||||
// Serve capture servlet
|
||||
context.addServlet(new ServletHolder(new RequestHeaderServlet()),"/*");
|
||||
|
||||
// Start Server
|
||||
server.start();
|
||||
|
||||
String host = connector.getHost();
|
||||
if (host == null)
|
||||
{
|
||||
host = "localhost";
|
||||
}
|
||||
int port = connector.getLocalPort();
|
||||
serverUri = new URI(String.format("http://%s:%d/",host,port));
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer()
|
||||
{
|
||||
try
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLowercaseHeader() throws IOException
|
||||
{
|
||||
HttpURLConnection http = null;
|
||||
try
|
||||
{
|
||||
http = (HttpURLConnection)serverUri.toURL().openConnection();
|
||||
// Set header in all lowercase
|
||||
http.setRequestProperty("x-camel-type","bactrian");
|
||||
|
||||
try (InputStream in = http.getInputStream())
|
||||
{
|
||||
String resp = IO.toString(in, "UTF-8");
|
||||
Assert.assertThat("Response", resp, is("X-Camel-Type = bactrian"));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (http != null)
|
||||
{
|
||||
http.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -130,8 +130,11 @@ public class DoSFilter implements Filter
|
|||
{
|
||||
private static final Logger LOG = Log.getLogger(DoSFilter.class);
|
||||
|
||||
private static final Pattern IP_PATTERN = Pattern.compile("(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})");
|
||||
private static final Pattern CIDR_PATTERN = Pattern.compile(IP_PATTERN + "/(\\d{1,2})");
|
||||
private static final String IPv4_GROUP = "(\\d{1,3})";
|
||||
private static final Pattern IPv4_PATTERN = Pattern.compile(IPv4_GROUP+"\\."+IPv4_GROUP+"\\."+IPv4_GROUP+"\\."+IPv4_GROUP);
|
||||
private static final String IPv6_GROUP = "(\\p{XDigit}{1,4})";
|
||||
private static final Pattern IPv6_PATTERN = Pattern.compile(IPv6_GROUP+":"+IPv6_GROUP+":"+IPv6_GROUP+":"+IPv6_GROUP+":"+IPv6_GROUP+":"+IPv6_GROUP+":"+IPv6_GROUP+":"+IPv6_GROUP);
|
||||
private static final Pattern CIDR_PATTERN = Pattern.compile("([^/]+)/(\\d+)");
|
||||
|
||||
private static final String __TRACKER = "DoSFilter.Tracker";
|
||||
private static final String __THROTTLED = "DoSFilter.Throttled";
|
||||
|
@ -625,31 +628,94 @@ public class DoSFilter implements Filter
|
|||
return false;
|
||||
}
|
||||
|
||||
protected boolean subnetMatch(String subnetAddress, String candidate)
|
||||
protected boolean subnetMatch(String subnetAddress, String address)
|
||||
{
|
||||
Matcher matcher = CIDR_PATTERN.matcher(subnetAddress);
|
||||
int subnet = intFromAddress(matcher);
|
||||
int prefix = Integer.parseInt(matcher.group(5));
|
||||
// Sets the most significant prefix bits to 1
|
||||
// If prefix == 8 => 11111111_00000000_00000000_00000000
|
||||
int mask = ~((1 << (32 - prefix)) - 1);
|
||||
int ip = intFromAddress(IP_PATTERN.matcher(candidate));
|
||||
return (ip & mask) == (subnet & mask);
|
||||
Matcher cidrMatcher = CIDR_PATTERN.matcher(subnetAddress);
|
||||
if (!cidrMatcher.matches())
|
||||
return false;
|
||||
|
||||
String subnet = cidrMatcher.group(1);
|
||||
int prefix;
|
||||
try
|
||||
{
|
||||
prefix = Integer.parseInt(cidrMatcher.group(2));
|
||||
}
|
||||
catch (NumberFormatException x)
|
||||
{
|
||||
LOG.info("Ignoring malformed CIDR address {}", subnetAddress);
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] subnetBytes = addressToBytes(subnet);
|
||||
if (subnetBytes == null)
|
||||
{
|
||||
LOG.info("Ignoring malformed CIDR address {}", subnetAddress);
|
||||
return false;
|
||||
}
|
||||
byte[] addressBytes = addressToBytes(address);
|
||||
if (addressBytes == null)
|
||||
{
|
||||
LOG.info("Ignoring malformed remote address {}", address);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Comparing IPv4 with IPv6 ?
|
||||
int length = subnetBytes.length;
|
||||
if (length != addressBytes.length)
|
||||
return false;
|
||||
|
||||
byte[] mask = prefixToBytes(prefix, length);
|
||||
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
if ((subnetBytes[i] & mask[i]) != (addressBytes[i] & mask[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private int intFromAddress(Matcher matcher)
|
||||
private byte[] addressToBytes(String address)
|
||||
{
|
||||
int result = 0;
|
||||
if (matcher.matches())
|
||||
Matcher ipv4Matcher = IPv4_PATTERN.matcher(address);
|
||||
if (ipv4Matcher.matches())
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
int b = Integer.parseInt(matcher.group(i + 1));
|
||||
result |= b << 8 * (3 - i);
|
||||
}
|
||||
byte[] result = new byte[4];
|
||||
for (int i = 0; i < result.length; ++i)
|
||||
result[i] = Integer.valueOf(ipv4Matcher.group(i + 1)).byteValue();
|
||||
return result;
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
else
|
||||
{
|
||||
Matcher ipv6Matcher = IPv6_PATTERN.matcher(address);
|
||||
if (ipv6Matcher.matches())
|
||||
{
|
||||
byte[] result = new byte[16];
|
||||
for (int i = 0; i < result.length; i += 2)
|
||||
{
|
||||
int word = Integer.valueOf(ipv6Matcher.group(i / 2 + 1), 16);
|
||||
result[i] = (byte)((word & 0xFF00) >>> 8);
|
||||
result[i + 1] = (byte)(word & 0xFF);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private byte[] prefixToBytes(int prefix, int length)
|
||||
{
|
||||
byte[] result = new byte[length];
|
||||
int index = 0;
|
||||
while (prefix / 8 > 0)
|
||||
{
|
||||
result[index] = -1;
|
||||
prefix -= 8;
|
||||
++index;
|
||||
}
|
||||
// Sets the _prefix_ most significant bits to 1
|
||||
result[index] = (byte)~((1 << (8 - prefix)) - 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void destroy()
|
||||
|
@ -980,14 +1046,7 @@ public class DoSFilter implements Filter
|
|||
private boolean addWhitelistAddress(List<String> list, String address)
|
||||
{
|
||||
address = address.trim();
|
||||
if (address.length() > 0)
|
||||
{
|
||||
if (CIDR_PATTERN.matcher(address).matches() || IP_PATTERN.matcher(address).matches())
|
||||
return list.add(address);
|
||||
else
|
||||
LOG.warn("Ignoring malformed whitelist IP address {}", address);
|
||||
}
|
||||
return false;
|
||||
return address.length() > 0 && list.add(address);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -80,12 +80,17 @@ public class DoSFilterTest extends AbstractDoSFilterTest
|
|||
List<String> whitelist = new ArrayList<String>();
|
||||
whitelist.add("192.168.0.1");
|
||||
whitelist.add("10.0.0.0/8");
|
||||
whitelist.add("4d8:0:a:1234:ABc:1F:b18:17");
|
||||
whitelist.add("4d8:0:a:1234:ABc:1F:0:0/96");
|
||||
Assert.assertTrue(filter.checkWhitelist(whitelist, "192.168.0.1"));
|
||||
Assert.assertFalse(filter.checkWhitelist(whitelist, "192.168.0.2"));
|
||||
Assert.assertFalse(filter.checkWhitelist(whitelist, "11.12.13.14"));
|
||||
Assert.assertTrue(filter.checkWhitelist(whitelist, "10.11.12.13"));
|
||||
Assert.assertTrue(filter.checkWhitelist(whitelist, "10.0.0.0"));
|
||||
Assert.assertFalse(filter.checkWhitelist(whitelist, "0.0.0.0"));
|
||||
Assert.assertTrue(filter.checkWhitelist(whitelist, "4d8:0:a:1234:ABc:1F:b18:17"));
|
||||
Assert.assertTrue(filter.checkWhitelist(whitelist, "4d8:0:a:1234:ABc:1F:b18:0"));
|
||||
Assert.assertFalse(filter.checkWhitelist(whitelist, "4d8:0:a:1234:ABc:1D:0:0"));
|
||||
}
|
||||
|
||||
private boolean hitRateTracker(DoSFilter doSFilter, int sleep) throws InterruptedException
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue