4

We have a legacy system that has a admim module that allows users to upload jar files. After the upload, the jar file is validated and if not compliant to internal rules, it is deleted.

The problem is that windows is throwing an exception telling that the file "is already being used by another process." (when I call Files.delete(tmpJar);). I'm not able to identify why the file is open. Seems to me that I have closed everything.

First, we are using primefaces (4.0) to upload the file. Primefaces relies on commons-fileupload (1.3.1). It call the following method:

public void handleFileUpload(FileUploadEvent event) {
   Path tmpJar = null;
   try {
      tmpJar = Files.createFile(Paths.get(event.getFile().getFileName()));
      Files.write(tmpJar, event.getFile().getContents());
   } catch (IOException e) {
      LOGGER.error(e.getMessage(), e);
   }

   if (tmpJar != null) {
      try {
         this.validateJar(tmpJar.toString());
         Files.delete(tmpJar);
      } catch (IOException e) {
         LOGGER.error(e.getMessage(), e);
      }
   }
}

Before NIO Files.write, I was using "standard" java IO classes. The problem isn't related to the above code, because if I comment the call to validateJar, Files.delete(tmpJar) is executed without problems and the file is removed. So, the problem is related with the code below, but I can't find where...

Job is an internal class, basically a simple POJO. "jobAnnotation" is a custom annotation to identify Jobs. I have shortened the code, but the essencial parts are preserved.

private List<Job> validateJar(final String jarPath) throws IOException {
   List<Job> jobs = new ArrayList<Job>();

   try (JarFile jarFile = new JarFile(jarPath)) {
      URL[] jars = { new URL("file:" + jarPath) };

      ClassLoader jobClassLoader = URLClassLoader.newInstance(jars, this.getClass().getClassLoader());

      Enumeration<JarEntry> jarEntries = jarFile.entries();
      while (jarEntries.hasMoreElements()) {
         JarEntry jarEntry = jarEntries.nextElement();
         String className = jarEntry.getName();
         Class<?> classToLoad;
         try {
            classToLoad = Class.forName(className, true, jobClassLoader);
         } catch (Exception e1) {
            LOGGER.error(e1.getMessage(), e1);
            continue;
         }

         if (classToLoad.isAnnotationPresent(jobAnnotation)) {
            String vlr = null;
            try {
               Class<?> jobClass = (Class<?>) Class.forName(classToLoad.getCanonicalName(), true, jobClassLoader);
                Annotation annotation = jobClass.getAnnotation(jobAnnotation);
                Method method = annotation.getClass().getMethod("getValue");
                vlr = ((String) method.invoke(annotation, new Object[0]));
            } catch (Exception e1) {
               LOGGER.error(e1.getMessage(), e1);
            }

            Job job = new Job();
            job.setEnabled(true);
            job.setJarfile(jarPath);
            job.setClassName(classToLoad.getName());

            Parameter parameter = new Parameter();
            parameter.setRequired(true);
            parameter.setName("name");
            parameter.setValue(vlr);

            job.addParameter(parameter);
            jobs.add(job);
         }
      }
   } catch (IOException e) {
      throw e;
   }
   return jobs;
}

Before using try-with-resources, I was using regular try-catch-finally to close the JarFile, thats the only thing that has a explicit close method. Probably is the classloading that is holding the file open, but I don't know how to close it.

I did some searches, and I found that I can't unload classes (Unloading classes in java?).

So, the problem is, how do I release it? Or how can I remove the file?

BTW, I'm using java 1.7.0_71, jboss 7.1.1, windows 7 (64).

Community
  • 1
  • 1
Bob Rivers
  • 5,261
  • 6
  • 47
  • 59
  • 1
    The URLClassLoader class has a close() method, can you try and use that ? The close() method will close any Jar file that are opened with the URLClassLoader. – codelion Dec 30 '14 at 16:05
  • If closing the ClassLoader doesn't work, you could try using a byte code interpreter instead, so you can just open the .class files like a regular file, and have complete control over when the file is closed. [BECL and others are available for this.](http://java-source.net/open-source/bytecode-libraries) – markspace Dec 30 '14 at 16:15
  • @codelion Put your comment as an answer, so I can close my question accepting your answer. It worked. My fault is that I was using the interface to create the classloader -- ClassLoader jobClassLoader = URLClassLoader.newInstance(jars, this.getClass().getClassLoader()); -- and it doesnt have the close() method. So I changed to -- URLClassLoader jobClassLoader = URLClassLoader.newInstance(jars, this.getClass().getClassLoader()); -- and then I called close, and it worked... – Bob Rivers Dec 30 '14 at 18:18

3 Answers3

1

The URLClassLoader class already has a close() method. The close() method will close any Jar file that are opened with the URLClassLoader. This should prevent the "file already in use" exception.

codelion
  • 1,056
  • 1
  • 11
  • 17
0

File is already being used by another process. says that it could be not your fault, maybe just another application is used that file. You can check this question to find a process which is used your file.

Community
  • 1
  • 1
darkled
  • 257
  • 2
  • 11
  • If you see his code, he is opening the jar file and loading using URLClassLoader so it is not another application that is using the file. – codelion Dec 30 '14 at 16:16
0

Some Virus scanner software take a long time in checking JARs. Try to disable the Virusscanner. Other candidates can be the Windows indexer process, or the explorer.exe itself. When you don't find any reason for the file lock, try a delay between the validation and the deletion. Maybe you need a loop with multiple tries.

Cfx
  • 2,272
  • 2
  • 15
  • 21