jetty-9 merged jetty-8

Merge remote-tracking branch 'origin/jetty-8' into jetty-9

Conflicts:
	jetty-security/src/main/java/org/eclipse/jetty/security/authentication/FormAuthenticator.java
	jetty-server/src/main/java/org/eclipse/jetty/server/AbstractHttpConnection.java
	jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java
	jetty-util/src/main/java/org/eclipse/jetty/util/log/StdErrLog.java
	jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarFileResource.java
	jetty-util/src/test/java/org/eclipse/jetty/util/log/StdErrLogTest.java
This commit is contained in:
Greg Wilkins 2012-08-17 23:35:42 +10:00
commit 086e74bed8
28 changed files with 801 additions and 100 deletions

15
header-template.txt Normal file
View File

@ -0,0 +1,15 @@
========================================================================
Copyright (c) ${copyright-range} 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.
========================================================================

View File

@ -587,6 +587,55 @@ public class HttpURI
return new String(_raw,_path,length,_charset);
return new String(bytes,0,n,_charset);
}
public String getDecodedPath(String encoding)
{
if (_path==_param)
return null;
int length = _param-_path;
byte[] bytes=null;
int n=0;
for (int i=_path;i<_param;i++)
{
byte b = _raw[i];
if (b=='%')
{
if ((i+2)>=_param)
throw new IllegalArgumentException("Bad % encoding: "+this);
b=(byte)(0xff&TypeUtil.parseInt(_raw,i+1,2,16));
i+=2;
}
else if (bytes==null)
{
n++;
continue;
}
if (bytes==null)
{
bytes=new byte[length];
System.arraycopy(_raw,_path,bytes,0,n);
}
bytes[n++]=b;
}
if (bytes==null)
return StringUtil.toString(_raw,_path,_param-_path,encoding);
return StringUtil.toString(bytes,0,n,encoding);
}
public String getPathAndParam()
{

View File

@ -23,8 +23,11 @@ import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import org.eclipse.jetty.security.authentication.DeferredAuthentication;
import org.eclipse.jetty.server.AbstractHttpConnection;
import org.eclipse.jetty.server.Authentication;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
@ -33,6 +36,7 @@ import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandler.Context;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.server.session.AbstractSessionManager;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -285,6 +289,32 @@ public abstract class SecurityHandler extends HandlerWrapper implements Authenti
getInitParameter(name)==null)
setInitParameter(name,context.getInitParameter(name));
}
//register a session listener to handle securing sessions when authentication is performed
context.getContextHandler().addEventListener(new HttpSessionListener()
{
public void sessionDestroyed(HttpSessionEvent se)
{
}
public void sessionCreated(HttpSessionEvent se)
{
//if current request is authenticated, then as we have just created the session, mark it as secure, as it has not yet been returned to a user
AbstractHttpConnection connection = AbstractHttpConnection.getCurrentConnection();
if (connection == null)
return;
Request request = connection.getRequest();
if (request == null)
return;
if (request.isSecure())
{
se.getSession().setAttribute(AbstractSessionManager.SESSION_KNOWN_ONLY_TO_AUTHENTICATED, Boolean.TRUE);
}
}
});
}
// complicated resolution of login and identity service to handle

View File

@ -83,7 +83,7 @@ public class BasicAuthenticator extends LoginAuthenticator
UserIdentity user = _loginService.login(username,password);
if (user!=null)
{
renewSessionOnAuthentication(request,response);
renewSession(request,response);
return new UserAuthentication(getAuthMethod(),user);
}
}

View File

@ -119,7 +119,7 @@ public class ClientCertAuthenticator extends LoginAuthenticator
UserIdentity user = _loginService.login(username,credential);
if (user!=null)
{
renewSessionOnAuthentication(request,response);
renewSession(request,response);
return new UserAuthentication(getAuthMethod(),user);
}
}

View File

