ablog

不器用で落着きのない技術者のメモ

org.apache.poi.openxml4j.exceptions.InvalidOperationException: Can't open the specified file: ...

現象

org.apache.poi.openxml4j.exceptions.InvalidOperationException: Can't open the specified file: '/tmp/poifiles/...'
  • 環境依存。発生する環境と発生しない環境がある。

原因

  • /tmp の容量不足。
  • /tmp を df で監視しながらバッチ処理を実行すると、/tmp が 100% になりバッチが異常終了した。

対処

$ java -Djava.io.tmpdir=/var/tmp ...

蛇足

ちょっと、Java VM のソースを見てみた。

% cd ~/Documents/src
% curl -O http://download.java.net/jdk6/6u21/promoted/b05/jdk-6u21-ea-src-b05-jrl-29_may_2010.jar 
% java -jar jdk-6u21-ea-src-b05-jrl-29_may_2010.jar 
  • [ACCEPT] を押す。
  • Select install directory: src
  • jvm のソースをのぞいてみる。
% cd ~/Documents/src/jdk-6u21-ea-src-b05-jrl-29_may_2010/src
% grep -R java.io.tmpdir *
deploy/src/common/share/classes/com/sun/deploy/util/NativeLibraryBundle.java:  java.io.tmpdir
deploy/src/common/share/classes/com/sun/deploy/util/NativeLibraryBundle.java:        String rootDirName = System.getProperty("java.io.tmpdir") +
j2se/src/linux/doc/man/ja/rmid.1:\f2System.err\fP ???Ф?????Ϥϡ??ե?????˥?????쥯?Ȥ???롣???Υե?????? \f2java.io.tmpdir\fP ?????ƥ?ץ??ѥƥ????ǻ??ꤵ???ǥ??쥯?ȥ? (?̾?? \f2/var/tmp\fP ?ޤ??? \f2/tmp\fP) ?ˤ??롣 ?ե?????̾????Ƭ???? \f2rmid\-err\fP ?ǡ????????? \f2"tmp"\fP ?Ǥ??? 
j2se/src/linux/doc/man/rmid.1:Output printed to \f2System.err\fP is redirected to a file. This file is located in the directory specified by the \f2java.io.tmpdir\fP system property (typically \f2/var/tmp\fP or \f2/tmp\fP) with the prefix \f2"rmid\-err"\fP and the suffix \f2"tmp"\fP. 
j2se/src/share/classes/com/sun/tools/javac/util/DefaultFileManager.java:                    preindexCacheLocation = options.get("java.io.tmpdir");
j2se/src/share/classes/com/sun/tools/javac/zip/ZipFileIndex.java: *  the value of the "java.io.tmpdir" system property.
j2se/src/share/classes/java/io/File.java:                    new GetPropertyAction("java.io.tmpdir")));
j2se/src/share/classes/java/io/File.java:     * <code>java.io.tmpdir</code>.  On UNIX systems the default value of this
j2se/src/share/classes/java/lang/System.java:     * <tr><td><code>java.io.tmpdir</code></td>
j2se/src/share/classes/java/util/logging/FileHandler.java:	    	    String tmpDir = System.getProperty("java.io.tmpdir");
j2se/src/share/classes/javax/imageio/ImageIO.java:     * java.io.tmpdir system property.
j2se/src/share/classes/javax/imageio/ImageIO.java:        GetPropertyAction a = new GetPropertyAction("java.io.tmpdir");
j2se/src/share/classes/javax/management/loading/MLet.java:	     //      can't read the MLET_LIB_DIR or java.io.tmpdir properties
j2se/src/share/classes/javax/management/loading/MLet.java:	 String tmpDir = System.getProperty("java.io.tmpdir");
j2se/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/package.html:the JVM implementation. However, the <tt>java.io.tmpdir</em> system
j2se/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataFile.java:     * This method generally returns the value of the java.io.tmpdir
j2se/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataFile.java:     * the directory indicated in the java.io.tmpdir property. However,
j2se/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataFile.java:     * Solaris java.io.tmpdir property were set to /tmp by default
j2se/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataFile.java:         * Why is java.io.tmpdir on Solaris set to "/var/tmp/" when the
j2se/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataFile.java:        String tmpdir = System.getProperty("java.io.tmpdir");
j2se/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataFile.java:              * java.io.tmpdir is set to "/var/tmp/" on Solaris and Linux,
j2se/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataFile.java:              * to java.io.tmpdir.
j2se/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataFile.java:         * changed such that the java.io.tmpdir string no longer terminates
j2se/src/share/classes/sun/security/provider/SeedGenerator.java:			File f = new File(p.getProperty("java.io.tmpdir"));
j2se/src/share/demo/java2d/J2DBench/src/j2dbench/ResultSet.java:	"java.io.tmpdir",
j2se/src/share/native/java/lang/System.c:    PUTPROP_ForPlatformCString(props, "java.io.tmpdir", sprops->tmp_dir);
j2se/src/share/sample/jmx/jmx-scandir/test/com/sun/jmx/examples/scandir/config/XmlConfigUtilsTest.java:        final String tmp = System.getProperty("java.io.tmpdir");
j2se/src/share/sample/jmx/jmx-scandir/test/com/sun/jmx/examples/scandir/config/XmlConfigUtilsTest.java:        final String tmp = System.getProperty("java.io.tmpdir");
j2se/src/share/sample/jmx/jmx-scandir/test/com/sun/jmx/examples/scandir/DirectoryScannerTest.java:            final String tmpdir = System.getProperty("java.io.tmpdir");
j2se/src/share/sample/jmx/jmx-scandir/test/com/sun/jmx/examples/scandir/DirectoryScannerTest.java:            final String tmpdir = System.getProperty("java.io.tmpdir");
j2se/src/share/sample/jmx/jmx-scandir/test/com/sun/jmx/examples/scandir/DirectoryScannerTest.java:        bean.setRootDirectory(System.getProperty("java.io.tmpdir"));
j2se/src/share/sample/jmx/jmx-scandir/test/com/sun/jmx/examples/scandir/DirectoryScannerTest.java:            final String tmpdir = System.getProperty("java.io.tmpdir");
j2se/src/solaris/doc/sun/man/man1/ja/rmid.1:\f2System.err\fP ???Ф?????&#996;&#993;??&#1381;?????&#741;?????&#51567;?&#548;???&#47203;???Υ&#1381;?????? \f2java.io.tmpdir\fP ?????&#421;?&#1509;??&#1125;&#421;????&#507;??&#43317;???&#485;??&#51567;?&#549;? (?&#830;?? \f2/var/tmp\fP ?&#1956;??? \f2/tmp\fP) ?&#740;??&#47203; ?&#1381;?????&#830;????&#428;???? \f2rmid\-err\fP ?&#481;????????? \f2"tmp"\fP ?&#484;??? 
j2se/src/solaris/doc/sun/man/man1/rmid.1:Output printed to \f2System.err\fP is redirected to a file. This file is located in the directory specified by the \f2java.io.tmpdir\fP system property (typically \f2/var/tmp\fP or \f2/tmp\fP) with the prefix \f2"rmid\-err"\fP and the suffix \f2"tmp"\fP.
  • j2se/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataFile.java
