0

I have an application which offers a frontend but should also allow API calls (modifying the application). Therefore I have an Index class which redirects to the frontend when the context is not similar to an API call url and otherwise doesn't redirect. However, the api call doesn't reach the Jersey API handler but simply displays the default layout.

In my Index class I have:

Object onActivate(EventContext context) {
    if (context.getCount() < 2)
        return WorkspaceOverview.class;
    String first = context.get(String.class, 0);
    String second = context.get(String.class, 1);
    return "api".equalsIgnoreCase(first) &&
            Arrays.stream(allowedApiCalls).anyMatch(s -> s.equalsIgnoreCase(second))
            ? null
            : WorkspaceOverview.class;
}

In the API class I have (with different namings but generally the same):

@Path("/api")
@Api(description = "description")
@Generated(value = "io.swagger.codegen.languages.JavaJerseyServerCodegen", date = "2018-10-29T15:10:52.958Z")
public class Api {
    private ApiService getDelegate() {
        return (ApiService) Bootstrap.getRegistry().getService(ApiService.class);
    }

    @PUT
    @Path("/request")
    @ApiOperation(value = "handles", notes = "notes", response = Void.class, authorizations = {
            @Authorization(value = "basicAuth") }, tags = {})
    @ApiResponses(value = {    
            @ApiResponse(code = 200, message = "The request update was successful", response = Void.class),
            @ApiResponse(code = 400, message = "An unknown error occurred", response = Void.class),
            @ApiResponse(code = 401, message = "Authentication information is missing or invalid", response = Void.class),
            @ApiResponse(code = 404, message = "The request doesn't exist", response = Void.class) })
    public Response request(
            @ApiParam(value = "The of the request", required = true) @QueryParam("id") Long id,
            @ApiParam(value = "value", required = true, allowableValues = "A, B, C") @QueryParam("value") String value,
            @Context SecurityContext securityContext, @Context HttpHeaders headers, @Context HttpServletRequest request) throws NotFoundException {
        String authorizationHeader = headers.getHeaderString("Authorization");
        String requestHost = headers.getHeaderString("Host");
        String remoteHost = request.getRemoteHost();
        return getDelegate().handleRequest(id, value, securityContext, authorizationHeader, requestHost, remoteHost);
    }
}

And in my pom (drastically shortened):

