Pergunta

Eu tenho um pouco de código Java que gera um arquivo XML para um sistema de arquivos montado NFS. Em outro servidor que possui o FileSyTem montado como um compartilhamento de samba, há um processo em execução que pesquisou novos arquivos XML a cada 30 segundos. Se um novo arquivo for encontrado, ele será processado e depois renomeado como um arquivo de backup. 99% das vezes, os arquivos são escritos sem um problema. No entanto, de vez em quando o arquivo de backup contém um arquivo parcialmente escrito.

Depois de alguma discussão com outras pessoas, imaginamos que o processo em execução no servidor externo estava interferindo no fluxo de saída Java quando leu o arquivo. Eles sugeriram primeiro criar um arquivo do tipo .temp que será renomeado para .xml após a conclusão da gravação do arquivo. Uma prática comum da indústria. Após a mudança, a renomeação falha sempre.

Algumas pesquisas apareceram que a E/S do arquivo java é buggy ao trabalhar com sistemas de arquivos montados no NFS.

Ajude -me Java Gurus! Como eu resolvo este problema?

Aqui estão algumas informações relevantes:

  • Meu processo é Java 1.6.0_16 em execução no Solaris 10
  • Montado o sistema de arquivos é um NAS
  • O servidor com processo de pesquisa é padrão do Windows Server 2003 R2, Service Pack 2

Aqui está uma amostra do meu código:

//Write the file
XMLOutputter serializer = new XMLOutputter(Format.getPrettyFormat());
FileOutputStream os = new FileOutputStream(outputDirectory + fileName + ".temp");
serializer.output(doc, os);//doc is a constructed xml document using JDOM
os.flush();
os.close();

//Rename the file
File oldFile = new File(outputDirectory + fileName + ".temp");
File newFile = new File(fileName + ".xml");
boolean success = oldFile.renameTo(newFile);
if (!success) {
    // File was not successfully renamed.
    throw new IOException("The file " + fileName + ".temp could not be renamed.");
}//if
Foi útil?

Solução

Você provavelmente precisa especificar o caminho completo no novo nome do arquivo:

File newFile = new File(outputDirectory + fileName + ".xml");

Outras dicas

Isso parece um bug para mim:

File oldFile = new File(outputDirectory + fileName + ".temp");
File newFile = new File(fileName + ".xml");

Eu teria esperado isso:

File oldFile = new File(outputDirectory + fileName + ".temp");
File newFile = new File(outputDirectory + fileName + ".xml");

Em geral, parece que há uma condição de corrida entre a redação do arquivo XML e a tarefa de leitura/processo/renomeio. Você pode ter a tarefa Read/Process/Renomear apenas operar em arquivos> 1 minuto de idade ou algo semelhante?

Ou, peça ao programa Java, escreva um arquivo vazio adicional depois de concluir a redação do arquivo XML que sinaliza que a gravação no arquivo XML foi concluída. Leia/Processe/renomeie apenas o arquivo XML quando o arquivo de sinal estiver presente. Em seguida, exclua o arquivo de sinal.

O bug original definitivamente soa como um problema com acesso simultâneo ao arquivo - sua solução deveria ter funcionado, mas também existem soluções alternativas.

Por exemplo, coloque um cronômetro no seu processo de leitura automática para que, quando um novo arquivo seja detectado, registra o tamanho do arquivo, acomoda x segundos e, se os tamanhos não corresponderem, reiniciar o timer. Isso deve evitar problemas com a transferência parcial de arquivos.

Editar: ou verifique os registros de data e hora mais acima para verificar isso, mas verifique se tem idade suficiente para que qualquer imprecisão no registro de data e hora não importa (digamos, 10 segundos a 1 minuto desde o último modificado).

Como alternativa, tente o seguinte:

File f = new File("foo.xml");
FileOutputStream fos = new FileOutputStream(f);
FileChannel fc = fos.getChannel();
FileLock lock = fc.lock();
(DO FILE WRITE)
fis.flush();
lock.release();
fos.close();

Isso deve usar o bloqueio de arquivos do sistema operacional nativo para evitar o acesso simultâneo por outros programas (como o seu Daemon XML Reader).

Quanto às falhas do NFS: existe um "recurso" documentado (bug) em que os arquivos não podem ser movidos entre os sistemas de arquivos via "renomear" em Java. Poderia haver confusão, pois está em um sistema de arquivos NFS?

Algumas informações para o NFS em geral. Dependendo das configurações do NFS, os bloqueios podem não funcionar e muitas instalações grandes do NFS são ajustadas para o desempenho de leitura; portanto, novos dados podem aparecer mais tarde do que o esperado, devido a efeitos de cache.

Vi efeitos em que você criou um arquivo, adicionou dados (isso foi visto em outra máquina), mas todos os dados depois apareceram com um atraso de 30 segundos.

A melhor solução, a propósito, é um esquema de arquivo rotativo. Para que o último seja considerado escrito e o anterior foi escrito com segurança e pode ser lido. Eu não funcionaria em um único arquivo e o usava como um "tubo".

Como alternativa, você pode usar um arquivo vazio que é escrito após o escrito e fechado corretamente. Então, se os caras pequenos estão lá, o grandalhão foi definitivamente feito e pode ser lido.

Possivelmente devido à "a operação de renomear pode não ser capaz de mover um arquivo de um sistema de arquivos para outro" de http://java.sun.com/j2se/1.5.0/docs/api/java/io/file.html#renameto%28java.io.file%2) Tente usar o Apache Commons io filtutils.copyFiletodirectory http://commons.apache.org/io/api-release/org/apache/commons/io/fileutils.html#copyfiletodirectory(java.io.file ,%20java.io.file) em vez de

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top