afnf.net

Jetty+Eclipseで「保管を完了できませんでした」

Jetty useFileMappedBuffer 2014/10/09 18:16

Jettyで開発を行っていると、静的ファイルの保存時に「保管を完了できませんでした」(Save could not completed)が出てしまう場合があります。

20141009_jetty_filelock


ファイルがロックされてしまうため、Jettyを終了するまで書き込みができなくなってしまします。これは、Windowsのmemory-mapped fileに起因する現象とのことです。公式ページの Troubleshooting Locked Files on Windows には、以下の回避方法が記載されています。

  • webdefault.xmlに書く方法
  • web.xmlのDefaultServletにinit-paramを書く方法
  • copyWebDir=trueとする方法

どれも面倒なので、別の方法を探ってみました。

web.xmlのcontext-paramに書く方法

公式の方法ではないのですが、web.xmlのcontext-paramでも設定できるようです。

<web-app>
  <context-param>
    <param-name>org.eclipse.jetty.servlet.Default.useFileMappedBuffer</param-name>
    <param-value>false</param-value>
  </context-param>
  ...
</web-app>

DefaultServletの設定よりもすっきりしてて良いですね。

ServletContextListenerを使う方法

プログラム側で挙動を変更したい場合は、ServletContextListenerを使えば良いと思います。まずweb.xmlにlistenerを登録して・・・

<listener>
  <listener-class>net.afnf.hoge.MyServletContextListener</listener-class>
</listener>

ServletContext.setInitParameterでuseFileMappedBuffer=falseを設定することで、今回の問題に対処できました。

public class MyServletContextListener implements ServletContextListener {
  @Override
  public void contextInitialized(ServletContextEvent e) {
    String osname = System.getProperty("os.name");
    if (osname != null && osname.toLowerCase().indexOf("windows") != -1) {
      e.getServletContext().setInitParameter(
         "org.eclipse.jetty.servlet.Default.useFileMappedBuffer", "false"
      );
    }
  }
  ...
}

一例として、OS名での切り替えを行っています。システムプロパティーでも環境変数でも、好き放題しちゃってください。

Spring MVCのApplicationListenerでも同じようなことができると思います。

その他

なおJertty8以前では、以下のようにpom.xmlでconnector実装を変更する方法があったようですが、Jetty9ではSocketConnectorが削除されてしまったようです。

<plugin>
  <groupId>org.mortbay.jetty</groupId>
  <artifactId>jetty-maven-plugin</artifactId>
  <configuration>
    <connectors>
      <connector implementation="org.eclipse.jetty.server.bio.SocketConnector">
        <port>8080</port>
      </connector>
    </connectors>
  </configuration>
</plugin>

Spring MVCの場合(2014/10/19追記)

Spring MVCを利用するため、web.xmlで

<servlet-mapping>
  <servlet-name>mvc-dispatcher</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>

とし、mvc-dispatcher-servlet.xmlで

<mvc:resources mapping="/static/**" location="/static/" />

とした場合、/static/以下のファイルについては、org.eclipse.jetty.servlet.DefaultServletではなく、Spring MVCのResourceHttpRequestHandlerで処理されます。さらに余談ですが、バグSPR-11644が原因で、ファイルがロックされる問題がありました(3.2.8, 4.0.2, 4.0.3のみ)。

Jetty useFileMappedBuffer 2014/10/09 18:16
comments (0)

blog-java2 engine (build:2019-02-23 17:57 JST)