@ -183,7 +183,7 @@ public class DigestAuthenticator extends LoginAuthenticator
UserIdentity user = _loginService.login(digest.username,digest);
if (user!=null)
{
renewSessionOnAuthentication(request,response);
renewSession(request,response);
return new UserAuthentication(getAuthMethod(),user);
}
}

View File

@ -189,8 +189,8 @@ public class FormAuthenticator extends LoginAuthenticator
if (!mandatory)
return _deferred;
if (isLoginOrErrorPage(URIUtil.addPaths(request.getServletPath(),request.getPathInfo())))
return Authentication.NOT_CHECKED;
if (isLoginOrErrorPage(URIUtil.addPaths(request.getServletPath(),request.getPathInfo())) &&!DeferredAuthentication.isDeferred(response))
return _deferred;
HttpSession session = request.getSession(true);
@ -205,7 +205,7 @@ public class FormAuthenticator extends LoginAuthenticator
UserIdentity user = _loginService.login(username,password);
if (user!=null)
{
session=renewSessionOnAuthentication(request,response);
session=renewSession(request,response);
// Redirect to original request
String nuri;

View File

@ -13,10 +13,6 @@
package org.eclipse.jetty.security.authentication;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@ -24,10 +20,10 @@ import javax.servlet.http.HttpSession;
import org.eclipse.jetty.security.Authenticator;
import org.eclipse.jetty.security.IdentityService;
import org.eclipse.jetty.security.LoginService;
import org.eclipse.jetty.server.session.AbstractSessionManager;
public abstract class LoginAuthenticator implements Authenticator
{
public final static String SESSION_SECURED="org.eclipse.jetty.security.secured";
protected final DeferredAuthentication _deferred=new DeferredAuthentication(this);
protected LoginService _loginService;
protected IdentityService _identityService;
@ -53,34 +49,29 @@ public abstract class LoginAuthenticator implements Authenticator
return _loginService;
}
/* ------------------------------------------------------------ */
/** Change the session when the request is authenticated for the first time
/** Change the session id.
* The session is changed to a new instance with a new ID if and only if:<ul>
* <li>A session exists.
* <li>The {@link AuthConfiguration#isSessionRenewedOnAuthentication()} returns true.
* <li>The session ID has been given to unauthenticated responses
* </ul>
* @param request
* @param response
* @return The new session.
*/
protected HttpSession renewSessionOnAuthentication(HttpServletRequest request, HttpServletResponse response)
protected HttpSession renewSession(HttpServletRequest request, HttpServletResponse response)
{
HttpSession httpSession = request.getSession(false);
if (_renewSession && httpSession!=null && httpSession.getAttribute(SESSION_SECURED)==null)
//if we should renew sessions, and there is an existing session that may have been seen by non-authenticated users
//(indicated by SESSION_SECURED not being set on the session) then we should change id
if (_renewSession && httpSession!=null && httpSession.getAttribute(AbstractSessionManager.SESSION_KNOWN_ONLY_TO_AUTHENTICATED)!=Boolean.TRUE)
{
synchronized (this)
{
Map<String,Object> attributes = new HashMap<String, Object>();
for (Enumeration<String> e=httpSession.getAttributeNames();e.hasMoreElements();)
{
String name=e.nextElement();
attributes.put(name,httpSession.getAttribute(name));
httpSession.removeAttribute(name);
}
httpSession.invalidate();
httpSession = request.getSession(true);
httpSession.setAttribute(SESSION_SECURED,Boolean.TRUE);
for (Map.Entry<String, Object> entry: attributes.entrySet())
httpSession.setAttribute(entry.getKey(),entry.getValue());
httpSession = AbstractSessionManager.renewSession(request, httpSession,true);
}
}
return httpSession;
}
}

View File

