Jetty 12 #9630 dumpable context (#9885)

* Fix #9630 dumpable context

Added DumpableAttributes utility class

* fixed test format

* Updates from review
This commit is contained in:
Greg Wilkins 2023-06-08 21:14:04 +02:00 committed by GitHub
parent 64b54adb9c
commit c2dc87c039
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 138 additions and 365 deletions

View File

@ -1,26 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.server;
/**
* @deprecated {@link org.eclipse.jetty.util.component.ClassLoaderDump}
*/
@Deprecated
public class ClassLoaderDump extends org.eclipse.jetty.util.component.ClassLoaderDump
{
public ClassLoaderDump(ClassLoader loader)
{
super(loader);
}
}

View File

@ -1,279 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.server;
/**
* A HttpChannel.Listener that holds a collection of
* other HttpChannel.Listener instances that are efficiently
* invoked without iteration.
* @see AbstractConnector
*/
@Deprecated // TODO update or remove
public class HttpChannelListeners // TODO ??? implements HttpChannel.Listener
{
/* TODO
static final Logger LOG = LoggerFactory.getLogger(HttpChannelListeners.class);
public static HttpChannel.Listener NOOP = new HttpChannel.Listener() {};
private final NotifyRequest onRequestBegin;
private final NotifyRequest onBeforeDispatch;
private final NotifyFailure onDispatchFailure;
private final NotifyRequest onAfterDispatch;
private final NotifyContent onRequestContent;
private final NotifyRequest onRequestContentEnd;
private final NotifyRequest onRequestTrailers;
private final NotifyRequest onRequestEnd;
private final NotifyFailure onRequestFailure;
private final NotifyRequest onResponseBegin;
private final NotifyRequest onResponseCommit;
private final NotifyContent onResponseContent;
private final NotifyRequest onResponseEnd;
private final NotifyFailure onResponseFailure;
private final NotifyRequest onComplete;
public HttpChannelListeners(Collection<HttpChannel.Listener> listeners)
{
try
{
NotifyRequest onRequestBegin = NotifyRequest.NOOP;
NotifyRequest onBeforeDispatch = NotifyRequest.NOOP;
NotifyFailure onDispatchFailure = NotifyFailure.NOOP;
NotifyRequest onAfterDispatch = NotifyRequest.NOOP;
NotifyContent onRequestContent = NotifyContent.NOOP;
NotifyRequest onRequestContentEnd = NotifyRequest.NOOP;
NotifyRequest onRequestTrailers = NotifyRequest.NOOP;
NotifyRequest onRequestEnd = NotifyRequest.NOOP;
NotifyFailure onRequestFailure = NotifyFailure.NOOP;
NotifyRequest onResponseBegin = NotifyRequest.NOOP;
NotifyRequest onResponseCommit = NotifyRequest.NOOP;
NotifyContent onResponseContent = NotifyContent.NOOP;
NotifyRequest onResponseEnd = NotifyRequest.NOOP;
NotifyFailure onResponseFailure = NotifyFailure.NOOP;
NotifyRequest onComplete = NotifyRequest.NOOP;
for (HttpChannel.Listener listener : listeners)
{
if (!listener.getClass().getMethod("onRequestBegin", Request.class).isDefault())
onRequestBegin = combine(onRequestBegin, listener::onRequestBegin);
if (!listener.getClass().getMethod("onBeforeDispatch", Request.class).isDefault())
onBeforeDispatch = combine(onBeforeDispatch, listener::onBeforeDispatch);
if (!listener.getClass().getMethod("onDispatchFailure", Request.class, Throwable.class).isDefault())
onDispatchFailure = combine(onDispatchFailure, listener::onDispatchFailure);
if (!listener.getClass().getMethod("onAfterDispatch", Request.class).isDefault())
onAfterDispatch = combine(onAfterDispatch, listener::onAfterDispatch);
if (!listener.getClass().getMethod("onRequestContent", Request.class, ByteBuffer.class).isDefault())
onRequestContent = combine(onRequestContent, listener::onRequestContent);
if (!listener.getClass().getMethod("onRequestContentEnd", Request.class).isDefault())
onRequestContentEnd = combine(onRequestContentEnd, listener::onRequestContentEnd);
if (!listener.getClass().getMethod("onRequestTrailers", Request.class).isDefault())
onRequestTrailers = combine(onRequestTrailers, listener::onRequestTrailers);
if (!listener.getClass().getMethod("onRequestEnd", Request.class).isDefault())
onRequestEnd = combine(onRequestEnd, listener::onRequestEnd);
if (!listener.getClass().getMethod("onRequestFailure", Request.class, Throwable.class).isDefault())
onRequestFailure = combine(onRequestFailure, listener::onRequestFailure);
if (!listener.getClass().getMethod("onResponseBegin", Request.class).isDefault())
onResponseBegin = combine(onResponseBegin, listener::onResponseBegin);
if (!listener.getClass().getMethod("onResponseCommit", Request.class).isDefault())
onResponseCommit = combine(onResponseCommit, listener::onResponseCommit);
if (!listener.getClass().getMethod("onResponseContent", Request.class, ByteBuffer.class).isDefault())
onResponseContent = combine(onResponseContent, listener::onResponseContent);
if (!listener.getClass().getMethod("onResponseEnd", Request.class).isDefault())
onResponseEnd = combine(onResponseEnd, listener::onResponseEnd);
if (!listener.getClass().getMethod("onResponseFailure", Request.class, Throwable.class).isDefault())
onResponseFailure = combine(onResponseFailure, listener::onResponseFailure);
if (!listener.getClass().getMethod("onComplete", Request.class).isDefault())
onComplete = combine(onComplete, listener::onComplete);
}
this.onRequestBegin = onRequestBegin;
this.onBeforeDispatch = onBeforeDispatch;
this.onDispatchFailure = onDispatchFailure;
this.onAfterDispatch = onAfterDispatch;
this.onRequestContent = onRequestContent;
this.onRequestContentEnd = onRequestContentEnd;
this.onRequestTrailers = onRequestTrailers;
this.onRequestEnd = onRequestEnd;
this.onRequestFailure = onRequestFailure;
this.onResponseBegin = onResponseBegin;
this.onResponseCommit = onResponseCommit;
this.onResponseContent = onResponseContent;
this.onResponseEnd = onResponseEnd;
this.onResponseFailure = onResponseFailure;
this.onComplete = onComplete;
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
@Override
public void onRequestBegin(Request request)
{
onRequestBegin.onRequest(request);
}
@Override
public void onBeforeDispatch(Request request)
{
onBeforeDispatch.onRequest(request);
}
@Override
public void onDispatchFailure(Request request, Throwable failure)
{
onDispatchFailure.onFailure(request, failure);
}
@Override
public void onAfterDispatch(Request request)
{
onAfterDispatch.onRequest(request);
}
@Override
public void onRequestContent(Request request, ByteBuffer content)
{
onRequestContent.onContent(request, content);
}
@Override
public void onRequestContentEnd(Request request)
{
onRequestContentEnd.onRequest(request);
}
@Override
public void onRequestTrailers(Request request)
{
onRequestTrailers.onRequest(request);
}
@Override
public void onRequestEnd(Request request)
{
onRequestEnd.onRequest(request);
}
@Override
public void onRequestFailure(Request request, Throwable failure)
{
onRequestFailure.onFailure(request, failure);
}
@Override
public void onResponseBegin(Request request)
{
onResponseBegin.onRequest(request);
}
@Override
public void onResponseCommit(Request request)
{
onResponseCommit.onRequest(request);
}
@Override
public void onResponseContent(Request request, ByteBuffer content)
{
onResponseContent.onContent(request, content);
}
@Override
public void onResponseEnd(Request request)
{
onResponseEnd.onRequest(request);
}
@Override
public void onResponseFailure(Request request, Throwable failure)
{
onResponseFailure.onFailure(request, failure);
}
@Override
public void onComplete(Request request)
{
onComplete.onRequest(request);
}
private interface NotifyRequest
{
void onRequest(Request request);
NotifyRequest NOOP = request ->
{
};
}
private interface NotifyFailure
{
void onFailure(Request request, Throwable failure);
NotifyFailure NOOP = (request, failure) ->
{
};
}
private interface NotifyContent
{
void onContent(Request request, ByteBuffer content);
NotifyContent NOOP = (request, content) ->
{
};
}
private static NotifyRequest combine(NotifyRequest first, NotifyRequest second)
{
if (first == NotifyRequest.NOOP)
return second;
if (second == NotifyRequest.NOOP)
return first;
return request ->
{
first.onRequest(request);
second.onRequest(request);
};
}
private static NotifyFailure combine(NotifyFailure first, NotifyFailure second)
{
if (first == NotifyFailure.NOOP)
return second;
if (second == NotifyFailure.NOOP)
return first;
return (request, throwable) ->
{
first.onFailure(request, throwable);
second.onFailure(request, throwable);
};
}
private static NotifyContent combine(NotifyContent first, NotifyContent second)
{
if (first == NotifyContent.NOOP)
return (request, content) -> second.onContent(request, content.slice());
if (second == NotifyContent.NOOP)
return (request, content) -> first.onContent(request, content.slice());
return (request, content) ->
{
content = content.slice();
first.onContent(request, content);
second.onContent(request, content);
};
}
*/
}

View File

@ -51,6 +51,8 @@ import org.eclipse.jetty.util.Uptime;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.Name;
import org.eclipse.jetty.util.component.AttributeContainerMap;
import org.eclipse.jetty.util.component.ClassLoaderDump;
import org.eclipse.jetty.util.component.DumpableAttributes;
import org.eclipse.jetty.util.component.DumpableCollection;
import org.eclipse.jetty.util.component.Environment;
import org.eclipse.jetty.util.component.Graceful;
@ -804,8 +806,10 @@ public class Server extends Handler.Wrapper implements Attributes
@Override
public void dump(Appendable out, String indent) throws IOException
{
dumpObjects(out, indent, new org.eclipse.jetty.util.component.ClassLoaderDump(this.getClass().getClassLoader()),
dumpObjects(out, indent,
new ClassLoaderDump(this.getClass().getClassLoader()),
new DumpableCollection("environments", Environment.getAll()),
new DumpableAttributes("attributes", _attributes),
FileSystemPool.INSTANCE);
}

View File

@ -52,7 +52,7 @@ import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.component.ClassLoaderDump;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.component.DumpableAttributes;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceFactory;
@ -272,8 +272,8 @@ public class ContextHandler extends Handler.Wrapper implements Attributes, Alias
{
dumpObjects(out, indent,
new ClassLoaderDump(getClassLoader()),
Dumpable.named("context " + this, _context),
Dumpable.named("handler attributes " + this, _persistentAttributes));
new DumpableAttributes("handler attributes", _persistentAttributes),
new DumpableAttributes("attributes", _context));
}
@ManagedAttribute(value = "Context")

View File

@ -60,6 +60,7 @@ import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.component.Graceful;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
@ -1061,4 +1062,54 @@ public class ContextHandlerTest
assertTrue(shutdown.isDone());
assertThat(gracefulHandler.getCurrentRequestCount(), is(0L));
}
@Test
public void testContextDump() throws Exception
{
Server server = new Server();
ContextHandler contextHandler = new ContextHandler("/ctx");
server.setHandler(contextHandler);
contextHandler.setHandler(new Handler.Abstract()
{
@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception
{
callback.succeeded();
return true;
}
@Override
public String toString()
{
return "TestHandler";
}
});
contextHandler.setAttribute("name", "hidden");
contextHandler.setAttribute("persistent1", "value1");
contextHandler.setAttribute("persistent2", Dumpable.named("named", "value2"));
server.start();
contextHandler.getContext().setAttribute("name", "override");
contextHandler.getContext().setAttribute("transient1", "value1");
contextHandler.getContext().setAttribute("transient2", Dumpable.named("named", "value2"));
String dump = contextHandler.dump().replaceAll("\\r?\\n", "\n");
assertThat(dump, containsString("oejsh.ContextHandler@"));
String expected = """
+> No ClassLoader
+> handler attributes size=3
| +> name: hidden
| +> persistent1: value1
| +> persistent2: named: value2
+> attributes size=5
+> name: override
+> persistent1: value1
+> persistent2: named: value2
+> transient1: value1
+> transient2: named: value2
""";
assertThat(dump, containsString(expected));
}
}

View File

@ -64,63 +64,51 @@ public interface Attributes
// TODO: change to getAttributeNames() once jetty-core is cleaned of servlet-api usages
Set<String> getAttributeNameSet();
// TODO something better than this
default Map<String, Object> asAttributeMap()
{
return new AbstractMap<>()
{
private final Set<String> _attributeNameSet = getAttributeNameSet();
private final AbstractSet<Entry<String, Object>> _entrySet = new AbstractSet<>()
{
@Override
public Iterator<Entry<String, Object>> iterator()
{
Iterator<String> names = _attributeNameSet.iterator();
return new Iterator<>()
{
@Override
public boolean hasNext()
{
return names.hasNext();
}
@Override
public Entry<String, Object> next()
{
String name = names.next();
return new SimpleEntry<>(name, getAttribute(name));
}
};
}
@Override
public int size()
{
return _attributeNameSet.size();
}
};
@Override
public int size()
{
return _attributeNameSet.size();
}
@Override
public Set<Entry<String, Object>> entrySet()
{
return new AbstractSet<>()
{
Iterator<String> names = getAttributeNameSet().iterator();
@Override
public Iterator<Entry<String, Object>> iterator()
{
return new Iterator<>()
{
@Override
public boolean hasNext()
{
return names.hasNext();
}
@Override
public Entry<String, Object> next()
{
String name = names.next();
return new Map.Entry<>()
{
@Override
public String getKey()
{
return name;
}
@Override
public Object getValue()
{
return getAttribute(name);
}
@Override
public Object setValue(Object value)
{
throw new UnsupportedOperationException();
}
};
}
};
}
@Override
public int size()
{
return 0;
}
};
return _entrySet;
}
};
}

View File

@ -0,0 +1,40 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.util.component;
import java.io.IOException;
import java.util.Map;
import org.eclipse.jetty.util.Attributes;
public class DumpableAttributes implements Dumpable
{
private final String _name;
private final Attributes _attributes;
public DumpableAttributes(String name, Attributes attributes)
{
_name = name;
_attributes = attributes;
}
@Override
public void dump(Appendable out, String indent) throws IOException
{
Object[] array = _attributes.asAttributeMap().entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.map(e -> Dumpable.named(e.getKey(), e.getValue())).toArray(Object[]::new);
Dumpable.dumpObjects(out, indent, _name + " size=" + array.length, array);
}
}

View File

@ -44,12 +44,6 @@ public class DumpableCollection implements Dumpable
return new DumpableCollection(name, collection);
}
@Override
public String dump()
{
return Dumpable.dump(this);
}
@Override
public void dump(Appendable out, String indent) throws IOException
{
@ -57,3 +51,4 @@ public class DumpableCollection implements Dumpable
Dumpable.dumpObjects(out, indent, _name + " size=" + (array == null ? 0 : array.length), array);
}
}