Jettyで開発を行っていると、静的ファイルの保存時に「保管を完了できませんでした」(Save could not completed)が出てしまう場合があります。
ファイルがロックされてしまうため、Jettyを終了するまで書き込みができなくなってしまします。これは、Windowsのmemory-mapped fileに起因する現象とのことです。公式ページの Troubleshooting Locked Files on Windows には、以下の回避方法が記載されています。
どれも面倒なので、別の方法を探ってみました。
公式の方法ではないのですが、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を使えば良いと思います。まず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を利用するため、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のみ)。