Skip to content

Directory Issue with drive letter (Windows) on 2.2.3 #988

Description

@pyxide

Hello,

the method LocalReference.createFileReference(rootDirectory) does not return a valid root path for a Directory restlet with version 2.2.3, contrary to version 2.2.2. There is a workaround by tweaking the URI manually (use file:///C:/ rather than file:///C%3A/) and passing this string to the Directory constructor.

Test 2.2.2

Testsuite: org.restlet.test.TestRestletDirectory
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0,191 sec
------------- Standard Output ---------------
-- TestRestletDirectory.testDirectory : restletVersion [2.2.2]
-- TestRestletDirectory.testDirectory : rootDirectory [C:\Users\oparent\AppData\Local\Temp\restlet-test]
test    result  httpStatus  rootDirUri  mime    uri
upper case plain    OK  200 file:///C:/Users/oparent/AppData/Local/Temp/restlet-test    text/html   http://localhost:28092/hello.html
upper case plain    OK  200 file:///C:/Users/oparent/AppData/Local/Temp/restlet-test    application/x-javascript    http://localhost:28092/test.js
upper case plain    KO  404 file:///C:/Users/oparent/AppData/Local/Temp/restlet-test    -   http://localhost:28092/test-1.2.js
lower case plain    OK  200 file:///c:/Users/oparent/AppData/Local/Temp/restlet-test    text/html   http://localhost:28092/hello.html
lower case plain    OK  200 file:///c:/Users/oparent/AppData/Local/Temp/restlet-test    application/x-javascript    http://localhost:28092/test.js
lower case plain    KO  404 file:///c:/Users/oparent/AppData/Local/Temp/restlet-test    -   http://localhost:28092/test-1.2.js
lower case uri encoded  OK  200 file:///c%3A/Users/oparent/AppData/Local/Temp/restlet-test  text/html   http://localhost:28092/hello.html
lower case uri encoded  OK  200 file:///c%3A/Users/oparent/AppData/Local/Temp/restlet-test  application/x-javascript    http://localhost:28092/test.js
lower case uri encoded  KO  404 file:///c%3A/Users/oparent/AppData/Local/Temp/restlet-test  -   http://localhost:28092/test-1.2.js
upper case uri encoded  OK  200 file:///C%3A/Users/oparent/AppData/Local/Temp/restlet-test  text/html   http://localhost:28092/hello.html
upper case uri encoded  OK  200 file:///C%3A/Users/oparent/AppData/Local/Temp/restlet-test  application/x-javascript    http://localhost:28092/test.js
upper case uri encoded  KO  404 file:///C%3A/Users/oparent/AppData/Local/Temp/restlet-test  -   http://localhost:28092/test-1.2.js
no drive letter OK  200 file:///Users/oparent/AppData/Local/Temp/restlet-test   text/html   http://localhost:28092/hello.html
no drive letter OK  200 file:///Users/oparent/AppData/Local/Temp/restlet-test   application/x-javascript    http://localhost:28092/test.js
no drive letter KO  404 file:///Users/oparent/AppData/Local/Temp/restlet-test   -   http://localhost:28092/test-1.2.js
localref.createref  OK  200 file:///C%3A/Users/oparent/AppData/Local/Temp/restlet-test  text/html   http://localhost:28092/hello.html
localref.createref  OK  200 file:///C%3A/Users/oparent/AppData/Local/Temp/restlet-test  application/x-javascript    http://localhost:28092/test.js
localref.createref  KO  404 file:///C%3A/Users/oparent/AppData/Local/Temp/restlet-test  -   http://localhost:28092/test-1.2.js
------------- ---------------- ---------------

Testcase: testDirectoryWithDriveLetter took 0,189 sec

Test 2.2.3

Testsuite: org.restlet.test.TestRestletDirectory
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0,141 sec
------------- Standard Output ---------------
-- TestRestletDirectory.testDirectory : restletVersion [2.2.3]
-- TestRestletDirectory.testDirectory : rootDirectory [C:\Users\oparent\AppData\Local\Temp\restlet-test]
test    result  httpStatus  rootDirUri  mime    uri
upper case plain    OK  200 file:///C:/Users/oparent/AppData/Local/Temp/restlet-test    text/html   http://localhost:28092/hello.html
upper case plain    OK  200 file:///C:/Users/oparent/AppData/Local/Temp/restlet-test    application/x-javascript    http://localhost:28092/test.js
upper case plain    KO  404 file:///C:/Users/oparent/AppData/Local/Temp/restlet-test    -   http://localhost:28092/test-1.2.js
lower case plain    OK  200 file:///c:/Users/oparent/AppData/Local/Temp/restlet-test    text/html   http://localhost:28092/hello.html
lower case plain    OK  200 file:///c:/Users/oparent/AppData/Local/Temp/restlet-test    application/x-javascript    http://localhost:28092/test.js
lower case plain    KO  404 file:///c:/Users/oparent/AppData/Local/Temp/restlet-test    -   http://localhost:28092/test-1.2.js
lower case uri encoded  KO  403 file:///c%3A/Users/oparent/AppData/Local/Temp/restlet-test  -   http://localhost:28092/hello.html
lower case uri encoded  KO  403 file:///c%3A/Users/oparent/AppData/Local/Temp/restlet-test  -   http://localhost:28092/test.js
lower case uri encoded  KO  403 file:///c%3A/Users/oparent/AppData/Local/Temp/restlet-test  -   http://localhost:28092/test-1.2.js
upper case uri encoded  KO  403 file:///C%3A/Users/oparent/AppData/Local/Temp/restlet-test  -   http://localhost:28092/hello.html
upper case uri encoded  KO  403 file:///C%3A/Users/oparent/AppData/Local/Temp/restlet-test  -   http://localhost:28092/test.js
upper case uri encoded  KO  403 file:///C%3A/Users/oparent/AppData/Local/Temp/restlet-test  -   http://localhost:28092/test-1.2.js
no drive letter OK  200 file:///Users/oparent/AppData/Local/Temp/restlet-test   text/html   http://localhost:28092/hello.html
no drive letter OK  200 file:///Users/oparent/AppData/Local/Temp/restlet-test   application/x-javascript    http://localhost:28092/test.js
no drive letter KO  404 file:///Users/oparent/AppData/Local/Temp/restlet-test   -   http://localhost:28092/test-1.2.js
localref.createref  KO  403 file:///C%3A/Users/oparent/AppData/Local/Temp/restlet-test  -   http://localhost:28092/hello.html
localref.createref  KO  403 file:///C%3A/Users/oparent/AppData/Local/Temp/restlet-test  -   http://localhost:28092/test.js
localref.createref  KO  403 file:///C%3A/Users/oparent/AppData/Local/Temp/restlet-test  -   http://localhost:28092/test-1.2.js
------------- ---------------- ---------------

Testcase: testDirectoryWithDriveLetter took 0,14 sec

Junit Test Case

|package org.restlet.test;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import junit.framework.TestCase;

import org.restlet.Application;
import org.restlet.Client;
import org.restlet.Component;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.Restlet;
import org.restlet.data.CharacterSet;
import org.restlet.data.LocalReference;
import org.restlet.data.Method;
import org.restlet.data.Protocol;
import org.restlet.data.Reference;
import org.restlet.data.Status;
import org.restlet.engine.Engine;
import org.restlet.representation.Representation;
import org.restlet.resource.Directory;
import org.restlet.routing.Router;

/**
 * Tests Restlet {@link Directory} with different root URIs, issue on Windows with drive letter for 2.2.3.
 * 
 * 
 * <!-- creation 2014-11-28 -->
 * @author Olivier Parent
 */
public class TestRestletDirectory extends TestCase {

    /**  DEBUG constant.  */
    private static final boolean DEBUG = false;

    static final Charset  CHARSET  = Charset.forName("utf-8");
    static final Protocol PROTOCOL = Protocol.HTTP;

    /**
     * Tests {@link Directory} with a directory starting with a drive letter (Windows).
     * @throws Exception  
     */
    public void testDirectoryWithDriveLetter() throws Exception {

        final String restletVersion = getRestletVersion();

        System.out.println("-- TestRestletDirectory.testDirectory : restletVersion [" + restletVersion + "]");

        final String host = "localhost";
        final int port    = 28092;

        // Creates the root directory in tmp folder
        // ex C:\Users\{user}\AppData\Local\Temp\restlet-test
        final File rootDirectory;
        {
            rootDirectory = new File(System.getProperty("java.io.tmpdir") + "restlet-test").getCanonicalFile();
            if(!rootDirectory.exists()) {
                if(!rootDirectory.mkdirs()) {
                    fail("cannot create the root directory [" + rootDirectory + "]");
                }
            }
        }
        final String rootPath = rootDirectory.getAbsolutePath();

        System.out.println("-- TestRestletDirectory.testDirectory : rootDirectory [" + rootDirectory.getAbsolutePath() + "]");

        // Creates same root directory URIs, with drive part modifications
        final String[][] rootDirUris = createRootPaths(rootDirectory, CHARSET);

        if(rootDirUris.length == 0) {
            System.out.println("-- TestRestletDirectory.testDirectory : no drive letter [" + rootPath + "] -> skip test");
            return;
        }

        // Some test files to be retrieved
        final List<FileNameContent> fileNameContents;
        {
            final List<FileNameContent> list = new ArrayList<FileNameContent>();
            list.add(new FileNameContent(rootDirectory, "hello.html"  , "<!DOCTYPE html><html><head><meta charset=\"" + CHARSET.name() + "\"></head><body><h1>Hello World!</h1></body></html>"));
            list.add(new FileNameContent(rootDirectory, "test.js"     , "function test() {return 'test';}"));
            // could not be retrieved with restlet-jse-2.2.2 ( 1.2 issue with tunneling maybe ?)
            // ok with restlet-jse-2.2.3
            list.add(new FileNameContent(rootDirectory, "test-1.2.js" , "function test1_2() {return 'test-1.2';}"));
            fileNameContents = Collections.unmodifiableList(list);
        }

        // create local resources
        {
            for(FileNameContent f: fileNameContents) {
                f.ensurePresent();
            }
        }

        final Client client = new Client(PROTOCOL);
        final Reference baseRef = new Reference(PROTOCOL.getSchemeName()+ "://" + host + ":" + port);
        if(DEBUG) System.out.println("-- TestRestletDirectory.testDirectory : base ref [" + baseRef + "]");

        final List<List<String>> results = new ArrayList<List<String>>();

        final Component component = createComponent(host, port);
        component.start();

        for (int i = 0; i < rootDirUris.length;) {
            final String testName   = rootDirUris[i  ][0];
            final String rootDirUri = rootDirUris[i++][1];

            if(DEBUG) {
                System.out.println("-- TestRestletDirectory.testDirectory : test [" + testName + "]");
                System.out.println("  [" + rootDirUri + "]");
            }

            final Application application = createApplication(rootDirUri);
            component.getDefaultHost().attach(application);

            for(FileNameContent f: fileNameContents) {

                final Reference ref = new Reference(baseRef, f.getFileName());
                if(DEBUG) System.out.println("-- TestRestletDirectory.testDirectory : ref [" + ref.getTargetRef() + "]");

                final Request request = new Request(Method.GET, ref);

                final Response response = client.handle(request);
                if(DEBUG) System.out.println("-- TestRestletDirectory.testDirectory : response status [" + response + "]");


                final String contentType;
                if(response.getStatus().getCode() == Status.SUCCESS_OK.getCode()) {
                    final Representation rep = response.getEntity();
                    if(DEBUG) System.out.println("-- TestRestletDirectory.testDirectory : rep [" + rep + "]");
                    contentType = rep.getMediaType().getName();
                }
                else {
                    contentType = "-";
                }

                final List<String> result = new ArrayList<String>();
                result.add(testName);
                result.add(response.getStatus().getCode() == Status.SUCCESS_OK.getCode() ? "OK" : "KO" );
                result.add(String.valueOf(response.getStatus().getCode()));
                result.add(rootDirUri);
                result.add(contentType);
                result.add(ref.getTargetRef().toString());

                results.add(result);

            }
            component.getDefaultHost().detach(application);
        }

        component.stop();

        final char separator = '\t';
        final List<String> headers = Arrays.asList(
            "test", "result", "httpStatus", "rootDirUri", "mime", "uri" 
        ); 

        System.out.println(join(headers, separator));
        for(List<String> result: results) {
            System.out.println(join(result, separator));
        }

    }

    private static final String join(Collection<?> values, char separator) {
        final StringBuilder sb = new StringBuilder();
        boolean isFirst = true;
        for(Object value: values) {
            if(isFirst) {
                isFirst = false;
            }
            else {
                sb.append(separator);
            }
            sb.append(String.valueOf(value));
        }
        return sb.toString();
    }

    /**
     * @return the restlet version
     */
    private static final String getRestletVersion() throws Exception {
//      {
//          final ClassLoader classLoader = Restlet.class.getClassLoader();
//          //final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
//          final URL url = classLoader.getResource(Restlet.class.getName().replace('.', '/') + ".class");
//          if(DEBUG) System.out.println("-- TestRestletDirectory.getRestletVersion : url [" + url + "]");
//          
//          final Pattern pattern = Pattern.compile("/(restlet[^/]+)");
//          final Matcher matcher = pattern.matcher(url.toString());
//          if(matcher.find()) {
//              restletVersion = matcher.group(1);
//          }
//          else {
//              restletVersion = (String) Engine.class.getField("VERSION").get(null);
//          }
        // 
        return (String) Engine.class.getField("VERSION").get(null);
//      return Engine.VERSION;
    }

    /**
     * @param rootDirectory
     * @param uriCharset
     * @return test paths
     * @throws UnsupportedEncodingException
     */
    private static final String[][] createRootPaths(File rootDirectory, Charset uriCharset) throws UnsupportedEncodingException {

        final Pattern pattern = Pattern.compile("^((?i)[A-Z]):(.+)$");
        final Matcher matcher = pattern.matcher(rootDirectory.getAbsolutePath());
        if(matcher.matches()) {
            final String driveLetter = matcher.group(1); 
            final String pathUri     = toUri(matcher.group(2), uriCharset);

            // format(test name, path)+
            return new String[][] {
                {"lower case plain"       , "file:///" + driveLetter.toLowerCase(Locale.ENGLISH) + ":" + pathUri},  
                {"upper case plain"       , "file:///" + driveLetter.toUpperCase(Locale.ENGLISH) + ":" + pathUri},  
                {"lower case uri encoded" , "file:///" + driveLetter.toLowerCase(Locale.ENGLISH) + toUri(":", uriCharset) + pathUri},   
                {"upper case uri encoded" , "file:///" + driveLetter.toUpperCase(Locale.ENGLISH) + toUri(":", uriCharset) + pathUri},
                {"no drive letter"        , "file://" + pathUri},
                {"localref.createref"     , LocalReference.createFileReference(rootDirectory).toString()}
            };
        }
        return new String[0][0];
    }

    /**
     * @param value
     * @throws UnsupportedEncodingException 
     */
    private static final String toUri(String value, Charset charset) throws UnsupportedEncodingException {

        final String delim = "/";
        value = value.replace('\\', delim.charAt(0));

        final StringBuilder sb = new StringBuilder();
        //sb.append();

        final StringTokenizer tokenizer = new StringTokenizer(value, delim, true);
        while(tokenizer.hasMoreElements()) {
            String token = tokenizer.nextToken();
            if(!token.equals(delim)) {
                token = URLEncoder.encode(token, charset.name());
            }
            sb.append(token);
        }
        return sb.toString();
    }

    private static final Component createComponent(final String host, final int port) {
        final Component component = new Component();
        component.getClients().add(Protocol.FILE);
        component.getClients().add(Protocol.CLAP);
        component.getClients().add(Protocol.HTTP);
        component.getServers().add(PROTOCOL, host, port);
        return component;
    }


    /**
     * Creates a component with a server and and application with a router built with a single directory. 
     * @param rootDirUri
     * @param host
     * @param port
     * @return a test component
     */
    private static final Application createApplication(final String rootDirUri) {
        final Application application = new Application() { 

            public Restlet createInboundRoot() {

                final Router router = new Router(getContext());

                final Directory directory = new Directory(getContext(), rootDirUri);
                directory.setListingAllowed(true);
                router.attachDefault(directory);

                return router;

            }
        };
        application.getMetadataService().setDefaultCharacterSet(new CharacterSet(CHARSET));
        return application;
    }

    /**
     * File name and content
     */
    private static final class FileNameContent {

        private final File directory;
        private final String fileName;
        private final String content;


        public FileNameContent(File directory, String fileName, String content) {
            this.directory = directory;
            this.fileName = fileName;
            this.content  = content;
        }

        /**
         * 
         */
        public void ensurePresent() throws IOException {
            final File file = new File(directory, fileName);            
            if(file.exists()) {
                return;
            }
            if(DEBUG) System.out.println("-- FileNameContent.ensurePresent : create [" + file + "]");
            writeTextIfNotPresent(file, content);
        }

        public final String getFileName() {
            return fileName;
        }

//      public final String getContent() {
//          return content;
//      }

        private static final void writeTextIfNotPresent(File file, String text) throws IOException {

            OutputStream output = null; 
            Writer writer = null;
            try {
                output = new FileOutputStream(file);
                writer = new OutputStreamWriter(output, CHARSET);
                writer.write(text);
                writer.close();
            }
            finally {
                if(writer != null) {
                    try { writer.close(); } catch (IOException e) { }
                }
                if(output != null) {
                    try { output.close(); } catch (IOException e) { }
                }
            }
        }

    }
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions