Hi Mark,
OK - as a newbie I read this from the stack trace: ...
>20160713-161427.340 ERROR [catalina-exec-64] [] [[/]] StandardWrapper.Throwable
>[...]
> at
> org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
> at javax.servlet.GenericServlet.init(Unknown Source)
> at org.apache.catalina.core.StandardWrapper.initServlet(Unknown Source)
> at org.apache.catalina.core.StandardWrapper.loadServlet(Unknown Source)
> at org.apache.catalina.core.StandardWrapper.allocate(Unknown Source)
> 2) at org.apache.catalina.core.StandardWrapper.isSingleThreadModel(Unknown
> Source)
> at sun.reflect.GeneratedMethodAccessor178.invoke(Unknown Source)
> at
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
> at java.lang.reflect.Method.invoke(Method.java:497)
> 1) at org.apache.tomcat.util.modeler.BaseModelMBean.getAttribute(Unknown
> Source)
> at
> com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.getAttribute(DefaultMBeanServerInterceptor.java:647)
> at
> com.sun.jmx.mbeanserver.JmxMBeanServer.getAttribute(JmxMBeanServer.java:678)
> at org.apache.catalina.mbeans.MBeanDumper.dumpBeans(Unknown Source)
[...]
It seems to me that "all reading is passiv" don't hold because the call to
getAttibute() at 1) will invoke the MBean and this leads to the call of
isSingleThreadModel at 2) -- I don't see why at the moment.
But about 2) I take a look at the sources at github:
public boolean isSingleThreadModel() {
// Short-cuts
// If singleThreadModel is true, must have already checked this
// If instance != null, must have already loaded
if (singleThreadModel || instance != null) {
return singleThreadModel;
}
// The logic to determine this safely is more complex than one might
// expect. allocate() already has the necessary logic so re-use it.
// Make sure the Servlet is loaded with the right class loader
ClassLoader oldCL = null;
try {
oldCL = ((Context) getParent()).bind(false, null);
---> Servlet s = allocate();
deallocate(s);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
} finally {
((Context) getParent()).unbind(false, oldCL);
}
return singleThreadModel;
}
And this will load (and initialize) the servlet because some conditions are
fulfilled: It is sure that singleThradModel == false and instance == null
because we are called from isSingleThreadModel
So maybe there should be a special code path in case of an ongoing
initialization on the top like the 'if(unloading)' clause. The note states,
that one can't decide if a Servlet implements SingleThreadModel until loading.
So maybe on startup this must be answered in a "conservative way" (if possible)
or thrown back with an exception. If this will lead to an overall exception for
the MBean request, this might be "the right thing", because at leaset one of
the returned information can't be answered yet at this moment (during
initialization of the servlet).
@Override
public Servlet allocate() throws ServletException {
// If we are currently unloading this servlet, throw an exception
if (unloading) {
throw new
ServletException(sm.getString("standardWrapper.unloading", getName()));
}
boolean newInstance = false;
// If not SingleThreadedModel, return the same instance every time
if (!singleThreadModel) {
// Load and initialize our instance if necessary
---> if (instance == null || !instanceInitialized) {
synchronized (this) {
if (instance == null) {
try {
if (log.isDebugEnabled()) {
log.debug("Allocating non-STM instance");
}
// Note: We don't know if the Servlet implements
// SingleThreadModel until we have loaded it.
instance = loadServlet();
[...]
By the way: The only occurrence where instaceInitialized is set to true is
*after* a call to the core servlet.init at initServelt. Therefore it semantic
is not "initialization is started and in progress" but "initialization is
completed" and IMHO therefore one can't take it as an indicator for the fact
that's it should be done. But I can't overview if the synchronized(this) will
close this gap.
Greetings
Guido