Search code examples
javahttprestrestlet

Restlet Router Relative Path Error


I am having an opposite error of what's written here.

I'm just trying to run a very simple sample application of Restlet in Eclipse.

MailServerApplication.java

public class MailServerApplication extends Application {

    /**
     * Launches the application with an HTTP server.
     * 
     * @param args
     *            The arguments.
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        Server mailServer = new Server(Protocol.HTTP, 8111);
        mailServer.setNext(new MailServerApplication());
        mailServer.start();
    }

    /**
     * Constructor.
     */
    public MailServerApplication() {
        setName("RESTful Mail Server");
        setDescription("Example for 'Restlet in Action' book");
        setOwner("Restlet S.A.S.");
        setAuthor("The Restlet Team");
    }

    /**
     * Creates a root Router to dispatch call to server resources.
     */
    @Override
    public Restlet createInboundRoot() {
        Router router = new Router(getContext());
        router.attach("http://localhost:8111/", 
                RootServerResource.class);
        router.attach("http://localhost:8111/accounts/",
                AccountsServerResource.class);
        router.attach("http://localhost:8111/accounts/{accountId}",
                AccountServerResource.class);
        return router;
    }
}

RootServerResource.java

public class RootServerResource 
    extends ServerResource implements RootResource {

    public String represent() {
        return "This is the root resource";
    }

    public String describe() {
        throw new RuntimeException("Not yet implemented");
    }
}

RootResource.java

/**
 * Root resource.
 */
public interface RootResource {

    /**
     * Represents the application root with a welcome message.
     * 
     * @return The root representation.
     */
    @Get("txt")
    public String represent();

}

The code works perfectly as is if I'm running the server locally and if I type the full uri including the localhost on my browser "localhost:8111". However, as soon as I change my router declaration to be router, the page always throws a 404 error.

@Override
public Restlet createInboundRoot() {
    Router router = new Router(getContext());
    router.attach("/", RootServerResource.class); 
    router.attach("/accounts/", AccountsServerResource.class);
    router.attach("/accounts/{accountId}", AccountServerResource.class);
    return router;
}

So in other words, if I attach a full path including the http and the ip address to the router, it works correctly but the relative path doesn't.

This is pretty bizzare. If there's any error, I would've assumed that the relative definition should work and the localhost definition shouldn't, but what I'm experiencing is the exact opposite. Any suggestions?

Edit:

Upon request, I'm including my AccountServerResource.class

/**
 * Implementation of a mail account resource.
 */
public class AccountServerResource extends ServerResource implements
        AccountResource {

    /** The account identifier. */
    private int accountId;

    /**
     * Retrieve the account identifier based on the URI path variable
     * "accountId" declared in the URI template attached to the application
     * router.
     */
    @Override
    protected void doInit() throws ResourceException {
        this.accountId = Integer.parseInt(getAttribute("accountId"));
    }

    public String represent() {
        return AccountsServerResource.getAccounts().get(this.accountId);
    }

    public void store(String account) {
        AccountsServerResource.getAccounts().set(this.accountId, account);
    }

    public void remove() {
        AccountsServerResource.getAccounts().remove(this.accountId);
    }
}

And the AccountResource interface:

/**
 * User account resource.
 */
public interface AccountResource {

    /**
     * Represents the account as a simple string with the owner name for now.
     * 
     * @return The account representation.
     */
    @Get("txt")
    public String represent();

    /**
     * Stores the new value for the identified account.
     * 
     * @param account
     *            The identified account.
     */
    @Put("txt")
    public void store(String account);

    /**
     * Deletes the identified account by setting its value to null.
     */
    @Delete
    public void remove();

}

