JDK-6502392 : Invalid relative names for Filer.createResource and Filer.getResource
  • Type: Bug
  • Component: core-libs
  • Sub-Component: javax.annotation.processing
  • Affected Version: 6u1,7
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2006-12-08
  • Updated: 2012-03-22
  • Resolved: 2011-07-15
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6 JDK 7 Other
6-poolResolved 7 b114Fixed OpenJDK6Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Description
javax.annotation.processing.Filer has two methods: createResource() and
getResource().
The spec for getResource() and createResource() states that
IllegalArgumentException is thrown if relativeName is not relative,
IOException is thrown if the file cannot be created/opened.

On the top of Filer interface description there is a spec excerpt:
====
A relative name is a non-null, non-empty sequence of path segments
separated by '/'; '.' and '..' are invalid path segments. A valid
relative name must match the "path-rootless" rule of RFC 3986, section 3.3.
====

So the following relative names are invalid: "/boo", "goo/../hoo",
"./ioo", "".
And the filer should throw IllegalArgumentException if it uses the
relative names above when
creating or getting resources. As a variant the filer may throw
IOException when getResource is trying to open nonexistent file.

However, getting or creating resource with invalid relative names causes
to throwing neither
IllegalArgumentException nor IOException.


# javac -cp . -processor tmp.InvalidRelativeNameProcessor java.lang.Object

You may reproduce the behavior using following processor class (you need
to uncomment either writer,
or reader, or output stream, or inputstream block of code marked as 1,
2, 3, and 4). Also you may substitute StandardLocation.SOURCE_OUTPUT to
StandardLocation.CLASS_OUTPUT and run it once more.

package tmp;

import java.io.InputStream;
import java.io.Reader;
import java.io.Writer;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.Filer;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.Element;
import javax.lang.model.SourceVersion;
import java.util.Set;
import java.util.HashSet;
import java.io.IOException;
import java.io.OutputStream;
import javax.tools.JavaFileManager;
import javax.tools.StandardLocation;

public class InvalidRelativeNameProcessor extends AbstractProcessor {
    String[] invalidRelativeNames = {
            "/boo", "goo/../hoo", "./ioo", ""
    };

    public Set<String> getSupportedAnnotationTypes() {
        return new HashSet<String>() {{add("*");}};
    }

    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.RELEASE_6;
    }

    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        boolean passed = true;
        if(roundEnv.processingOver()) {
            Filer filer = processingEnv.getFiler();
            for (String relative: invalidRelativeNames) {
                try {
//                    Writer writer = filer.createResource(                                   // 1
//                            StandardLocation.SOURCE_OUTPUT, "", relative).openWriter();     // 1
//                    writer.close();                                                         // 1

//                    Reader reader = filer.createResource(                                   // 2
//                            StandardLocation.SOURCE_OUTPUT, "", relative).openReader(true); // 2
//                    reader.close();                                                         // 2

//                    OutputStream out = filer.createResource(                                  // 3
//                            StandardLocation.SOURCE_OUTPUT, "", relative).openOutputStream(); // 3
//                    out.close();                                                              // 3

//                    InputStream in = filer.createResource(                                   // 4
//                            StandardLocation.SOURCE_OUTPUT, "", relative).openInputStream(); // 4
//                    in.close();                                                              // 4
                    System.out.println("relative path: " + relative);
                } catch (IllegalArgumentException expected) {
                } catch (IOException expected) {
                } catch (Exception e) {
                    passed = false;
                    System.out.println("relative path: " + relative + ", thrown: " + e);
                }
            }
        }
        return true;
    }
}


As a result of running these tests relative names "goo/../hoo" and
"./ioo" doesn't cause to throwing either IllegalArgumentException, or
IOException. When we are trying to write a resource there are no
exception thrown at all. When we are trying to get resource then
unexpected java.lang.IllegalStateException is thrown.

I suppose it is unexpected behavior.

Comments
EVALUATION The bug is "inherited" from JavacFileManager. The spec for JavaFileManager contains the same text regarding relative names as does the spec for Filer. The impl of Filer uses JavaFileManager internally, and so it is reasonable for the Filer to expect that the impl of JavaFileManager will throw the necessary exceptions. Which it does not. The fix is simple and is to check for equality the path before and after uri normalization in JavacFileManager.isRelativeUri diff -r 3eea38ce151c src/share/classes/com/sun/tools/javac/file/JavacFileManager.java --- a/src/share/classes/com/sun/tools/javac/file/JavacFileManager.java Wed Sep 22 12:53:26 2010 -0700 +++ b/src/share/classes/com/sun/tools/javac/file/JavacFileManager.java Thu Sep 23 14:54:10 2010 -0700 @@ -672,7 +672 @@ -806,6 +806,8 @@ String path = uri.normalize().getPath(); if (path.length() == 0 /* isEmpty() is mustang API */) return false; + if (!path.equals(uri.getPath())) // implicitly checks for embedded . and .. + return false; char first = path.charAt(0); return first != '.' && first != '/'; } The test also demonstrates some inconsistencies in the argument strings given to IllegalArgumentException which should be also be fixed.
23-09-2010