I'm trying to configure LoggingFilter
for Jersey in an embedded Jetty setup. The glue code that is used is as follows:
ServletContainer servletContainer = new ServletContainer(application);
ServletHolder servletHolder = new ServletHolder(servletContainer);
servletHolder.setInitParameter("com.sun.jersey.config.feature.Debug", "true");
servletHolder.setInitParameter("com.sun.jersey.config.feature.Trace", "true");
servletHolder.setInitParameter("com.sun.jersey.spi.container.ContainerRequestFilters",
"com.sun.jersey.api.container.filter.LoggingFilter");
servletHolder.setInitParameter("com.sun.jersey.spi.container.ContainerResponseFilters",
"com.sun.jersey.api.container.filter.LoggingFilter");
But the logging filter is actually ignored and I see no relevant logs in the console. How can I do this? Tested both on Jersey 1.x and 2.x.
A relevant answer describes how to achieve this using web.xml
.
I think this is a very subtle nuance of the documented behavior of ServletContainer
if it isn't an outright bug. The ServletContainer
docs on the subject of init params read as:
All initialization parameters are added as properties of the created ResourceConfig.
The answer is hidden in there. Specifically, if the ResourceConfig instance isn't created by the ServletContainer, then the servlet init parameters aren't added as properties and thus won't influence your app's configuration. When you provide your own Application
instance, as you did with the new ServletContainer(application)
, initialization follows roughly this course:
Your code invokes the following ServletContainer
constructor with your Application
instance:
public ServletContainer(Application app) {
this.app = app;
}
The container initializes your ServletContainer
as part of a typical Servlet
lifecycle:
protected void init(WebConfig webConfig) throws ServletException {
webComponent = (app == null)
? new InternalWebComponent()
: new InternalWebComponent(app);
webComponent.init(webConfig);
}
There goes your Application
instance into the InternalWebComponent
constructor. An InternalWebComponent
is just a slight customization of WebComponent
, so:
InternalWebComponent(Application app) {
super(app);
}
calls:
public WebComponent(Application app) {
if (app == null)
throw new IllegalArgumentException();
if (app instanceof ResourceConfig) {
resourceConfig = (ResourceConfig) app;
} else {
resourceConfig = new ApplicationAdapter(app);
}
}
This is where, since you provided an Application
instance directly, a ResourceConfig
is built for you in one of the branches of that second if
. Immediately after construction, WebComponent.init()
is called on the new component (refer back to the ServletContainer.init()
call above, where we came from). Inside this init()
call is where the "created ResourceConfig" referred to by the docs would be created, but in your case, one already exists, as shown by the trail we followed to get here. I.e., the resourceConfig
isn't null, so the important line below doesn't execute:
public void init(WebConfig webConfig) throws ServletException {
...
if (resourceConfig == null)
resourceConfig = createResourceConfig(config);
...
}
That createResourceConfig()
method (still in WebComponent
) reads as:
private ResourceConfig createResourceConfig(WebConfig webConfig)
throws ServletException {
final Map<String, Object> props = getInitParams(webConfig);
final ResourceConfig rc = createResourceConfig(webConfig, props);
rc.setPropertiesAndFeatures(props);
return rc;
}
You can see in that call that setPropertiesAndFeatures()
is used to copy the servlet's init params into the ResourceConfig
instance. Unfortunately, this is the only place where that call is made, and in your case, execution never makes it here, basically because of your use of one of the non-default ServletContainer
constructors.
I expect that the original authors wrote ServletContainer
with just one, no-arg constructor and the other two were added later on for ease of use with Servlet 3.0 containers without realizing that this behavior was being introduced. Otherwise, I'd expect to see some mention of it in the docs.
So, long story short: either use the default ServletContainer
constructor or find a way to take care of this part yourself:
Map<String, Object> props = getInitParams(webConfig);
rc.setPropertiesAndFeatures(props);
The first way is probably the simplest. For instance, you could specify your Application
class as an init parameter, too, as long as there's nothing requiring you to instantiate it ahead of time, such as:
servletHolder.setInitParameter("javax.ws.rs.Application", "org.foo.MyApplication");
That way, the "normal" initialization path will be taken, meaning the WebComponent
will create the ResourceConfig
for you and apply the init params correctly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With