diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java index 9cfc2ddcb77..38534445fb8 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java @@ -24,10 +24,12 @@ import java.lang.management.ManagementFactory; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.PropertiesConfigurationManager; +import org.eclipse.jetty.deploy.bindings.DebugListenerBinding; import org.eclipse.jetty.deploy.providers.WebAppProvider; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.security.HashLoginService; +import org.eclipse.jetty.server.DebugListener; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; @@ -155,6 +157,9 @@ public class LikeJettyXml // === jetty-deploy.xml === DeploymentManager deployer = new DeploymentManager(); + DebugListener debug = new DebugListener(System.out,true,true,true); + server.addBean(debug); + deployer.addLifeCycleBinding(new DebugListenerBinding(debug)); deployer.setContexts(contexts); deployer.setContextAttribute( "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/DebugListenerBinding.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/DebugListenerBinding.java new file mode 100644 index 00000000000..cc8698cf505 --- /dev/null +++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/DebugListenerBinding.java @@ -0,0 +1,53 @@ +// +// ======================================================================== +// Copyright (c) 1995-2015 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.deploy.bindings; + +import org.eclipse.jetty.deploy.App; +import org.eclipse.jetty.deploy.graph.Node; +import org.eclipse.jetty.server.DebugListener; + + +/** A Deployment binding that installs a DebugListener in all deployed contexts + */ +public class DebugListenerBinding extends DebugBinding +{ + final DebugListener _debugListener; + + public DebugListenerBinding() + { + this(new DebugListener()); + } + + public DebugListenerBinding(DebugListener debugListener) + { + super(new String[]{"deploying"}); + _debugListener=debugListener; + } + + public DebugListener getDebugListener() + { + return _debugListener; + } + + public void processBinding(Node node, App app) throws Exception + { + app.getContextHandler().addEventListener(_debugListener); + } + +} diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java index 5cda93f2db9..91f70c95390 100644 --- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java +++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java @@ -257,7 +257,7 @@ public class WebAppProvider extends ScanningAppProvider if (resource.exists() && FileID.isXmlFile(file)) { - XmlConfiguration xmlc = new XmlConfiguration(resource.getURL()) + XmlConfiguration xmlc = new XmlConfiguration(resource.getURI().toURL()) { @Override public void initializeDefaults(Object context) diff --git a/jetty-server/src/main/config/etc/jetty-debug.xml b/jetty-server/src/main/config/etc/jetty-debug.xml new file mode 100644 index 00000000000..2e47a5f9ffc --- /dev/null +++ b/jetty-server/src/main/config/etc/jetty-debug.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + /yyyy_mm_dd.debug.log + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jetty-server/src/main/config/modules/debug.mod b/jetty-server/src/main/config/modules/debug.mod new file mode 100644 index 00000000000..0141699461e --- /dev/null +++ b/jetty-server/src/main/config/modules/debug.mod @@ -0,0 +1,29 @@ +# +# Debug module +# + +[depend] +deploy + +[files] +logs/ + +[xml] +etc/jetty-debug.xml + +[ini-template] + +## How many days to retain old log files +# jetty.debug.retainDays=14 + +## Timezone of the log entries +# jetty.debug.timezone=GMT + +## Show Request/Response headers +# jetty.debug.showHeaders=true + +## Rename threads while in context scope +# jetty.debug.renameThread=false + +## Dump context as deployed +# jetty.debug.dumpContext=true diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java index 483a41c50b7..66e28253020 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java @@ -33,11 +33,18 @@ import org.eclipse.jetty.server.handler.ContextHandler; public class AsyncContextState implements AsyncContext { + private final HttpChannel _channel; volatile HttpChannelState _state; public AsyncContextState(HttpChannelState state) { _state=state; + _channel=_state.getHttpChannel(); + } + + public HttpChannel getHttpChannel() + { + return _channel; } HttpChannelState state() diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/DebugListener.java b/jetty-server/src/main/java/org/eclipse/jetty/server/DebugListener.java new file mode 100644 index 00000000000..e1f6a3e36b5 --- /dev/null +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/DebugListener.java @@ -0,0 +1,334 @@ +// +// ======================================================================== +// Copyright (c) 1995-2015 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.io.OutputStream; +import java.io.PrintStream; +import java.util.Locale; + +import javax.servlet.AsyncEvent; +import javax.servlet.AsyncListener; +import javax.servlet.DispatcherType; +import javax.servlet.ServletContext; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.servlet.ServletRequest; +import javax.servlet.ServletRequestEvent; +import javax.servlet.ServletRequestListener; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.handler.ContextHandler.Context; +import org.eclipse.jetty.server.handler.ContextHandler.ContextScopeListener; +import org.eclipse.jetty.util.DateCache; +import org.eclipse.jetty.util.annotation.ManagedAttribute; +import org.eclipse.jetty.util.annotation.ManagedObject; +import org.eclipse.jetty.util.annotation.Name; +import org.eclipse.jetty.util.component.AbstractLifeCycle; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + + +/** A Context Listener that produces additional debug. + * This listener if added to a ContextHandler, will produce additional debug information to + * either/or a specific log stream or the standard debug log. + * The events produced by {@link ServletContextListener}, {@link ServletRequestListener}, + * {@link AsyncListener} and {@link ContextScopeListener} are logged. + */ +@ManagedObject("Debug Listener") +public class DebugListener extends AbstractLifeCycle implements ServletContextListener +{ + private static final Logger LOG = Log.getLogger(DebugListener.class); + private static final DateCache __date=new DateCache("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH); + + private final String _attr = String.format("__R%s@%x",this.getClass().getSimpleName(),System.identityHashCode(this)); + + private final PrintStream _out; + private boolean _renameThread; + private boolean _showHeaders; + private boolean _dumpContext; + + public DebugListener() + { + this(null,false,false,false); + } + + public DebugListener(@Name("renameThread") boolean renameThread, @Name("showHeaders") boolean showHeaders, @Name("dumpContext") boolean dumpContext) + { + this(null,renameThread,showHeaders,dumpContext); + } + + public DebugListener(@Name("outputStream") OutputStream out, @Name("renameThread") boolean renameThread, @Name("showHeaders") boolean showHeaders, @Name("dumpContext") boolean dumpContext) + { + _out=out==null?null:new PrintStream(out); + _renameThread=renameThread; + _showHeaders=showHeaders; + _dumpContext=dumpContext; + } + + @ManagedAttribute("Rename thread within context scope") + public boolean isRenameThread() + { + return _renameThread; + } + + public void setRenameThread(boolean renameThread) + { + _renameThread = renameThread; + } + + @ManagedAttribute("Show request headers") + public boolean isShowHeaders() + { + return _showHeaders; + } + + public void setShowHeaders(boolean showHeaders) + { + _showHeaders = showHeaders; + } + + @ManagedAttribute("Dump contexts at start") + public boolean isDumpContext() + { + return _dumpContext; + } + + public void setDumpContext(boolean dumpContext) + { + _dumpContext = dumpContext; + } + + @Override + public void contextInitialized(ServletContextEvent sce) + { + sce.getServletContext().addListener(_servletRequestListener); + ContextHandler handler = ContextHandler.getContextHandler(sce.getServletContext()); + handler.addEventListener(_contextScopeListener); + String cname=findContextName(sce.getServletContext()); + log("^ ctx=%s %s",cname,sce.getServletContext()); + if (_dumpContext) + { + if (_out==null) + handler.dumpStdErr(); + else + { + try + { + handler.dump(_out); + } + catch(Exception e) + { + LOG.warn(e); + } + } + } + } + + @Override + public void contextDestroyed(ServletContextEvent sce) + { + String cname=findContextName(sce.getServletContext()); + log("v ctx=%s %s",cname,sce.getServletContext()); + } + + protected String findContextName(ServletContext context) + { + if (context==null) + return null; + String n = (String)context.getAttribute(_attr); + if (n==null) + { + n=String.format("%s@%x",context.getContextPath(),context.hashCode()); + context.setAttribute(_attr,n); + } + return n; + } + + protected String findRequestName(ServletRequest request) + { + if (request==null) + return null; + HttpServletRequest r = (HttpServletRequest)request; + String n = (String)request.getAttribute(_attr); + if (n==null) + { + n=String.format("%s@%x",r.getRequestURI(),request.hashCode()); + request.setAttribute(_attr,n); + } + return n; + } + + protected void log(String format, Object... arg) + { + if (!isRunning()) + return; + + String s=String.format(format,arg); + + long now = System.currentTimeMillis(); + long ms = now%1000; + if (_out!=null) + _out.printf("%s.%03d:%s%n",__date.formatNow(now),ms,s); + if (LOG.isDebugEnabled()) + LOG.info(s); + } + + final AsyncListener _asyncListener = new AsyncListener() + { + @Override + public void onTimeout(AsyncEvent event) throws IOException + { + String cname=findContextName(((AsyncContextEvent)event).getServletContext()); + String rname=findRequestName(event.getAsyncContext().getRequest()); + log("! ctx=%s r=%s onTimeout %s",cname,rname,((AsyncContextEvent)event).getHttpChannelState()); + } + + @Override + public void onStartAsync(AsyncEvent event) throws IOException + { + String cname=findContextName(((AsyncContextEvent)event).getServletContext()); + String rname=findRequestName(event.getAsyncContext().getRequest()); + log("! ctx=%s r=%s onStartAsync %s",cname,rname,((AsyncContextEvent)event).getHttpChannelState()); + } + + @Override + public void onError(AsyncEvent event) throws IOException + { + String cname=findContextName(((AsyncContextEvent)event).getServletContext()); + String rname=findRequestName(event.getAsyncContext().getRequest()); + log("!! ctx=%s r=%s onError %s %s",cname,rname,event.getThrowable(),((AsyncContextEvent)event).getHttpChannelState()); + } + + @Override + public void onComplete(AsyncEvent event) throws IOException + { + AsyncContextEvent ace=(AsyncContextEvent)event; + String cname=findContextName(ace.getServletContext()); + String rname=findRequestName(ace.getAsyncContext().getRequest()); + + Request br=Request.getBaseRequest(ace.getAsyncContext().getRequest()); + Response response = br.getResponse(); + String headers=_showHeaders?("\n"+response.getHttpFields().toString()):""; + + log("! ctx=%s r=%s onComplete %s %d%s",cname,rname,ace.getHttpChannelState(),response.getStatus(),headers); + } + }; + + final ServletRequestListener _servletRequestListener = new ServletRequestListener() + { + @Override + public void requestInitialized(ServletRequestEvent sre) + { + String cname=findContextName(sre.getServletContext()); + HttpServletRequest r = (HttpServletRequest)sre.getServletRequest(); + + String rname=findRequestName(r); + DispatcherType d = r.getDispatcherType(); + if (d==DispatcherType.REQUEST) + { + Request br=Request.getBaseRequest(r); + + String headers=_showHeaders?("\n"+br.getMetaData().getFields().toString()):""; + + + StringBuffer url=r.getRequestURL(); + if (r.getQueryString()!=null) + url.append('?').append(r.getQueryString()); + log(">> %s ctx=%s r=%s %s %s %s %s %s%s",d, + cname, + rname, + d, + r.getMethod(), + url.toString(), + r.getProtocol(), + br.getHttpChannel(), + headers); + } + else + log(">> %s ctx=%s r=%s",d,cname,rname); + } + + @Override + public void requestDestroyed(ServletRequestEvent sre) + { + String cname=findContextName(sre.getServletContext()); + HttpServletRequest r = (HttpServletRequest)sre.getServletRequest(); + String rname=findRequestName(r); + DispatcherType d = r.getDispatcherType(); + if (sre.getServletRequest().isAsyncStarted()) + { + sre.getServletRequest().getAsyncContext().addListener(_asyncListener); + log("<< %s ctx=%s r=%s async=true",d,cname,rname); + } + else + { + Request br=Request.getBaseRequest(r); + String headers=_showHeaders?("\n"+br.getResponse().getHttpFields().toString()):""; + log("<< %s ctx=%s r=%s async=false %d%s",d,cname,rname,Request.getBaseRequest(r).getResponse().getStatus(),headers); + } + } + }; + + final ContextHandler.ContextScopeListener _contextScopeListener = new ContextHandler.ContextScopeListener() + { + @Override + public void enterScope(Context context, Request request, Object reason) + { + String cname=findContextName(context); + if (request==null) + log("> ctx=%s %s",cname,reason); + else + { + String rname=findRequestName(request); + + if (_renameThread) + { + Thread thread=Thread.currentThread(); + thread.setName(String.format("%s#%s",thread.getName(),rname)); + } + + log("> ctx=%s r=%s %s",cname,rname,reason); + } + } + + + @Override + public void exitScope(Context context, Request request) + { + String cname=findContextName(context); + if (request==null) + log("< ctx=%s",cname); + else + { + String rname=findRequestName(request); + + log("< ctx=%s r=%s",cname,rname); + if (_renameThread) + { + Thread thread=Thread.currentThread(); + if (thread.getName().endsWith(rname)) + thread.setName(thread.getName().substring(0,thread.getName().length()-rname.length()-1)); + } + } + } + }; +} diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java index 4f8cce73a0a..9e02326dfc8 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java @@ -447,6 +447,10 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor handleException(e); } } + finally + { + _request.setDispatcherType(null); + } action = _state.unhandle(); } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java index bf4af4b46ef..8a2b1cb7619 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java @@ -270,23 +270,37 @@ public class HttpChannelState _async=Async.STARTED; _event=event; lastAsyncListeners=_asyncListeners; - _asyncListeners=null; + _asyncListeners=null; } if (lastAsyncListeners!=null) { - for (AsyncListener listener : lastAsyncListeners) + Runnable callback=new Runnable() { - try + @Override + public void run() { - listener.onStartAsync(event); + for (AsyncListener listener : lastAsyncListeners) + { + try + { + listener.onStartAsync(event); + } + catch(Exception e) + { + // TODO Async Dispatch Error + LOG.warn(e); + } + } } - catch(Exception e) + @Override + public String toString() { - // TODO Async Dispatch Error - LOG.warn(e); + return "startAsync"; } - } + }; + + runInContext(event,callback); } } @@ -416,12 +430,17 @@ public class HttpChannelState public void dispatch(ServletContext context, String path) { - boolean dispatch; + boolean dispatch=false; + AsyncContextEvent event=null; try(Locker.Lock lock= _locker.lock()) { + boolean started=false; + event=_event; switch(_async) { case STARTED: + started=true; + break; case EXPIRING: case ERRORED: break; @@ -435,27 +454,26 @@ public class HttpChannelState if (path!=null) _event.setDispatchPath(path); - switch(_state) + if (started) { - case DISPATCHED: - case ASYNC_IO: - dispatch=false; - break; - case ASYNC_WAIT: - _state=State.ASYNC_WOKEN; - dispatch=true; - break; - case ASYNC_WOKEN: - dispatch=false; - break; - default: - LOG.warn("async dispatched when complete {}",this); - dispatch=false; - break; + switch(_state) + { + case DISPATCHED: + case ASYNC_IO: + case ASYNC_WOKEN: + break; + case ASYNC_WAIT: + _state=State.ASYNC_WOKEN; + dispatch=true; + break; + default: + LOG.warn("async dispatched when complete {}",this); + break; + } } } - cancelTimeout(); + cancelTimeout(event); if (dispatch) scheduleDispatch(); } @@ -471,6 +489,7 @@ public class HttpChannelState _async=Async.EXPIRING; event=_event; listeners=_asyncListeners; + } if (LOG.isDebugEnabled()) @@ -478,43 +497,65 @@ public class HttpChannelState if (listeners!=null) { - for (AsyncListener listener : listeners) + Runnable callback=new Runnable() { - try + @Override + public void run() { - listener.onTimeout(event); + for (AsyncListener listener : listeners) + { + try + { + listener.onTimeout(event); + } + catch(Exception e) + { + LOG.debug(e); + event.addThrowable(e); + _channel.getRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,event.getThrowable()); + break; + } + } } - catch(Exception e) + @Override + public String toString() { - LOG.debug(e); - event.addThrowable(e); - _channel.getRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,event.getThrowable()); - break; + return "onTimeout"; } - } + }; + + runInContext(event,callback); } boolean dispatch=false; try(Locker.Lock lock= _locker.lock()) { - if (_async==Async.EXPIRING) + switch(_async) { - // If the listeners did not call dispatch() or complete(), - // then the container must generate an error. - if (event.getThrowable()==null) - { - _async=Async.EXPIRED; - _event.addThrowable(new TimeoutException("Async API violation")); - } - else - { - _async=Async.ERRORING; - } - if (_state==State.ASYNC_WAIT) - { - _state=State.ASYNC_WOKEN; - dispatch=true; - } + case EXPIRING: + if (event.getThrowable()==null) + { + _async=Async.EXPIRED; + _event.addThrowable(new TimeoutException("Async API violation")); + } + else + { + _async=Async.ERRORING; + } + break; + + case COMPLETE: + case DISPATCH: + break; + + default: + throw new IllegalStateException(); + } + + if (_state==State.ASYNC_WAIT) + { + _state=State.ASYNC_WOKEN; + dispatch=true; } } @@ -530,11 +571,17 @@ public class HttpChannelState { // just like resume, except don't set _dispatched=true; boolean handle=false; + AsyncContextEvent event=null; try(Locker.Lock lock= _locker.lock()) { + boolean started=false; + event=_event; + switch(_async) { case STARTED: + started=true; + break; case EXPIRING: case ERRORED: break; @@ -542,22 +589,17 @@ public class HttpChannelState throw new IllegalStateException(this.getStatusStringLocked()); } _async=Async.COMPLETE; - if (_state==State.ASYNC_WAIT) + + if (started && _state==State.ASYNC_WAIT) { handle=true; _state=State.ASYNC_WOKEN; } } - cancelTimeout(); + cancelTimeout(event); if (handle) - { - ContextHandler handler=getContextHandler(); - if (handler!=null) - handler.handle(_channel.getRequest(),_channel); - else - _channel.handle(); - } + runInContext(event,_channel); } public void errorComplete() @@ -631,17 +673,31 @@ public class HttpChannelState { if (aListeners!=null) { - for (AsyncListener listener : aListeners) + Runnable callback = new Runnable() { - try + @Override + public void run() { - listener.onComplete(event); - } - catch(Exception e) + for (AsyncListener listener : aListeners) + { + try + { + listener.onComplete(event); + } + catch(Exception e) + { + LOG.warn(e); + } + } + } + @Override + public String toString() { - LOG.warn(e); + return "onComplete"; } - } + }; + + runInContext(event,callback); } event.completed(); } @@ -697,7 +753,6 @@ public class HttpChannelState } } - protected void scheduleDispatch() { _channel.execute(_channel); @@ -717,10 +772,15 @@ public class HttpChannelState { event=_event; } + cancelTimeout(event); + } + + protected void cancelTimeout(AsyncContextEvent event) + { if (event!=null) event.cancelTimeoutTask(); } - + public boolean isIdle() { try(Locker.Lock lock= _locker.lock()) @@ -779,7 +839,6 @@ public class HttpChannelState } } - public boolean isAsync() { try(Locker.Lock lock= _locker.lock()) @@ -805,7 +864,11 @@ public class HttpChannelState { event=_event; } + return getContextHandler(event); + } + ContextHandler getContextHandler(AsyncContextEvent event) + { if (event!=null) { Context context=((Context)event.getServletContext()); @@ -822,11 +885,25 @@ public class HttpChannelState { event=_event; } + return getServletResponse(event); + } + + public ServletResponse getServletResponse(AsyncContextEvent event) + { if (event!=null && event.getSuppliedResponse()!=null) return event.getSuppliedResponse(); return _channel.getResponse(); } - + + void runInContext(AsyncContextEvent event,Runnable runnable) + { + ContextHandler contextHandler = getContextHandler(event); + if (contextHandler==null) + runnable.run(); + else + contextHandler.handle(_channel.getRequest(),runnable); + } + public Object getAttribute(String name) { return _channel.getRequest().getAttribute(name); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java index 2b325694337..d30633345aa 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java @@ -975,7 +975,7 @@ public class Request implements HttpServletRequest @Override public String getMethod() { - return _metadata.getMethod(); + return _metadata==null?null:_metadata.getMethod(); } /* ------------------------------------------------------------ */ @@ -1787,6 +1787,12 @@ public class Request implements HttpServletRequest setPathInfo(info); } + /* ------------------------------------------------------------ */ + public org.eclipse.jetty.http.MetaData.Request getMetaData() + { + return _metadata; + } + /* ------------------------------------------------------------ */ public boolean hasMetaData() { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java index cbdde7fae5c..75a87f0e56e 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java @@ -1076,7 +1076,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu } if (old_context != _scontext) - enterScope(baseRequest); + enterScope(baseRequest,dispatch); if (LOG.isDebugEnabled()) LOG.debug("context={}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(), baseRequest.getPathInfo(),this); @@ -1184,8 +1184,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu /** * @param request A request that is applicable to the scope, or null + * @param reason An object that indicates the reason the scope is being entered. */ - protected void enterScope(Request request) + protected void enterScope(Request request, Object reason) { if (!_contextListeners.isEmpty()) { @@ -1193,7 +1194,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu { try { - listener.enterScope(_scontext,request); + listener.enterScope(_scontext,request,reason); } catch(Throwable e) { @@ -1235,10 +1236,18 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu { ClassLoader old_classloader = null; Thread current_thread = null; - Context old_context = null; + Context old_context = __context.get(); + + // Are we already in the scope? + if (old_context==_scontext) + { + runnable.run(); + return; + } + + // Nope, so enter the scope and then exit try { - old_context = __context.get(); __context.set(_scontext); // Set the classloader @@ -1249,7 +1258,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu current_thread.setContextClassLoader(_classLoader); } - enterScope(request); + enterScope(request,runnable); runnable.run(); } finally @@ -2859,7 +2868,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu * @param context The context being entered * @param request A request that is applicable to the scope, or null */ - void enterScope(Context context, Request request); + void enterScope(Context context, Request request, Object reason); /** diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DebugHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DebugHandler.java index ebb71641dfa..be5822294f9 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DebugHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DebugHandler.java @@ -30,6 +30,7 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.server.AbstractConnector; import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.DebugListener; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Response; import org.eclipse.jetty.util.DateCache; @@ -42,6 +43,7 @@ import org.eclipse.jetty.util.RolloverFileOutputStream; * Details of the request and response are written to an output stream * and the current thread name is updated with information that will link * to the details in that output. + * @deprecated Use {@link DebugListener} */ public class DebugHandler extends HandlerWrapper implements Connection.Listener { diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncIOServletTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncIOServletTest.java index d1e0379b9c3..034a26c1f89 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncIOServletTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncIOServletTest.java @@ -87,7 +87,7 @@ public class AsyncIOServletTest context.addEventListener(new ContextHandler.ContextScopeListener() { @Override - public void enterScope(Context context, Request request) + public void enterScope(Context context, Request request, Object reason) { if (scope.get()!=null) throw new IllegalStateException(); diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletIOTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletIOTest.java index 0326d44a47a..721e7914e20 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletIOTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletIOTest.java @@ -50,6 +50,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.DebugListener; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.Server; @@ -83,6 +84,7 @@ public class AsyncServletIOTest _server.setConnectors(new Connector[]{ _connector }); ServletContextHandler context = new ServletContextHandler(); context.setContextPath("/ctx"); + context.addEventListener(new DebugListener()); _server.setHandler(context); _servletHandler=context.getServletHandler(); diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java index 3c076af5849..111c302266e 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java @@ -44,6 +44,7 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.DebugListener; import org.eclipse.jetty.server.QuietServletException; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.RequestLog; @@ -101,6 +102,7 @@ public class AsyncServletTest ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS); context.setContextPath("/ctx"); logHandler.setHandler(context); + context.addEventListener(new DebugListener()); _servletHandler=context.getServletHandler(); ServletHolder holder=new ServletHolder(_servlet); diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppClassLoader.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppClassLoader.java index 782a6f93292..32bdccd9466 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppClassLoader.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppClassLoader.java @@ -374,42 +374,49 @@ public class WebAppClassLoader extends URLClassLoader String tmp = name; if (tmp != null && tmp.endsWith(".class")) tmp = tmp.substring(0, tmp.length()-6); - + boolean system_class=_context.isSystemClass(tmp); boolean server_class=_context.isServerClass(tmp); + if (LOG.isDebugEnabled()) + LOG.debug("getResource({}) system={} server={} cl={}",name,system_class,server_class,this); + if (system_class && server_class) return null; + ClassLoader source=null; + if (_parent!=null &&(_context.isParentLoaderPriority() || system_class ) && !server_class) { tried_parent= true; if (_parent!=null) - url= _parent.getResource(name); + { + source=_parent; + url=_parent.getResource(name); + } } if (url == null) { url= this.findResource(name); - + source=this; if (url == null && name.startsWith("/")) - { - if (LOG.isDebugEnabled()) - LOG.debug("HACK leading / off " + name); url= this.findResource(name.substring(1)); - } } if (url == null && !tried_parent && !server_class ) { if (_parent!=null) + { + tried_parent=true; + source=_parent; url= _parent.getResource(name); + } } - if (url != null) - if (LOG.isDebugEnabled()) - LOG.debug("getResource("+name+")=" + url); + if (LOG.isDebugEnabled()) + LOG.debug("gotResource({})=={} from={} tried_parent={}",name,url,source,tried_parent); return url; } @@ -434,6 +441,11 @@ public class WebAppClassLoader extends URLClassLoader boolean system_class=_context.isSystemClass(name); boolean server_class=_context.isServerClass(name); + if (LOG.isDebugEnabled()) + LOG.debug("loadClass({}) system={} server={} cl={}",name,system_class,server_class,this); + + ClassLoader source=null; + if (system_class && server_class) { return null; @@ -442,6 +454,7 @@ public class WebAppClassLoader extends URLClassLoader if (c == null && _parent!=null && (_context.isParentLoaderPriority() || system_class) && !server_class) { tried_parent= true; + source=_parent; try { c= _parent.loadClass(name); @@ -458,6 +471,7 @@ public class WebAppClassLoader extends URLClassLoader { try { + source=this; c= this.findClass(name); } catch (ClassNotFoundException e) @@ -467,20 +481,24 @@ public class WebAppClassLoader extends URLClassLoader } if (c == null && _parent!=null && !tried_parent && !server_class ) + { + tried_parent=true; + source=_parent; c= _parent.loadClass(name); + } if (c == null && ex!=null) { - LOG.debug("not found {} from {}",name,this,ex); + LOG.debug("!loadedClass({}) from={} tried_parent={}",name,this,tried_parent); throw ex; } + if (LOG.isDebugEnabled()) + LOG.debug("loadedClass({})=={} from={} tried_parent={}",name,c,source,tried_parent); + if (resolve) resolveClass(c); - if (LOG.isDebugEnabled()) - LOG.debug("loaded {} from {}",c,c==null?null:c.getClassLoader()); - return c; } } @@ -541,7 +559,10 @@ public class WebAppClassLoader extends URLClassLoader { content = url.openStream(); byte[] bytes = IO.readBytes(content); - + + if (LOG.isDebugEnabled()) + LOG.debug("foundClass({}) url={} cl={}",name,url,this); + for (ClassFileTransformer transformer : _transformers) { byte[] tmp = transformer.transform(this,name,null,null,bytes);