2010-12-08 12 views
5

Tengo un directorio que contiene archivos de datos servidos a los clientes, por ejemplo, /srv/data. Al hacer una serie de actualizaciones, estoy trabajando en /srv/data_tmp, y al final de la operación, me gustaría reemplazar atómicamente data con data_tmp. File.renameTo() siempre devuelve falso para mí cuando el destino es un directorio existente. ¿Cómo puedo hacer esto?¿Cómo se puede reemplazar atómicamente un directorio por otro en Java?

+0

Tiene alguna restricción en el sistema de archivos subyacente. Obviamente, si es FAT, no hay forma, ya que el sistema de archivos no lo admite. ¿Sabes si los dos directorios están en el mismo volumen físico? ¿Puedes usar las nuevas API JDK7 java.nio.file o tiene que funcionar en un JDK <= 6? –

+1

No creo que las dos operaciones de traslado de archivos se puedan realizar "atómicamente", en el sentido estricto de la palabra. Algunos sistemas de archivos garantizan algunas [operaciones atómicas] (http://www.softpanorama.org/Internals/Filesystems/ntfs.shtml) pero no conozco ninguna forma de solicitar al sistema operativo que se lleven a cabo múltiples acciones del servicio atómico para todos procesos; ciertamente no en Java. – maerics

+0

@Mike: Usaré HFS +, ext3 o NTFS, nunca FAT. Debería funcionar con JDK 6. –

Respuesta

1

Me temo que no puedes. No en el nivel SO al menos. Por lo tanto, incluso si administra la "atomicidad" en el contexto de su aplicación java, no tiene garantía de que algún otro proceso "fraudulento" interfiera en el nivel real del sistema de archivos.

Si yo fuera usted, leería this article (bastante viejo, pero debería darle algunas ideas) y luego veré si puede portar el enfoque sugerido a un more modern version.

¡Oh, espera, alguien ya hizo this!

Y al parecer usted no es the first one to ask here, ya sea

mejor de la suerte ...

+0

Las transacciones de Apache Commons me permiten hacer esto de una manera muy ordenada, aunque solo en el contexto de mi aplicación Java. Lo cual es suficiente para mí ahora. ¡Gracias por tu enlace! –

1

Puede reemplazar el directorio /srv/data con un enlace simbólico (o un junction in Windows XP) y cambiar el destino del enlace cuando corresponda. Sin embargo, no podrá hacer eso con una API de Java 6: tendrá que confiar en una biblioteca o escribir los comandos de línea de comando usted mismo.

NB: No garantizo nada sobre la atomicidad de esa operación.

+0

El enfoque del enlace symbolik es interesante, pero está equivocado al decir que no puede cambiar el nombre de un archivo a un archivo que ya existe. Si tanto a como b existen en '/ home/me', esto sobrescribe con éxito b (a en Mac OS X/HFS +):' nuevo archivo ("/ home/me/a"). RenameTo (nuevo archivo ("/home/me/b ")' –

+0

Oh, claro. No esperaba eso. –

1

La llamada al sistema Linux rename no lo permite (la llamada al sistema rename solo puede sobrescribir un directorio vacío), por lo que dudo que sea posible hacerlo en Java en Linux.

+0

¿La llamada a 'rename' es atómica? Y si es así, también en el caso de sobrescribir? –

+0

Sí, pero según la [página del manual ] (http://linux.die.net/man/2/rename) "cuando sobrescriba, probablemente haya una ventana en la que tanto oldpath como newpath se refieren al archivo que se renombra". –

0

lograr este objetivo es totalmente posible utilizar una combinación de "enlace simbólico" y "cambiar el nombre", junto con un intermedio tmp directorio. En el siguiente ejemplo se encuentra en la cáscara, pero se puede traducir fácilmente la funcionalidad aquí para utilizar las llamadas subyacentes:

mkdir -p tmp/real_dir1 tmp/real_dir2 
touch tmp/real_dir1/a tmp/real_dir2/a 
# start with ./target_dir pointing to tmp/real_dir1 
ln -s tmp/real_dir1 target_dir 
# create a symlink named target_dir in tmp, pointing to real_dir2 
ln -sf tmp/real_dir2 tmp/target_dir 
# atomically mv it into ./ replacing ./target_dir 
mv tmp/target_dir ./ 

Ejemplo aquí tomados de: http://axialcorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/

Esto se reduce a (en pseudo-código):

mkdir('./tmp'); 
mkdir('./tmp/real_dir1'); 
mkdir('./tmp/real_dir2'); 
symlink('./tmp/real_dir1', './target_dir') 
symlink('./tmp/real_dir2', './tmp/target_dir') 
rename('./tmp/target_dir', './target_dir') 

El cambio de nombre final aquí es atómico, por lo que la acción tendrá éxito o fallará por completo, desde el punto de cualquier proceso que utiliza el directorio, la acción es atómica.

Cuestiones relacionadas