Search code examples
javaweb-applicationscompiler-errorscompiler-warningsrhel

Java Compiler keeps saying "error while writing className: className.class (Permission denied)"


I'm working on a Tomcat WebApp for my university which enables students to compile their Java codes and see the trace. I'm installing it on a RHEL7 VM. But when I test the compilation function (this one is not implemented by me), the method I'm providing returns this:

error while writing className: className.class (Permission denied)
Error on line 1 in className.java

I'll show you the method I think is generating this:

public String compileJavaCode(String javaCode, String javaFileName, File workingDir) throws IOException, TimeoutException{

    javax.tools.JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
    StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
    this.createJavaFile(javaCode, javaFileName, workingDir);
    JavaFileObject file = new JavaSourceFromString(javaFileName, javaCode);

    Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(file);
    compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnits).call();

    String diagn = "";
    for ( Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()){
        diagn+=diagnostic.getMessage(null)+"\n";//E.g. cannot find symbol symbol: variable variablename
        diagn+="Error on line "+Long.toString(diagnostic.getLineNumber())+" in "+diagnostic.getSource().toUri();//E.g. Error on line 22 in ClassName.java
    }
       fileManager.close();
       compiler.run(null, null, null, workingDir.getAbsolutePath()+File.separator+javaFileName);
       return diagn;
}

Students will see the content of that diagn variable as a result for their code submission.

Fun fact is that I manage to get the className.class in the workingDir directory but I keep getting that error from the for cycle above. Could the problem be compiler.getTask(...).call()? I mean maybe compiler.run is able to generate the .class correctly but the compiler.getTask(...).call() is trying to write the .class somewhere else I don't have permission to write in.

P.S. This is a pretty legacy code so please be merciful with it. :)

As asked by @Alexander, this is the content of the Java file:

public class Sommatore {

    public int somma(int i, int j) {
        return i+j;
    }

    public int differenza(int i, int j) {
        return i-j;
    }
}

Solution

  • Seems like the user you are using doesn't have the permissions to write to the destination folder. What are the permissions of the workingDir?

    Fun fact is that i manage to get the className.class in the workingDir directory but i keep getting that error from the for cycle above. Could the problem be the compiler.getTask(...).call()? I mean maybe compiler.run is able to generate the .class correctly but the compiler.getTask(...).call() is trying to write the .class somewhere else i don't have permission to write in.

    In order to verify if this is true, you could create a folder with open permissions and try. For example, you could try using as workingDir = /tmp and check what happens.

    EDIT

    I tried to replicate your code:

        public class JavaCompiler {
        public static void main(String args[]) throws IOException, TimeoutException {
            File dir = new File(System.getProperty("user.dir") + "/src/main/java/");
            System.out.println(compileJavaCode(dir));
        }
        public static String compileJavaCode(File workingDir) throws IOException,      TimeoutException {
    
            javax.tools.JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
            StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
          //    this.createJavaFile(javaCode, javaFileName, workingDir);
          //    JavaFileObject file = new JavaSourceFromString(javaFileName, javaCode);
    
            Iterable<? extends JavaFileObject> compilationUnits = fileManager
                    .getJavaFileObjectsFromStrings(Arrays.asList(System.getProperty("user.dir") + "/src/main/java/Foo.java"));
    //    Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(file);
            compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnits).call();
    
            String diagn = "";
            for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
                diagn += diagnostic.getMessage(null) + "\n";//E.g. cannot find symbol symbol: variable variablename
                diagn += "Error on line " + Long.toString(diagnostic.getLineNumber()) + " in " + diagnostic.getSource().toUri();//E.g. Error on line 22 in ClassName.java
            }
            fileManager.close();
            compiler.run(null, null, null, workingDir.getAbsolutePath() + File.separator + "Foo.java");
            return diagn;
        }
    }
    

    with Foo.java

    public class Foo {
    
        public int somma(int i, int j) {
            return i+j;
        }
    
        public int differenza(int i, int j) {
            return i-j;
        }
    }
    

    There are some changes, but the result should be the same. I noticed that the "path" is specified in

    File workingDir
    

    that will be use in

    compiler.run(null, null, null, workingDir.getAbsolutePath() + File.separator + "Foo.java");
    

    and in

    JavaFileObject file = new JavaSourceFromString(javaFileName, javaCode);
    Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(file);
    

    that in my example:

    Iterable<? extends JavaFileObject> compilationUnits = fileManager
    .getJavaFileObjectsFromStrings(Arrays.asList(System.getProperty("user.dir") + "/src/main/java/Foo.java"));
    

    What contains yours

    workingDir
    

    and "file"?

    JavaFileObject file = new JavaSourceFromString(javaFileName, javaCode);
    

    In my case, are the same.

    I tried to execute the code with different users, and if I use an user that isn't able to write in this folder I obtain

    /tmp/testSO/src/main/java/Foo.java:5: error: error while writing Foo: /tmp/testSO/src/main/java/Foo.class (Permission denied)
    public class Foo {
           ^
    1 error
    error while writing Foo: /tmp/testSO/src/main/java/Foo.class (Permission denied)
    Error on line 5 in file:/tmp/testSO/src/main/java/Foo.java