<project
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0">

  <artifactId>Project</artifactId>

  <packaging>war</packaging>

  <name>Project</name>

    <dependency>
      <groupId>org.apache.tapestry</groupId>
      <artifactId>tapestry-core</artifactId>
    </dependency>
    <dependency>
      <groupId>org.apache.tapestry</groupId>
      <artifactId>tapestry-test</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.tapestry</groupId>
      <artifactId>tapestry-javadoc</artifactId>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.glassfish</groupId>
      <artifactId>javax.json</artifactId>
    </dependency>
    <dependency>
      <groupId>io.swagger</groupId>
      <artifactId>swagger-jersey2-jaxrs</artifactId>
      <scope>compile</scope>
      <version>${swagger-core-version}</version>
    </dependency>
    <dependency>
      <groupId>io.swagger</groupId>
      <artifactId>swagger-annotations</artifactId>
      <version>${swagger-core-version}</version>
    </dependency>
    <dependency>
      <groupId>com.squareup.okhttp</groupId>
      <artifactId>okhttp</artifactId>
      <version>${okhttp-version}</version>
    </dependency>
    <dependency>
      <groupId>com.squareup.okhttp</groupId>
      <artifactId>logging-interceptor</artifactId>
      <version>${okhttp-version}</version>
    </dependency>
    <dependency>
      <groupId>org.glassfish.jersey.containers</groupId>
      <artifactId>jersey-container-servlet-core</artifactId>
      <version>${jersey2-version}</version>
    </dependency>
    <dependency>
      <groupId>org.glassfish.jersey.media</groupId>
      <artifactId>jersey-media-multipart</artifactId>
      <version>${jersey2-version}</version>
    </dependency>
    <dependency>
        <groupId>de.mkammerer</groupId>
        <artifactId>argon2-jvm</artifactId>
    </dependency>

    <dependency>
        <groupId>org.apache.tapestry</groupId>
        <artifactId>tapestry-upload</artifactId>
        <version>${tapestry-release-version}</version>
    </dependency>

    <dependency>
      <groupId>org.apache.tapestry</groupId>
      <artifactId>tapestry-csrf-protection</artifactId>
      <version>${tapestry-csrf.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.tapestry</groupId>
      <artifactId>tapestry-kaptcha</artifactId>
      <version>${tapestry-release-version}</version>
    </dependency>
  </dependencies>

  <build>
    <finalName>Project</finalName>


  <properties>
    <jetty-maven-version>9.2.21.v20170120</jetty-maven-version>
    <tapestry-release-version>5.4.3</tapestry-release-version>
    <servlet-api-release-version>2.5</servlet-api-release-version>
    <testng-release-version>5.14.10</testng-release-version>
    <tapestry-security-version>0.6.6</tapestry-security-version>
    <swagger-core-version>1.5.15</swagger-core-version>
    <okhttp-version>2.7.5</okhttp-version>
    <gson-version>2.8.1</gson-version>
    <jersey2-version>2.22.2</jersey2-version>
    <maven.compiler.target>1.7</maven.compiler.target>
    <maven.compiler.source>1.7</maven.compiler.source>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <tapestry-csrf.version>1.2.0.RELEASE</tapestry-csrf.version>
  </properties>
</project>

And the web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
        PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>    
    <context-param>
        <!-- The only significant configuration for Tapestry 5, this informs Tapestry of where to look for pages, components and mixins. -->
        <param-name>tapestry.app-package</param-name>
        <param-value>the.package.to.the.application</param-value>
    </context-param>
    <context-param>
        <param-name>tapestry.secure-enabled</param-name>
        <param-value>false</param-value>
    </context-param>

    <filter>
        <filter-name>ServletFilter</filter-name>
        <filter-class>the.package.of.the.filter.Filter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>ServletFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter>
        <filter-name>app</filter-name>
        <filter-class>org.apache.tapestry5.TapestryFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>app</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <listener>
        <listener-class>
             the.package.of.the.listener.Listener
        </listener-class>
    </listener>

    <session-config>
      <tracking-mode>COOKIE</tracking-mode>
      <session-timeout>-1</session-timeout>
    </session-config>

    <!--API-->
    <servlet>
        <servlet-name>jersey</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>
                io.swagger.jaxrs.listing,
                io.swagger.sample.resource,
                the.package.to.the.api
            </param-value>
        </init-param>
        <init-param>
            <param-name>jersey.config.server.provider.classnames</param-name>
            <param-value>org.glassfish.jersey.media.multipart.MultiPartFeature</param-value>
        </init-param>
        <init-param>
            <param-name>jersey.config.server.wadl.disableWadl</param-name>
            <param-value>true</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet>
        <servlet-name>Jersey2Config</servlet-name>
        <servlet-class>io.swagger.jersey.config.JerseyJaxrsConfig</servlet-class>
        <init-param>
            <param-name>api.version</param-name>
            <param-value>1.0.0</param-value>
        </init-param>
        <init-param>
            <param-name>swagger.api.title</param-name>
            <param-value>Swagger Server</param-value>
        </init-param>
        <init-param>
            <param-name>swagger.api.basepath</param-name>
            <param-value>https://localhost</param-value>
        </init-param>

        <load-on-startup>2</load-on-startup>
    </servlet>

    <servlet>
        <servlet-name>Bootstrap</servlet-name>
        <servlet-class>the.package.to.the.bootstrap.Bootstrap</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Bootstrap</servlet-name>
        <url-pattern>the.package.to.the.api.*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>jersey</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
    <filter>
        <filter-name>ApiOriginFilter</filter-name>
        <filter-class>the.package.of.the.filter.Filter
        </filter-class>
    </filter>
    <filter-mapping>
        <filter-name>ApiOriginFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

An example request would be:

curl -X PUT "localhost:8080/api/request?id=0&vaue=A" -H  "accept: application/json" -H  "authorization: Basic dXNlcjoxMjM0d" -k -v

But the result is always 200 with the html code of the DefaultLayout page.

If there's some minor mistake in the source code which would cause a comipilation error let me know, I had to modify the code not to reveal any secrets.

I think that's all relevant files, if you need more information, let me know in the comments. I'm sorry for not being able to provide a minimal, working example, but I simply don't know which files / parts of code are relevant.

MetaColon
  • 2,895
  • 3
  • 16
  • 38
  • The key was to not run the filter chain on a request filter (it's not in the code snippets I provided) when the request contained something API-like, so that these requests would be directed to the API. – MetaColon Feb 16 '19 at 00:54

1 Answers1

0

Looks like the problem is in your <url-pattern>/*</url-pattern> -- they all watch the same pattern, so the first wins.

If all your API endpoints live under /api/* then update URL pattern accordingly, and move it before Tapestry filter.

Dmitry Gusev
  • 881
  • 8
  • 15
  • I thought so too, but adjusting the patterns didn't work – MetaColon Feb 21 '19 at 12:32
  • maybe that's because you have both servlet and filter mappings, as described here: https://stackoverflow.com/a/7938118/2414933 " if you hit an URL that's covered by both `` and `` bound filters ... then all URL-pattern bound filters are applied before all servlet-name bound filters" – Dmitry Gusev Feb 27 '19 at 10:04