Перемещение файлов после неудачной проверки (Java) - PullRequest
3 голосов
/ 01 августа 2011

Мы проверяем файлы XML и в зависимости от результата проверки мы должны переместить файл в другую папку.

Когда XML действителен, валидатор возвращает значение, и мы можем без проблем переместить файл.То же самое происходит, когда XML недопустим в соответствии со схемой.

Если, однако, XML не сформирован правильно, валидатор выдает исключение, и когда мы пытаемся переместить файл, происходит сбой.Мы полагаем, что в памяти все еще есть дескриптор, который хранит файл.Мы попытались поместить System.gc () перед перемещением файла, и это решило проблему, но у нас не может быть System.gc() в качестве решения.

Код выглядит следующим образом.У нас есть объект File, из которого мы создаем StreamSource.Затем StreamSource передается в валидатор.Когда XML не правильно сформирован, он генерирует SAXException.В обработке исключений мы используем метод .renameTo () для перемещения файла.

sc = new StreamSource(xmlFile);
validator.validate(sc);

В улове, который мы пробовали

validator.reset();
validator=null;
sc=null;

, но все же .renameTo() не может переместитьсяфайл.Если мы поместим System.gc() в зацепку, движение будет успешным.

Может кто-нибудь подсказать мне, как отсортировать это без System.gc()?

Мы используем JAXP и saxon-9.1.0.8 в качествепарсер.

Большое спасибо

Ответы [ 5 ]

5 голосов
/ 01 августа 2011

Попробуйте создать FileInputStream и передать его в StreamSource, затем закройте FileInputStream, когда закончите. Передав File, вы потеряли контроль над тем, как / когда закрывать дескриптор файла.

3 голосов
/ 01 августа 2011

Когда вы устанавливаете sc = null, вы указываете сборщику мусора, что файл StreamSource больше не используется и что его можно собрать. Потоки закрываются в своем методе destroy(), поэтому, если они будут собирать мусор, они будут закрыты и, следовательно, могут быть перемещены в системе Windows (у вас не будет этой проблемы в системе Unix).

Чтобы решить проблему, не вызывая GC вручную, просто позвоните sc.getInputStream().close() до sc = null. В любом случае, это хорошая практика.

Распространенным шаблоном является создание блока try .. finally вокруг любого дескриптора файла, например.

try {
    sc = new StreamSource(xmlFile);
    // check stuff
} finally {
    sc.getInputStream().close();
}
// move to the appropriate place

В Java 7 вместо этого можно использовать новую попытку с ресурсами block.

1 голос
/ 01 августа 2011

Все три ответа, которые уже даны, верны: вы должны закрыть базовый поток либо прямым вызовом StramSource, либо получить поток и закрыть его, либо создать поток самостоятельно и закрыть его.

Однако я уже видел, как это происходило под окнами, по крайней мере, три года: даже если вы закроете поток, на самом деле каждый поток, если вы попытаетесь переместить или удалить файл, он выдаст исключение .. если ... вы явно вызываете System.gc ().

Однако, поскольку System.gc () не является обязательным для JVM для фактического выполнения цикла сборки мусора, и, даже если это была JVM, не требуется удалять все возможные объекты мусора, у вас нет реального способа будучи уверенным, что файл можно удалить "сейчас".

У меня нет чёткого объяснения, я могу только представить, что, вероятно, реализация java.io в Windows каким-то образом кэширует дескриптор файла и не закрывает его, пока этот дескриптор не будет очищен.

Сообщалось, но я не подтвердил, что java.nio не подвержен этому поведению, потому что он имеет более низкий уровень контроля над файловыми дескрипторами.

Решение, которое я использовал в прошлом, но которое было взломано, заключалось в следующем:

  1. Поместить файлы для удаления в «список»
  2. Попросите фоновый поток периодически проверять этот список, calla System.gc и попытайтесь удалить эти файлы.
  3. Удалите из списка файлы, которые вам удалось удалить, и оставьте там файлы, которые еще не готовы.

Обычно «задержка» составляет порядка нескольких миллисекунд, за исключением некоторых случаев, когда файлы выживают немного дольше.

Может быть хорошей идеей также вызвать deleteOnExit для этих файлов, так что если JVM завершит работу до того, как ваш поток завершит очистку некоторых файлов, JVM попытается удалить их. Однако у deleteOnExit в то время была своя собственная ошибка, препятствующая точному удалению файла, поэтому я этого не сделал. Может быть, сегодня это решено, и вы можете доверять deleteOnExit.

Это ошибка JRE, которую я нахожу наиболее раздражающей и глупой, и не могу поверить, что она все еще существует, но, к сожалению, я обнаружил ее всего месяц назад на Windows Vista с последней установленной JRE.

1 голос
/ 01 августа 2011

Попробуйте sc.getInputStream (). Close () в подвохе

0 голосов
/ 06 апреля 2016

Довольно старый, но некоторые люди все еще могут найти этот вопрос.

  1. Я использовал Oracle Java 1.8.0_77.
  2. Проблема возникает в Windows, а не в Linux.
  3. Кажется, что StreamSource, инстанцированный File, автоматически распределяет и освобождает файловый ресурс при обработке валидатором или преобразователем. (getInputStream() возвращает null)
  4. В Windows перемещение файла на место исходного файла (удаление исходного файла) после обработки невозможно.

Решение / Обходной путь: переместите файл, используя

Files.move(from.toPath(), to.toPath(), REPLACE_EXISTING, ATOMIC_MOVE);

Использование ATOMIC_MOVE здесь является критической точкой. Какой бы ни была причина, это как-то связано с раздражающим поведением файлов блокировки Windows.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...