この日記は私的なものであり所属会社の見解とは無関係です。 GitHub: takahashikzn

"永続記憶装置からセッションをロード中の例外です"がウザったいのでセッション永続化をOFFにする

Tomcatを停止して再起動すると、こんな例外が発生することがあります。

java.lang.ClassCastException: java.lang.StackTraceElement cannot be cast to java.lang.String
    at java.io.ObjectInputStream.readTypeString(ObjectInputStream.java:1421)
    at java.io.ObjectStreamClass.readNonProxy(ObjectStreamClass.java:719)
    at java.io.ObjectInputStream.readClassDescriptor(ObjectInputStream.java:833)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1609)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1781)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2018)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1942)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1808)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)
    at java.util.ArrayList.readObject(ArrayList.java:791)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1058)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1909)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1808)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2018)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1942)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1808)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)
    at org.apache.catalina.session.StandardSession.doReadObject(StandardSession.java:1611)
    at org.apache.catalina.session.StandardSession.readObjectData(StandardSession.java:1077)
    at org.apache.catalina.session.StandardManager.doLoad(StandardManager.java:218)
    at org.apache.catalina.session.StandardManager.load(StandardManager.java:162)
    at org.apache.catalina.session.StandardManager.startInternal(StandardManager.java:356)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5196)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1403)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1393)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

Tomcatを停止した時にセッションを永続化し、再起動時に再ロードしているわけなんですがそれに失敗しています。 なにゆえStackTraceElementをStringにキャストしようとしているのかは謎ですが。。。そんなコード書いたっけ?


さて、とりあえず僕はセッションの永続化が不要なのでこの機能をOFFにしたい。 調べてみたところ、context.xmlを書く必要があるようです。

<?xml version="1.0" encoding="UTF-8"?>
<Context>
  <!-- pathnameがnullの場合、永続化は無効 -->
  <Manager className="org.apache.catalina.session.StandardManager" pathname="" />
</Context>

マニュアルによると、

This persistence may be disabled by setting this attribute to an empty string.

だそうですが、あてにならないのでソースを読みます。

以下はorg.apache.catalina.session.StandardManagerのそれっぽい箇所を抜粋したもの。

protected void doUnload() throws IOException {

    if (log.isDebugEnabled())
        log.debug(sm.getString("standardManager.unloading.debug"));

    if (sessions.isEmpty()) {
        log.debug(sm.getString("standardManager.unloading.nosessions"));
        return; // nothing to do
    }

    // Open an output stream to the specified pathname, if any
    File file = file();
    if (file == null) { // nullなら何もせず終了
        return;
    }

    ...
}

...

protected File file() {
    if (pathname == null || pathname.length() == 0) {
        return null; // pathnameがnullか空文字ならnullを返す
    }
    File file = new File(pathname);
    if (!file.isAbsolute()) {
        Context context = getContext();
        ServletContext servletContext = context.getServletContext();
        File tempdir = (File) servletContext.getAttribute(ServletContext.TEMPDIR);
        if (tempdir != null) {
            file = new File(tempdir, pathname);
        }
    }
    return file;
}

見ての通り、pathnameがnullなら永続化をしないことが確認できたのでこれで正しいようです。

なお、context.xmlの配置方法については過去記事を参照して下さい。