215      /**
216       * Return the name of the temporary directory being searched for
217       * HotSpot PerfData backing store files.
218       * <p>
219       * This method generally returns the value of the java.io.tmpdir
220       * property. However, on some platforms it may return a different
221       * directory, as the JVM implementation may store the PerfData backing
222       * store files in a different directory for performance reasons.
223       *
224       * @return String - the name of the temporary directory.
225       */
226      public static String getTempDirectory() {
227          return tmpDirName;
228      }
229      /**
230       * Return the name of the temporary directory to be searched
231       * for HotSpot PerfData backing store files for a given user.
232       * <p>
233       * This method generally returns the name of a subdirectory of
234       * the directory indicated in the java.io.tmpdir property. However,
235       * on some platforms it may return a different directory, as the
236       * JVM implementation may store the PerfData backing store files
237       * in a different directory for performance reasons.
238       *
239       * @return String - the name of the temporary directory.
240       */
241      public static String getTempDirectory(String user) {
242          return tmpDirName + dirNamePrefix + user + File.separator;
243      }
244      /*
245       * this static initializer would not be necessary if the
246       * Solaris java.io.tmpdir property were set to /tmp by default
247       */
248      static {
249          /*
250           * Why is java.io.tmpdir on Solaris set to "/var/tmp/" when the
251           * HotSpot JVM os:get_temp_path() method returns "/tmp/"
252           *
253           * Why do Solaris and Windows return a string with a trailing
254           * file separator character where as Linix does not? (this change
255           * seems to have occurred sometime during hopper beta)
256           */
257          String tmpdir = System.getProperty("java.io.tmpdir");
258          if (tmpdir.compareTo("/var/tmp/") == 0) {
259               /*
260                * shared memory files are created in /tmp. Interestingly,
261                * java.io.tmpdir is set to "/var/tmp/" on Solaris and Linux,
262                * but os::get_temp_directory() is set to "/tmp/" on these
263                * platforms. the java.io.logging packages also makes reference
264                * to java.io.tmpdir.
265                */
266               tmpdir = "/tmp/";
267          }
268          /*
269           * Assure that the string returned has a trailing File.separator
270           * character. This check was added because the Linux implementation
271           * changed such that the java.io.tmpdir string no longer terminates
272           * with a File.separator character.
273           */
274          if (tmpdir.lastIndexOf(File.separator) != (tmpdir.length()-1)) {
275              tmpdir = tmpdir + File.separator;
276          }
277          tmpDirName = tmpdir;
278      }
279  }

なるほど、id:t_yano の言っていた通りの感じになってた。


追記:
よく見ると、このソースコードは一時ファイルディレクトリを取得している箇所ではなさそう。