Skip to content

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

Open
@pyxide

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) { }
                }
            }
        }

    }
}

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions