United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-6502392 Invalid relative names for Filer.createResource and Filer.getResource
JDK-6502392 : Invalid relative names for Filer.createResource and Filer.getResource

Details
Type:
Bug
Submit Date:
2006-12-08
Status:
Closed
Updated Date:
2012-03-22
Project Name:
JDK
Resolved Date:
2011-07-15
Component:
core-libs
OS:
generic
Sub-Component:
javax.annotation.processing
CPU:
generic
Priority:
P3
Resolution:
Fixed
Affected Versions:
6u1,7
Fixed Versions:

Related Reports
Backport:
Backport:
Relates:
Relates:
Relates:

Sub Tasks

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.
                                     
2010-09-23



Hardware and Software, Engineered to Work Together