@ -29,6 +29,7 @@ import org.eclipse.jetty.security.SecurityHandler;
import org.eclipse.jetty.server.Authentication;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.server.UserIdentity.Scope;
import org.eclipse.jetty.server.session.AbstractSessionManager;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -102,7 +103,7 @@ public class SessionAuthentication implements Authentication.User, Serializable,
if (security!=null)
security.logout(this);
if (_session!=null)
_session.removeAttribute(LoginAuthenticator.SESSION_SECURED);
_session.removeAttribute(AbstractSessionManager.SESSION_KNOWN_ONLY_TO_AUTHENTICATED);
}
@Override

View File

@ -37,6 +37,7 @@ import org.eclipse.jetty.util.ByteArrayISO8859Writer;
public class ErrorHandler extends AbstractHandler
{
boolean _showStacks=true;
boolean _showMessageInTitle=true;
String _cacheControl="must-revalidate,no-cache,no-store";
/* ------------------------------------------------------------ */
@ -86,12 +87,16 @@ public class ErrorHandler extends AbstractHandler
/* ------------------------------------------------------------ */
protected void writeErrorPageHead(HttpServletRequest request, Writer writer, int code, String message)
throws IOException
{
{
writer.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\"/>\n");
writer.write("<title>Error ");
writer.write(Integer.toString(code));
writer.write(' ');
write(writer,message);
if (_showMessageInTitle)
{
writer.write(' ');
write(writer,message);
}
writer.write("</title>\n");
}
@ -175,6 +180,22 @@ public class ErrorHandler extends AbstractHandler
{
_showStacks = showStacks;
}
/* ------------------------------------------------------------ */
/**
* @param showMessageInTitle if true, the error message appears in page title
*/
public void setShowMessageInTitle(boolean showMessageInTitle)
{
_showMessageInTitle = showMessageInTitle;
}
/* ------------------------------------------------------------ */
public boolean getShowMessageInTitle()
{
return _showMessageInTitle;
}
/* ------------------------------------------------------------ */
protected void write(Writer writer,String string)

View File

@ -20,6 +20,7 @@ import java.util.Collections;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -69,6 +70,8 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement
new HashSet<SessionTrackingMode>(
Arrays.asList(new SessionTrackingMode[]{SessionTrackingMode.COOKIE,SessionTrackingMode.URL})));
public final static String SESSION_KNOWN_ONLY_TO_AUTHENTICATED="org.eclipse.jetty.security.sessionKnownOnlytoAuthenticated";
/* ------------------------------------------------------------ */
public final static int __distantFuture=60*60*24*7*52*20;
@ -121,6 +124,28 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement
protected final CounterStatistic _sessionsStats = new CounterStatistic();
protected final SampleStatistic _sessionTimeStats = new SampleStatistic();
/* ------------------------------------------------------------ */
public static HttpSession renewSession (HttpServletRequest request, HttpSession httpSession, boolean authenticated)
{
Map<String,Object> attributes = new HashMap<String, Object>();
for (Enumeration<String> e=httpSession.getAttributeNames();e.hasMoreElements();)
{
String name=e.nextElement();
attributes.put(name,httpSession.getAttribute(name));
httpSession.removeAttribute(name);
}
httpSession.invalidate();
httpSession = request.getSession(true);
if (authenticated)
httpSession.setAttribute(SESSION_KNOWN_ONLY_TO_AUTHENTICATED, Boolean.TRUE);
for (Map.Entry<String, Object> entry: attributes.entrySet())
httpSession.setAttribute(entry.getKey(),entry.getValue());
return httpSession;
}
/* ------------------------------------------------------------ */
public AbstractSessionManager()
{

View File

@ -121,6 +121,19 @@ public class HttpConnectionTest
System.err.println(response);
}
}
@Test
public void testNoPath() throws Exception
{
String response=connector.getResponses("GET http://localhost:80 HTTP/1.1\n"+
"Host: localhost:80\n"+
"\n");
int offset=0;
offset = checkContains(response,offset,"HTTP/1.1 200");
checkContains(response,offset,"pathInfo=/");
}
@Test
public void testEmpty() throws Exception
@ -183,9 +196,15 @@ public class HttpConnectionTest
response=connector.getResponses("GET % HTTP/1.1\n"+
"Host: localhost\n"+
"Connection: close\n"+
"Connection: close\n"+
"\015\012");
checkContains(response,0,"HTTP/1.1 400");
checkContains(response,0,"HTTP/1.1 200"); //now fallback to iso-8859-1
response=connector.getResponses("GET /bad/utf8%c1 HTTP/1.1\n"+
"Host: localhost\n"+
"Connection: close\n"+
"\015\012");
checkContains(response,0,"HTTP/1.1 200"); //now fallback to iso-8859-1
}
@Test

View File

@ -182,10 +182,11 @@ public class HttpURITest
private final String[][] encoding_tests=
{
/* 0*/ {"/path/info","/path/info"},
/* 1*/ {"/path/%69nfo","/path/info"},
/* 2*/ {"http://host/path/%69nfo","/path/info"},
/* 3*/ {"http://host/path/%69nf%c2%a4","/path/inf\u00a4"},
/* 0*/ {"/path/info","/path/info", "UTF-8"},
/* 1*/ {"/path/%69nfo","/path/info", "UTF-8"},
/* 2*/ {"http://host/path/%69nfo","/path/info", "UTF-8"},
/* 3*/ {"http://host/path/%69nf%c2%a4","/path/inf\u00a4", "UTF-8"},
/* 4*/ {"http://host/path/%E5", "/path/\u00e5", "ISO-8859-1"}
};
@Test
@ -196,7 +197,7 @@ public class HttpURITest
for (int t=0;t<encoding_tests.length;t++)
{
uri.parse(encoding_tests[t][0]);
assertEquals(""+t,encoding_tests[t][1],uri.getDecodedPath());
assertEquals(""+t,encoding_tests[t][1],uri.getDecodedPath(encoding_tests[t][2]));
}
}

View File

@ -1305,7 +1305,7 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
@Override
public String toString()
{
return String.format("DATA bytes @%x available=%d consumed=%d on %s",dataInfo.hashCode(),dataInfo.available(),dataInfo.consumed(),getStream());
return String.format("DATA bytes @%x available=%d consumed=%d on %s", dataInfo.hashCode(), dataInfo.available(), dataInfo.consumed(), getStream());
}
}

View File