Solution

  • This is because you are running restlet in standalone mode. To be more specific MailServerApplication has main-method from which you are running restlet.

    To fix the issue, you need to make your web-container understand the details of your Application.

    Here is a skeleton version of the code needed for you to run. This way, you need not mention the IP, Port details in the url-binding (this example uses Jetty, you could as well use tomcat):

    MyApplication.java:

    package com.sample;
    
    import org.restlet.Application;
    import org.restlet.Context;
    import org.restlet.Request;
    import org.restlet.Response;
    import org.restlet.Restlet;
    import org.restlet.data.MediaType;
    import org.restlet.representation.StringRepresentation;
    import org.restlet.routing.Router;
    
    public class MyApplication extends Application {
    
        public MyApplication() {
            super();
        }
    
        public MyApplication(Context parentContext) {
            super(parentContext);
        }
    
        public Restlet createInboundRoot() {
            Router router = new Router(getContext());
    
            router.attach("/hello", HelloResource.class);
    
      Restlet mainpage = new Restlet() {
                @Override
                public void handle(Request request, Response response) {
                    StringBuilder stringBuilder = new StringBuilder();
    
                    stringBuilder.append("<html>");
                    stringBuilder.append("<head><title>Hello Application " +
                            "Servlet Page</title></head>");
                    stringBuilder.append("<body bgcolor=white>");
                    stringBuilder.append("<a href=\"app/hello\">hello</a> --> returns hello world message " +
                            "and date string");
                    stringBuilder.append("</body>");
                    stringBuilder.append("</html>");
                    response.setEntity(new StringRepresentation(
                            stringBuilder.toString(),
                            MediaType.TEXT_HTML));
                }
            };
            router.attach("", mainpage);
    
            return router;
        }
    
    }
    

    web.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
        id="WebApp_ID" version="2.5">
      <display-name>Archetype Created Web Application</display-name>
    
             <servlet>
                     <servlet-name>Restlet</servlet-name>
                     <servlet-class>org.restlet.ext.servlet.ServerServlet</servlet-class>
                     <init-param>
                             <param-name>org.restlet.application</param-name>
                             <param-value>com.sample.MyApplication</param-value>
                     </init-param>
             </servlet>
    
        <servlet-mapping>
            <servlet-name>Restlet</servlet-name>
            <url-pattern>/app/*</url-pattern>
        </servlet-mapping>  
    </web-app>
    

    HelloResource.java:

    package com.sample;
    
    import java.util.Calendar;
    
    import org.restlet.Context;
    import org.restlet.Request;
    import org.restlet.Response;
    import org.restlet.data.MediaType;
    import org.restlet.representation.Representation;
    import org.restlet.representation.StringRepresentation;
    import org.restlet.representation.Variant;
    import org.restlet.resource.ResourceException;
    import org.restlet.resource.ServerResource;
    
    public class HelloResource extends ServerResource {
    
        public HelloResource() {
            super();
        }
        public HelloResource(Context context,
                              Request request,
                              Response response) {     
            getVariants().add(new Variant(MediaType.TEXT_PLAIN));
        }
    
        @Override
        protected Representation get() throws ResourceException {
            String message = "Hello World!" +
            " \n\nTime of request is:"
            + Calendar.getInstance()
            .getTime().toString();
    
            return new StringRepresentation(message,
            MediaType.TEXT_PLAIN);
        }
    
    }
    

    pom.xml:

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.sample</groupId>
        <artifactId>testwar</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>war</packaging>
        <repositories>
            <repository>
               <id>maven-restlet</id>
               <name>Public online Restlet repository</name>
               <url>http://maven.restlet.org</url>
            </repository>
        </repositories>
    
        <pluginRepositories>
          <pluginRepository>
            <id>numberformat-releases</id>
            <url>https://raw.github.com/numberformat/20130213/master/repo</url>
          </pluginRepository>
        </pluginRepositories>
    
      <dependencies>
    
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>servlet-api</artifactId>
                <version>2.5</version>
                <scope>provided</scope>
            </dependency>
    
        <dependency>
           <groupId>org.restlet.jse</groupId>
           <artifactId>org.restlet</artifactId>
           <version>2.0.0</version>
        </dependency>
        <dependency>
           <groupId>org.restlet.jse</groupId>
           <artifactId>org.restlet.ext.simple</artifactId>
           <version>2.0.0</version>
        </dependency>
        <dependency>
           <groupId>org.restlet.jee</groupId>
           <artifactId>org.restlet.ext.servlet</artifactId>
           <version>2.0.0</version>
        </dependency>
            <dependency>
                <groupId>org.apache.geronimo.specs</groupId>
                <artifactId>geronimo-servlet_2.5_spec</artifactId>
                <version>1.2</version>
                <scope>provided</scope>
            </dependency>
      </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>2.0.2</version>
                    <configuration>
                        <source>1.5</source>
                        <target>1.5</target>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.mortbay.jetty</groupId>
                    <artifactId>jetty-maven-plugin</artifactId>
                    <version>7.0.0.v20091005</version>
                    <configuration>
                        <scanIntervalSeconds>2</scanIntervalSeconds>
                    </configuration>
                </plugin>
                <plugin>        
                    <groupId>github.numberformat</groupId>
                    <artifactId>blog-plugin</artifactId>
                    <version>1.0-SNAPSHOT</version>
                    <configuration>
                    <gitUrl>https://github.com/numberformat/20110220</gitUrl>
                    </configuration>
                <executions>
                  <execution>
                    <id>1</id>
                    <phase>site</phase>
                    <goals>
                      <goal>generate</goal>
                    </goals>            
                  </execution>
                </executions>
                </plugin>
            </plugins>
    
            <finalName>testwar</finalName>
        </build>
    
    </project>
    

    Goto the project's root folder and execute using: mvn clean compile jetty:run

    Hope this helps