Merge remote-tracking branch 'origin/master' into servlet-3.1-api

Conflicts:
	jetty-jsp/pom.xml
This commit is contained in:
Jan Bartel 2013-04-04 13:31:33 +11:00
commit 7523c5234c
143 changed files with 10580 additions and 1511 deletions

4
.travis.yml Normal file
View File

@ -0,0 +1,4 @@
language: java
jdk:
- openjdk7
- oraclejdk7

View File

@ -1,5 +1,6 @@
This is a source checkout of the Jetty webserver.
To build, use:
mvn clean install

View File

@ -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();

View File

@ -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);

View File

@ -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;
}
});
}
}
}

View File

@ -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();
}
}
}
/**

View File

@ -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();

View File

@ -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;
}
}
}

View File

@ -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>

View File

@ -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);

View File

@ -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;

View File

@ -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();

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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)}.

View File

@ -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.

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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);

View File

@ -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()

View File

@ -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;

View File

@ -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

View File

@ -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());
}
}

View File

@ -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
{

View File

@ -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>

View File

@ -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

View File

@ -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>

View File

@ -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/
#===========================================================

View File

@ -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);

View File

@ -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)

View File

@ -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)
{

View File

@ -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

View File

@ -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
{

View File

@ -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

View File

@ -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>

View File

@ -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)
{

13
jetty-monitor/README.txt Normal file
View File

@ -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.

145
jetty-monitor/pom.xml Normal file
View 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>

View File

@ -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>

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);
}
}
});
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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));
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -115,7 +115,7 @@ public class OSGiWebInfConfiguration extends WebInfConfiguration
for (Resource r:matchingResources)
{
context.getMetaData().addContainerJar(r);
context.getMetaData().addContainerResource(r);
}
}

View File

@ -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);

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
/* ------------------------------------------------------------ */

View File

@ -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());

View File

@ -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()
{

View File

@ -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();
}
}

View File

@ -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);
}
}
}
}

View File

@ -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());
}
/* ------------------------------------------------------------ */

View File

@ -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
{

View File

@ -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)

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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

View File

@ -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();
}
}
}

View File

@ -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

View File

@ -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());
}
}

View File

@ -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);

View File

@ -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();
}
}
}
}

View File

@ -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);
}
/**

View File

@ -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