@ -350,7 +350,7 @@ public class StdErrLog extends AbstractLogger
this._level = LEVEL_DEBUG;
for (Logger log : Log.getLoggers().values())
{
{
if (log.getName().startsWith(getName()) && log instanceof StdErrLog)
((StdErrLog)log).setLevel(LEVEL_DEBUG);
}

View File

@ -0,0 +1,43 @@
//========================================================================
//Copyright 2012 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//http://www.apache.org/licenses/LICENSE-2.0
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//========================================================================
package org.eclipse.jetty.util.preventers;
import java.awt.Toolkit;
/**
* AWTLeakPreventer
*
* See https://issues.jboss.org/browse/AS7-3733
*
* The java.awt.Toolkit class has a static field that is the default toolkit.
* Creating the default toolkit causes the creation of an EventQueue, which has a
* classloader field initialized by the thread context class loader.
*
*/
public class AWTLeakPreventer extends AbstractLeakPreventer
{
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.util.preventers.AbstractLeakPreventer#prevent(java.lang.ClassLoader)
*/
@Override
public void prevent(ClassLoader loader)
{
LOG.debug("Pinning classloader for java.awt.EventQueue using "+loader);
Toolkit.getDefaultToolkit();
}
}

View File

@ -0,0 +1,57 @@
// ========================================================================
// Copyright (c) 2012 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.util.preventers;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* AbstractLeakPreventer
*
* Abstract base class for code that seeks to avoid pinning of webapp classloaders by using the jetty classloader to
* proactively call the code that pins them (generally pinned as static data members, or as static
* data members that are daemon threads (which use the context classloader)).
*
* Instances of subclasses of this class should be set with Server.addBean(), which will
* ensure that they are called when the Server instance starts up, which will have the jetty
* classloader in scope.
*
*/
public abstract class AbstractLeakPreventer extends AbstractLifeCycle
{
protected static final Logger LOG = Log.getLogger(AbstractLeakPreventer.class);
/* ------------------------------------------------------------ */
abstract public void prevent(ClassLoader loader);
/* ------------------------------------------------------------ */
@Override
protected void doStart() throws Exception
{
ClassLoader loader = Thread.currentThread().getContextClassLoader();
try
{
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
prevent(getClass().getClassLoader());
super.doStart();
}
finally
{
Thread.currentThread().setContextClassLoader(loader);
}
}
}

View File

@ -0,0 +1,36 @@
// ========================================================================
// Copyright (c) 2012 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.util.preventers;
import javax.imageio.ImageIO;
/**
* AppContextLeakPreventer
*
* Cause the classloader that is pinned by AppContext.getAppContext() to be
* a container or system classloader, not a webapp classloader.
*
* Inspired by Tomcat JreMemoryLeakPrevention.
*/
public class AppContextLeakPreventer extends AbstractLeakPreventer
{
/* ------------------------------------------------------------ */
@Override
public void prevent(ClassLoader loader)
{
LOG.debug("Pinning classloader for AppContext.getContext() with "+loader);
ImageIO.getUseCache();
}
}

View File

@ -0,0 +1,52 @@
//========================================================================
//Copyright 2012 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//http://www.apache.org/licenses/LICENSE-2.0
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//========================================================================
package org.eclipse.jetty.util.preventers;
import javax.xml.parsers.DocumentBuilderFactory;
/**
* DOMLeakPreventer
*
* See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6916498
*
* Prevent the RuntimeException that is a static member of AbstractDOMParser
* from pinning a webapp classloader by causing it to be set here by a non-webapp classloader.
*
* Note that according to the bug report, a heap dump may not identify the GCRoot, making
* it difficult to identify the cause of the leak.
*
*/
public class DOMLeakPreventer extends AbstractLeakPreventer
{
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.util.preventers.AbstractLeakPreventer#prevent(java.lang.ClassLoader)
*/
@Override
public void prevent(ClassLoader loader)
{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try
{
factory.newDocumentBuilder();
}
catch (Exception e)
{
LOG.warn(e);
}
}
}

View File

@ -0,0 +1,37 @@
// ========================================================================
// Copyright (c) 2012 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.util.preventers;
import java.sql.DriverManager;
/**
* DriverManagerLeakPreventer
*
* Cause DriverManager.getCallerClassLoader() to be called, which will pin the classloader.
*
* Inspired by Tomcat JreMemoryLeakPrevention.
*/
public class DriverManagerLeakPreventer extends AbstractLeakPreventer
{
/* ------------------------------------------------------------ */
@Override
public void prevent(ClassLoader loader)
{
LOG.debug("Pinning DriverManager classloader with "+loader);
DriverManager.getDrivers();
}
}

View File

@ -0,0 +1,60 @@
//========================================================================
//Copyright 2012 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//http://www.apache.org/licenses/LICENSE-2.0
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//========================================================================
package org.eclipse.jetty.util.preventers;
import java.lang.reflect.Method;
/**
* GCThreadLeakPreventer
*
* Prevents a call to sun.misc.GC.requestLatency pinning a webapp classloader
* by calling it with a non-webapp classloader. The problem appears to be that
* when this method is called, a daemon thread is created which takes the
* context classloader. A known caller of this method is the RMI impl. See
* http://stackoverflow.com/questions/6626680/does-java-garbage-collection-log-entry-full-gc-system-mean-some-class-called
*
* This preventer will start the thread with the longest possible interval, although
* subsequent calls can vary that. Recommend to only use this class if you're doing
* RMI.
*
* Inspired by Tomcat JreMemoryLeakPrevention.
*
*/
public class GCThreadLeakPreventer extends AbstractLeakPreventer
{
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.util.preventers.AbstractLeakPreventer#prevent(java.lang.ClassLoader)
*/
@Override
public void prevent(ClassLoader loader)
{
try
{
Class clazz = Class.forName("sun.misc.GC");
Method requestLatency = clazz.getMethod("requestLatency", new Class[] {long.class});
requestLatency.invoke(null, Long.valueOf(Long.MAX_VALUE-1));
}
catch (ClassNotFoundException e)
{
LOG.ignore(e);
}
catch (Exception e)
{
LOG.warn(e);
}
}
}

View File

@ -0,0 +1,45 @@
//========================================================================
//Copyright 2012 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//http://www.apache.org/licenses/LICENSE-2.0
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//========================================================================
package org.eclipse.jetty.util.preventers;
/**
* Java2DLeakPreventer
*
* Prevent pinning of webapp classloader by pre-loading sun.java2d.Disposer class
* before webapp classloaders are created.
*
* See https://issues.apache.org/bugzilla/show_bug.cgi?id=51687
*
*/
public class Java2DLeakPreventer extends AbstractLeakPreventer
{
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.util.preventers.AbstractLeakPreventer#prevent(java.lang.ClassLoader)
*/
@Override
public void prevent(ClassLoader loader)
{
try
{
Class.forName("sun.java2d.Disposer", true, loader);
}
catch (ClassNotFoundException e)
{
LOG.ignore(e);
}
}
}

View File

@ -0,0 +1,47 @@
//========================================================================
//Copyright 2012 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//http://www.apache.org/licenses/LICENSE-2.0
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//========================================================================
package org.eclipse.jetty.util.preventers;
/**
* LDAPLeakPreventer
*
* If com.sun.jndi.LdapPoolManager class is loaded and the system property
* com.sun.jndi.ldap.connect.pool.timeout is set to a nonzero value, a daemon
* thread is started which can pin a webapp classloader if it is the first to
* load the LdapPoolManager.
*
* Inspired by Tomcat JreMemoryLeakPrevention
*
*/
public class LDAPLeakPreventer extends AbstractLeakPreventer
{
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.util.preventers.AbstractLeakPreventer#prevent(java.lang.ClassLoader)
*/
@Override
public void prevent(ClassLoader loader)
{
try
{
Class.forName("com.sun.jndi.LdapPoolManager", true, loader);
}
catch (ClassNotFoundException e)
{
LOG.ignore(e);
}
}
}

View File

@ -0,0 +1,45 @@
//========================================================================
//Copyright 2012 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//http://www.apache.org/licenses/LICENSE-2.0
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//========================================================================
package org.eclipse.jetty.util.preventers;
/**
* LoginConfigurationLeakPreventer
*
* The javax.security.auth.login.Configuration class keeps a static reference to the
* thread context classloader. We prevent a webapp context classloader being used for
* that by invoking the classloading here.
*
* Inspired by Tomcat JreMemoryLeakPrevention
*/
public class LoginConfigurationLeakPreventer extends AbstractLeakPreventer
{
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.util.preventers.AbstractLeakPreventer#prevent(java.lang.ClassLoader)
*/
@Override
public void prevent(ClassLoader loader)
{
try
{
Class.forName("javax.security.auth.login.Configuration", true, loader);
}
catch (ClassNotFoundException e)
{
LOG.warn(e);
}
}
}

View File

@ -0,0 +1,40 @@
//========================================================================
//Copyright 2012 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//http://www.apache.org/licenses/LICENSE-2.0
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//========================================================================
package org.eclipse.jetty.util.preventers;
import java.security.Security;
/**
* SecurityProviderLeakPreventer
*
* Some security providers, such as sun.security.pkcs11.SunPKCS11 start a deamon thread,
* which will use the thread context classloader. Load them here to ensure the classloader
* is not a webapp classloader.
*
* Inspired by Tomcat JreMemoryLeakPrevention
*/
public class SecurityProviderLeakPreventer extends AbstractLeakPreventer
{
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.util.preventers.AbstractLeakPreventer#prevent(java.lang.ClassLoader)
*/
@Override
public void prevent(ClassLoader loader)
{
Security.getProviders();
}
}

View File

@ -19,6 +19,7 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
@ -108,7 +109,7 @@ class JarFileResource extends JarResource
/* ------------------------------------------------------------ */
/**
* Returns true if the respresenetd resource exists.
* Returns true if the represented resource exists.
*/
@Override
public boolean exists()
@ -240,68 +241,102 @@ class JarFileResource extends JarResource
@Override
public synchronized String[] list()
{
if(isDirectory() && _list==null)
if (isDirectory() && _list==null)
{
ArrayList<String> list = new ArrayList<>(32);
List<String> list = null;
try
{
list = listEntries();
}
catch (Exception e)
{
//Sun's JarURLConnection impl for jar: protocol will close a JarFile in its connect() method if
//useCaches == false (eg someone called URLConnection with defaultUseCaches==true).
//As their sun.net.www.protocol.jar package caches JarFiles and/or connections, we can wind up in
//the situation where the JarFile we have remembered in our _jarFile member has actually been closed
//by other code.
//So, do one retry to drop a connection and get a fresh JarFile
LOG.warn("Retrying list:"+e);
LOG.debug(e);
release();
list = listEntries();
}
checkConnection();
JarFile jarFile=_jarFile;
if(jarFile==null)
if (list != null)
{
try
{
JarURLConnection jc=(JarURLConnection)((new URL(_jarUrl)).openConnection());
jc.setUseCaches(getUseCaches());
jarFile=jc.getJarFile();
}
catch(Exception e)
{
LOG.ignore(e);
}
if(jarFile==null)
throw new IllegalStateException();
}
Enumeration<JarEntry> e=jarFile.entries();
String dir=_urlString.substring(_urlString.indexOf("!/")+2);
while(e.hasMoreElements())
{
JarEntry entry = e.nextElement();
String name=entry.getName().replace('\\','/');
if(!name.startsWith(dir) || name.length()==dir.length())
{
continue;
}
String listName=name.substring(dir.length());
int dash=listName.indexOf('/');
if (dash>=0)
{
//when listing jar:file urls, you get back one
//entry for the dir itself, which we ignore
if (dash==0 && listName.length()==1)
continue;
//when listing jar:file urls, all files and
//subdirs have a leading /, which we remove
if (dash==0)
listName=listName.substring(dash+1, listName.length());
else
listName=listName.substring(0,dash+1);
if (list.contains(listName))
continue;
}
list.add(listName);
}
_list=new String[list.size()];
list.toArray(_list);
_list=new String[list.size()];
list.toArray(_list);
}
}
return _list;
}
/* ------------------------------------------------------------ */
private List<String> listEntries ()
{
checkConnection();
ArrayList<String> list = new ArrayList<String>(32);
JarFile jarFile=_jarFile;
if(jarFile==null)
{
try
{
JarURLConnection jc=(JarURLConnection)((new URL(_jarUrl)).openConnection());
jc.setUseCaches(getUseCaches());
jarFile=jc.getJarFile();
}
catch(Exception e)
{
e.printStackTrace();
LOG.ignore(e);
}
if(jarFile==null)
throw new IllegalStateException();
}
Enumeration e=jarFile.entries();
String dir=_urlString.substring(_urlString.indexOf("!/")+2);
while(e.hasMoreElements())
{
JarEntry entry = (JarEntry) e.nextElement();
String name=entry.getName().replace('\\','/');
if(!name.startsWith(dir) || name.length()==dir.length())
{
continue;
}
String listName=name.substring(dir.length());
int dash=listName.indexOf('/');
if (dash>=0)
{
//when listing jar:file urls, you get back one
//entry for the dir itself, which we ignore
if (dash==0 && listName.length()==1)
continue;
//when listing jar:file urls, all files and
//subdirs have a leading /, which we remove
if (dash==0)
listName=listName.substring(dash+1, listName.length());
else
listName=listName.substring(0,dash+1);
if (list.contains(listName))
continue;
}
list.add(listName);
}
return list;
}
/* ------------------------------------------------------------ */
/**
* Return the length of the resource

View File

@ -77,18 +77,28 @@ public class StdErrLogTest
log.debug("YOU SHOULD NOT SEE THIS!",null,null);
// Test for backward compat with old (now deprecated) method
Logger before = log.getLogger("before");
log.setDebugEnabled(true);
Logger after = log.getLogger("after");
before.debug("testing {} {}","test","debug-before");
log.debug("testing {} {}","test","debug-deprecated");
after.debug("testing {} {}","test","debug-after");
log.setDebugEnabled(false);
before.debug("testing {} {}","test","debug-before-false");
log.debug("testing {} {}","test","debug-deprecated-false");
after.debug("testing {} {}","test","debug-after-false");
output.assertContains("DBUG:xxx:tname: testing test debug");
output.assertContains("INFO:xxx:tname: testing test info");
output.assertContains("WARN:xxx:tname: testing test warn");
output.assertNotContains("YOU SHOULD NOT SEE THIS!");
output.assertContains("DBUG:xxx:tname: testing test debug-deprecated");
output.assertContains("DBUG:x.before:tname:testing test debug-before");
output.assertNotContains("DBUG:xxx:tname: testing test debug-depdeprecated-false");
output.assertContains("DBUG:x.after:testing test debug-after");
output.assertNotContains("DBUG:x.before:tname:testing test debug-before-false");
output.assertNotContains("DBUG:xxx:tname:testing test debug-deprecated-false");
output.assertNotContains("DBUG:x.after:tname:testing test debug-after-false");
}
@Test

44
pom.xml
View File

@ -51,7 +51,7 @@
</goals>
<configuration>
<resourceBundles>
<resourceBundle>org.eclipse.jetty.toolchain:jetty-artifact-remote-resources:1.0</resourceBundle>
<resourceBundle>org.eclipse.jetty.toolchain:jetty-artifact-remote-resources:1.1</resourceBundle>
</resourceBundles>
</configuration>
</execution>
@ -623,5 +623,47 @@
</plugins>
</build>
</profile>
<profile>
<id>license-check</id>
<build>
<plugins>
<plugin>
<inherited>false</inherited>
<groupId>com.mycila.maven-license-plugin</groupId>
<artifactId>maven-license-plugin</artifactId>
<version>1.10.b1</version>
<configuration>
<header>header-template.txt</header>
<failIfMissing>true</failIfMissing>
<aggregate>true</aggregate>
<strictCheck>true</strictCheck>
<properties>
<copyright-range>${project.inceptionYear}-2012</copyright-range>
</properties>
<mapping>
<java>DOUBLESLASH_STYLE</java>
</mapping>
<includes>
<include>**/*.java</include>
</includes>
<excludes>
<exclude>jetty-util/src/main/java/org/eclipse/jetty/util/security/UnixCrypt.java</exclude>
<exclude>jetty-policy/src/main/java/org/eclipse/jetty/policy/loader/DefaultPolicyLoader.java</exclude>
<exclude>jetty-policy/src/main/java/org/eclipse/jetty/policy/loader/PolicyFileScanner.java</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>check-headers</id>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>