0
votes

I use RestEasy with embedded Tomcat to export runnable jar file which deploy some Rest API (just for testing, I don't want to use SpringBoots). I've write some code in main entry point to register HttpServletDispatcher

public class Main {

public static void main(String[] args) throws Exception {
    try {
        new Main().run();
    } catch (Throwable t) {
        t.printStackTrace();
    }
}

    public void run() throws Exception {
        Tomcat tomcat = new Tomcat();
        tomcat.setPort(8080);

        Context ctx = tomcat.addContext("/", new File(".").getAbsolutePath());
        String contextPath = "/";
        String appBase = ".";
        tomcat.getHost().setAppBase(appBase);
        tomcat.addWebapp(contextPath, appBase);

        Tomcat.addServlet(ctx, "rest-easy-servlet", new HttpServletDispatcher());
        ctx.addServletMapping("/*", "rest-easy-servlet");
        tomcat.start();
        tomcat.getServer().await();
    }
}

Server starts successfully but without any Rest API. When access to the path localhost:8080/users/hello I've got error in log.

Nov 09, 2017 2:21:22 PM org.apache.coyote.AbstractProtocol start INFO: Starting ProtocolHandler ["http-nio-8080"] Nov 09, 2017 2:21:30 PM org.jboss.resteasy.core.ExceptionHandler SEVERE: failed to execute javax.ws.rs.NotFoundException: Could not find resource for full path: http://localhost:8080/users/hello at org.jboss.resteasy.core.registry.ClassNode.match(ClassNode.java:73) at org.jboss.resteasy.core.registry.RootClassNode.match(RootClassNode.java:48) at org.jboss.resteasy.core.ResourceMethodRegistry.getResourceInvoker(ResourceMethodRegistry.java:444) at org.jboss.resteasy.core.SynchronousDispatcher.getInvoker(SynchronousDispatcher.java:234) at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:171) at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:220) at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56) at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51) at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:217) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:673) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:745)

I think RestEasy don't have any signal to scan its components. Please help me the way to scan RestEasy components, or the way to register RestEasy same as using web.xml

2

2 Answers

2
votes

Resources and providers won't be automatically scanned when using an embedded Tomcat. You must register them manually either in an Application subclass or in the resteasy.resources context parameter using comma separated values.

First of all, ensure you have the following dependencies in your project:

<properties>
    <tomcat.version>8.5.23</tomcat.version>
    <resteasy.version>3.1.4.Final</resteasy.version>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-core</artifactId>
        <version>${tomcat.version}</version>
    </dependency>
    <dependency>
        <groupId>org.jboss.resteasy</groupId>
        <artifactId>resteasy-jaxrs</artifactId>
        <version>${resteasy.version}</version>
    </dependency>
</dependencies>

Create a JAX-RS resource class:

@Path("hello")
public class HelloWorldResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public Response helloWorld() {
        return Response.ok("Hello World!").build();
    }
}

Create an Application subclass and register the resource class create above:

public class MyApplication extends Application {

    @Override
    public Set<Class<?>> getClasses() {
        return Collections.singleton(HelloWorldResource.class);
    }
}

Create a class to launch Tomcat and deploy your application:

public class Launcher {

    private static final String RESTEASY_SERVLET_NAME = "resteasy-servlet";

    public static void main(String[] args) throws Exception {
        new Launcher().start();
    }

    void start() throws Exception {

        String port = System.getenv("PORT");
        if (port == null || port.isEmpty()) {
            port = "8080";
        }

        String contextPath = "/api";
        String appBase = ".";

        Tomcat tomcat = new Tomcat();
        tomcat.setPort(Integer.valueOf(port));
        tomcat.getHost().setAppBase(appBase);

        Context context = tomcat.addContext(contextPath, appBase);
        context.addApplicationListener(ResteasyBootstrap.class.getName());
        Tomcat.addServlet(context, RESTEASY_SERVLET_NAME, new HttpServletDispatcher());
        context.addParameter("javax.ws.rs.Application", MyApplication.class.getName());
        context.addServletMappingDecoded("/*", RESTEASY_SERVLET_NAME);

        tomcat.start();
        tomcat.getServer().await();
    }
}

Finally add the Maven Shade plugin to create an executable JAR, where the mainClass attribute references the above create class:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>2.4.3</version>
            <configuration>
                <finalName>tomcat-embedded-example-${project.version}</finalName>
            </configuration>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <transformers>
                            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <mainClass>com.example.Launcher</mainClass>
                            </transformer>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

To compile and run the application, follow these steps:

  • Open a command line window or terminal.
  • Navigate to the root directory of the project, where the pom.xml resides.
  • Compile the project: mvn clean compile.
  • Package the application: mvn package.
  • Look in the target directory. You should see a file with the following or a similar name: tomcat-embedded-sample-1.0-SNAPSHOT.jar.
  • Change into the target directory.
  • Execute the JAR: java -jar tomcat-embedded-example-1.0-SNAPSHOT.jar.
  • The application is available at http://localhost:8080/api/hello.

See more:

0
votes

Your Rest Controller might not be detected/scanned automatically - as you suggested. You could try to manually register your class which exposes the "users/" resource.