-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implemented addBeanFromConstructor #11319
Conversation
Added mechanism to safely add beans from a super constructor of ContainerLifeCycle
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find some elegance in this proposal, the only things I'm reserved about are:
- the
addBeanFromConstructor
method name is horrible, how aboutinstallBean
instead? - maybe objects added with that new method should be impossible to remove with
removeBean
? - we should add a test to make sure
addBeanFromConstructor
doesn't make calls on the object-under-construction's reference, for instance by callingtoString
on it because of a logging call. - I'm reserving my final judgment for when all tests will be passing without any bad surprise.
jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java
Outdated
Show resolved
Hide resolved
jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java
Outdated
Show resolved
Hide resolved
OK
We spend a lot of effort to make sure beans are kept up to date with update mechanisms, so I don't think this is needed.
working on it
I've fixed some beans that were listeners, but I expect there will be a few more. |
jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java
Outdated
Show resolved
Hide resolved
jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java
Outdated
Show resolved
Hide resolved
…ponent/ContainerLifeCycle.java Co-authored-by: Simone Bordet <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I think this change has merits, it does not address the original problem reported in #11220 as in that case, the cause was:
Handler$Wrapper
's constructor- calls
Handler$Singleton.updateHandler
- calls
ContainerLifeCycle.updateBean
- calls
ContainerLifeCycle.addBean
- calls
- calls
- calls
so addBean
may be called indirectly from a general-purpose method, which bypasses this constructor-specific method.
jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java
Outdated
Show resolved
Hide resolved
Also, the following test fails when DEBUG logs are enabled, despite your change: @Test
public void testInstallBeanDoesNotEscapeConstructor()
{
ConcreteClazz c = new ConcreteClazz();
assertThat(c.exception, is(nullValue()));
}
private static abstract class AbstractClazz extends ContainerLifeCycle
{
public AbstractClazz()
{
// Pass a reference to a half-constructed object to installBean.
installBean(this);
}
}
private static class ConcreteClazz extends AbstractClazz
{
private final List<String> aList = List.of("single element");
private NullPointerException exception;
@Override
public String toString()
{
try
{
// Call a method on the aList field, if there was a constructor escape the ref will be null despite being final.
String entry = aList.get(0);
return "ConcreteClazz@" + hashCode() + "[" + entry + "]";
}
catch (NullPointerException npe)
{
exception = npe;
throw npe;
}
}
} |
@lorban I think both of your examples are poor code:
|
# Conflicts: # jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpClientTransportDynamic.java # jetty-core/jetty-http2/jetty-http2-client-transport/src/main/java/org/eclipse/jetty/http2/client/transport/ClientConnectionFactoryOverHTTP2.java # jetty-core/jetty-http2/jetty-http2-client-transport/src/main/java/org/eclipse/jetty/http2/client/transport/HttpClientTransportOverHTTP2.java # jetty-core/jetty-http3/jetty-http3-client-transport/src/main/java/org/eclipse/jetty/http3/client/transport/ClientConnectionFactoryOverHTTP3.java # jetty-core/jetty-http3/jetty-http3-client-transport/src/main/java/org/eclipse/jetty/http3/client/transport/HttpClientTransportOverHTTP3.java # jetty-core/jetty-http3/jetty-http3-client/src/main/java/org/eclipse/jetty/http3/client/HTTP3Client.java # jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/AbstractHTTP3ServerConnectionFactory.java # jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/QuicServerConnector.java # jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need some test coverage to make sure we don't de-reference objects under construction, otherwise LGTM.
jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ContextHandler.java
Outdated
Show resolved
Hide resolved
@@ -222,4 +223,58 @@ public void lifeCycleStopping(LifeCycle event) | |||
} | |||
} | |||
} | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The #1 motivation for this new installBean
method was to make sure we don't de-ref objects under construction. We should find a way to test that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neither me nor chatGPT can think of a way that the method addBean
can tell if it has been called from a super constructor or not. Thus it is impossible for us to test if it is safe for addBean
to call a listener passing this. We have provided and alternative to addBean
that is safe to call from constructors and we have javadoc'd that alternative.
I have visually scanned the list of callers of addBean and can see no constructors in there. However, I cannot tell if any of those callers are themselves called from a constructor.
I doubt that we are perfect.... but we are better than we were!
I believe we should have added a test like the following as part of #11321. It is up to you to include it in this PR or not, as that is the best coverage I can come up with, and I agree it's not much. @Test
public void testHandlerWrapperDoesNotLogUnconstructedObject()
{
JettyLogger jettyLogger = (JettyLogger)LoggerFactory.getLogger(ContainerLifeCycle.class);
JettyLevel originalLevel = jettyLogger.getLevel();
PrintStream originalPrintStream = System.err;
try
{
jettyLogger.setLevel(JettyLevel.ALL);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(baos);
System.setErr(printStream);
new Handler.Wrapper(new EchoHandler())
{
private final List<String> strings = List.of("aString");
@Override
public String toString()
{
// De-ref the final field here.
return "string[0]=" + strings.get(0);
}
};
printStream.flush();
String output = baos.toString(StandardCharsets.UTF_8);
// Check that SLF4j did not print an error to stderr.
assertThat(output, allOf(not(containsString("SLF4J: Failed toString()")), not(containsString("NullPointerException"))));
}
finally
{
jettyLogger.setLevel(originalLevel);
System.setErr(originalPrintStream);
}
} |
Maybe as a full integration test with almost all our modules turned on, full debug and a few requests sent in, then we check the log for anything that should not be there.... But I think if it is just a few handlers, it does not test much. |
@sbordet nudge! |
Added mechanism to safely add beans from a super constructor of ContainerLifeCycle
Fixes #11317