JDK-6791041 : Filer.createResource throws IAE "relativeName is invalid" for legitimate path ".something"
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 6
  • Priority: P3
  • Status: Resolved
  • Resolution: Cannot Reproduce
  • OS: linux
  • CPU: x86
  • Submitted: 2009-01-07
  • Updated: 2014-03-06
  • Resolved: 2014-03-06
Related Reports
Relates :  
Description
Run the following:

---%<---
import java.io.IOException;
import java.net.URI;
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.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
@SupportedAnnotationTypes("*")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class JSR269FilerBug extends AbstractProcessor {
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        if (!roundEnv.processingOver()) {
            try {
                processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", "META-INF/.something1");
                processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", ".something2");
            } catch (Exception x) {
                x.printStackTrace();
            }
        }
        return true;
    }
    public static void main(String[] args) {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        JavaFileObject file = new SimpleJavaFileObject(URI.create("no://where"), JavaFileObject.Kind.SOURCE) {
            @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
                return "class X {}";
            }
        };
        CompilationTask task = compiler.getTask(null, null, null, null, null, Collections.singleton(file));
        task.setProcessors(Collections.singleton(new JSR269FilerBug()));
        task.call();
    }
}
---%<---

On JDK 6 I get:

---%<---
java.lang.IllegalArgumentException: relativeName is invalid
        at com.sun.tools.javac.util.DefaultFileManager.getFileForOutput(DefaultFileManager.java:1050)
        at com.sun.tools.javac.processing.JavacFiler.createResource(JavacFiler.java:404)
        at JSR269FilerBug.process(JSR269FilerBug.java:24)
        at com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:624)
        at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:553)
        at com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:698)
        at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:981)
        at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:727)
        at com.sun.tools.javac.main.Main.compile(Main.java:353)
        at com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:115)
        at JSR269FilerBug.main(JSR269FilerBug.java:40)
---%<---

(Similar on JDK 7, though the class names have changed a little.)

In other words, "META-INF/.something1" is accepted as a resource name to create, but ".something2" is rejected. This is contrary to the Filer specification:

"A relative name is a non-null, non-empty sequence of path segments separated by '/'; '.' and '..' are invalid path segments."

Here the '.' is only part of a larger path segment.

Comments
Not reproducible in 7u40, 8 and 9dev.
06-03-2014

This is an old bug. Should check if it is still applicable.
04-03-2014

EVALUATION A fix for 6502392 is being discussed on compiler-dev
07-01-2009

SUGGESTED FIX protected static boolean isRelativeUri(URI uri) { if (uri.isAbsolute()) return false; String path = uri.normalize().getPath(); if (path.length() == 0) return false; char first = path.charAt(0); return first != '.' && first != '/'; } is to blame. Besides the obvious failure to check that an initial '.' is actually part of a path segment, it also fails on the presumably legitimate resource path "some:thing", and succeeds on the illegitimate path "some/../thing". Perhaps you need to examine the string directly rather than trying to go through URI. The Filer specification does refer obliquely to the URI specification but is unclear on whether e.g. "some?thing" is a valid relative name, i.e. whether the path should be treated more like a java.io.File relative path (where only '/' is a metacharacter) or an unparsed URI where metacharacters should be escaped as octets. Anyway, the most straightforward implementation under the former interpretation is: protected static boolean isRelativePath(String path) { String segment = "(?:(?![.][.]?(/|$))[^/]+)"; return path.matches(segment + "(/" + segment + ")*"); } Some test cases to try: Valid: "META-INF/.something", "something", "some:thing", ".something", ".../something", "some/thing" Invalid: "", "/something", "something/", "some/thing/", "/some/thing", "../something", "./something", "some/../thing"
07-01-2009