United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: 7068437 Regression: Filer.getResource(SOURCE_OUTPUT, ...) no longer works in JDK 7 w/o -s
7068437 : Regression: Filer.getResource(SOURCE_OUTPUT, ...) no longer works in JDK 7 w/o -s

Details
Type:
Bug
Submit Date:
2011-07-19
Status:
Closed
Updated Date:
2012-05-08
Project Name:
JDK
Resolved Date:
2011-10-06
Component:
tools
OS:
linux
Sub-Component:
javac
CPU:
x86
Priority:
P3
Resolution:
Fixed
Affected Versions:
7
Fixed Versions:
7u2

Related Reports
Backport:
Relates:
Relates:

Sub Tasks

Description
Run the following program with JDK 6:

---%<---
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Writer;
import java.util.Collections;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
public class Demo {
    public static void main(String[] args) {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        System.err.println("using " + compiler.getClass() + " from " + compiler.getClass().getProtectionDomain().getCodeSource());
        CompilationTask task = compiler.getTask(null, null, null, Collections.singleton("-proc:only"), Collections.singleton("java.lang.Object"), null);
        task.setProcessors(Collections.singleton(new Proc()));
        System.err.println("success? " + task.call());
        task = compiler.getTask(null, null, null, Collections.singleton("-proc:only"), Collections.singleton("java.lang.Object"), null);
        task.setProcessors(Collections.singleton(new Proc()));
        System.err.println("success? " + task.call());
    }
    @SupportedAnnotationTypes("*")
    @SupportedSourceVersion(SourceVersion.RELEASE_6)
    private static class Proc extends AbstractProcessor {
        int count;
        @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
            if (roundEnv.processingOver() || count++ > 0) {
                return false;
            }
            System.err.println("running Proc");
            try {
                processingEnv.getMessager().printMessage(Kind.NOTE, "found previous content of length " +
                        processingEnv.getFiler().getResource(StandardLocation.SOURCE_OUTPUT, "p", "C.java").getCharContent(false).length());
            } catch (FileNotFoundException x) {
                processingEnv.getMessager().printMessage(Kind.NOTE, "not previously there");
            } catch (IOException x) {
                processingEnv.getMessager().printMessage(Kind.ERROR, "while reading: " + x);
            }
            try {
                Writer w = processingEnv.getFiler().createSourceFile("p.C").openWriter();
                w.write("/* hello! */ package p; class C {}");
                w.close();
                processingEnv.getMessager().printMessage(Kind.NOTE, "wrote new content");
            } catch (IOException x) {
                processingEnv.getMessager().printMessage(Kind.ERROR, "while writing: " + x);
            }
            return true;
        }
    }
    private Demo() {}
}
---%<---

I get:

---%<---
using class com.sun.tools.javac.api.JavacTool from (file:/.../jdk1.6.0_26/lib/tools.jar <no signer certificates>)
running Proc
Note: not previously there
Note: wrote new content
success? true
running Proc
Note: found previous content of length 34
Note: wrote new content
success? true
---%<---

as expected. Now running the same program on JDK 7:

---%<---
using class com.sun.tools.javac.api.JavacTool from (file:/.../jdk1.7.0-b146/lib/tools.jar <no signer certificates>)
running Proc
warning: Supported source version 'RELEASE_6' from annotation processor 'Demo$Proc' less than -source '1.7'
Note: not previously there
Note: wrote new content
1 warning
success? true
running Proc
warning: Supported source version 'RELEASE_6' from annotation processor 'Demo$Proc' less than -source '1.7'
Note: not previously there
Note: wrote new content
1 warning
success? true
---%<---

Note that Filer.getResource(SOURCE_OUTPUT, ...) is throwing FileNotFoundException even though the source file is in fact present.

                                    

Comments
EVALUATION

We could do a short term fix to revert the Filer behavior back to that of JDK 6, but that ignores the more general problem of how should JavaFileManager handle "undefined" locations?

For example, what if a user writes a file to an "undefined" SOURCE_OUTPUT, and then tries to list the contents of that location?   Is that supposed to rollover to CLASS_OUTPUT?  What if CLASS_OUTPUT is "undefined"? -- it is not possible to list the directories that will be used using the sibling hint.
                                     
2011-09-10
EVALUATION

This is a side effect of an otherwise obvious change to JavacFiler.

In JDK 6, the method Filer.getResource (a method to read a file) used JavaFileManager.getFileForOutput to locate the resource.

In JDK 7, the method Filer.getResource (a method to read a file) was changed to use the more obvious method JavaFileManager.getFileForInput to locate the resource.

The problem is that javac is inconsistent with how these methods handle unset locations, and the spec does not define the behavior in this case.

For getFileForOutput, the method embodies the "standard" javac logic that if -s is unset, it rolls over to -d, and if -d is unset, it uses the sibling file to determine the directory to use.

For getFileForInput, the method is simpler and simply returns the file in the given Location, or FileNotFoundException if it does not exist.

This leads to the curious behavior that:

1. you can create a file using SOURCE_OUTPUT or CLASS_OUTPUT even if filemanager.hasLocation(...) is false for these locations.

2. you can create a file using SOURCE_OUTPUT or CLASS_OUTPUT and not be able to read it back in   (i.e. this bug).
                                     
2011-09-10
WORK AROUND

Pass -s to javac.
                                     
2011-07-19
PUBLIC COMMENTS

https://hg.netbeans.org/core-main/raw-file/default/openide.util/test/unit/src/org/netbeans/modules/openide/util/NbBundleProcessorTest.java similarly fails when run on JDK 7.
                                     
2011-07-19



Hardware and Software, Engineered to Work Together