Uploaded image for project: 'RESTEasy'
  1. RESTEasy
  2. RESTEASY-1709

Same JAX-RS Application instance injected across applications

    XMLWordPrintable

Details

    • Bug
    • Resolution: Unresolved
    • Critical
    • None
    • 3.0.19.Final, 3.6.2.Final
    • CDI Integration
    • None
    • Hide
      1. Create a JAX-RS app that uses Weld
      2. Add a jar file defining an Application subclass and only explicitly exporting a resource and a filter, as above.
      3. Add a @Context annotated Application field to the filter class
      4. Whenever the filter gets invoked the injected Application instance will be the same, regardless of which app is actually hit.

      You can also use WF (no wf-swarm) and this application:

      @ApplicationPath("/first")
      public class FirstApplication extends Application {
          @Override
          public Set<Class<?>> getClasses() {
              Set<Class<?>> resources = new HashSet<>();
              resources.add(TestResource.class);
              resources.add(TestFilter.class);
              return resources;
          }
          @Override
          public String toString() {
              return "FirstApplication{}";
          }
      }
      @ApplicationPath("/second")
      public class SecondApplication extends Application {
          @Override
          public Set<Class<?>> getClasses() {
              Set<Class<?>> resources = new HashSet<>();
              resources.add(TestResource.class);
              resources.add(TestFilter.class);
              return resources;
          }
          @Override
          public String toString() {
              return "SecondApplication{}";
          }
      }
      @Provider
      public class TestFilter implements ContainerRequestFilter, ContainerResponseFilter {
          @Inject
          CustomBean bean;
          @Context
          Application application;
          @Override
          public void filter(ContainerRequestContext requestContext) throws IOException {
              System.out.println("Request filter");
              System.out.println(application.toString());
              bean.print();
          }
          @Override
          public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
              System.out.println("Response filter");
              System.out.println(application.toString());
              bean.print();
          }
      }
      @Path("/")
      public class TestResource {
          @Path("a")
          @GET
          public String a() {
              return "a";
          }
      }
      @RequestScoped
      public class CustomBean {
          public void print() {
              System.out.println("Print from bean");
          }
      }
      
      <web-app></web-app>
      
      curl -X GET "http://localhost:8080/jaxrs-wf/first/a"
      curl -X GET "http://localhost:8080/jaxrs-wf/second/a"
      

      WF:

      # curl -X GET "http://localhost:8080/jaxrs-wf/first/a"
      
      08:25:38,309 INFO  [stdout] (default task-1) Request filter
      08:25:38,309 INFO  [stdout] (default task-1) FirstApplication{}
      08:25:38,309 INFO  [stdout] (default task-1) Print from bean
      08:25:38,314 INFO  [stdout] (default task-1) Response filter
      08:25:38,314 INFO  [stdout] (default task-1) FirstApplication{}
      08:25:38,314 INFO  [stdout] (default task-1) Print from bean
      
      # curl -X GET "http://localhost:8080/jaxrs-wf/second/a"
      
      08:25:42,378 INFO  [stdout] (default task-1) Request filter
      08:25:42,379 INFO  [stdout] (default task-1) FirstApplication{}
      08:25:42,379 INFO  [stdout] (default task-1) Print from bean
      08:25:42,379 INFO  [stdout] (default task-1) Response filter
      08:25:42,380 INFO  [stdout] (default task-1) FirstApplication{}
      08:25:42,380 INFO  [stdout] (default task-1) Print from bean
      

      Payara (with Jersey, reference jax-rs implementation):

      # curl -X GET "http://localhost:8080/jaxrs-wf/first/a"
      
        Request filter
        org.glassfish.jersey.server.ResourceConfig$WrappingResourceConfig@1679f1eb
        Print from bean
        Response filter
        org.glassfish.jersey.server.ResourceConfig$WrappingResourceConfig@1679f1eb
        Print from bean
      
      # curl -X GET "http://localhost:8080/jaxrs-wf/second/a"
      
        Request filter
        org.glassfish.jersey.server.ResourceConfig$WrappingResourceConfig@261dfb0d
        Print from bean
        Response filter
        org.glassfish.jersey.server.ResourceConfig$WrappingResourceConfig@261dfb0d
        Print from bean
      
      Show
      Create a JAX-RS app that uses Weld Add a jar file defining an Application subclass and only explicitly exporting a resource and a filter, as above. Add a @Context annotated Application field to the filter class Whenever the filter gets invoked the injected Application instance will be the same, regardless of which app is actually hit. You can also use WF (no wf-swarm) and this application: @ApplicationPath( "/first" ) public class FirstApplication extends Application { @Override public Set< Class <?>> getClasses() { Set< Class <?>> resources = new HashSet<>(); resources.add(TestResource.class); resources.add(TestFilter.class); return resources; } @Override public String toString() { return "FirstApplication{}" ; } } @ApplicationPath( "/second" ) public class SecondApplication extends Application { @Override public Set< Class <?>> getClasses() { Set< Class <?>> resources = new HashSet<>(); resources.add(TestResource.class); resources.add(TestFilter.class); return resources; } @Override public String toString() { return "SecondApplication{}" ; } } @Provider public class TestFilter implements ContainerRequestFilter, ContainerResponseFilter { @Inject CustomBean bean; @Context Application application; @Override public void filter(ContainerRequestContext requestContext) throws IOException { System .out.println( "Request filter" ); System .out.println(application.toString()); bean.print(); } @Override public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException { System .out.println( "Response filter" ); System .out.println(application.toString()); bean.print(); } } @Path( "/" ) public class TestResource { @Path( "a" ) @GET public String a() { return "a" ; } } @RequestScoped public class CustomBean { public void print() { System .out.println( "Print from bean" ); } } <web-app> </web-app> curl -X GET "http://localhost:8080/jaxrs-wf/first/a" curl -X GET "http://localhost:8080/jaxrs-wf/second/a" WF: # curl -X GET "http://localhost:8080/jaxrs-wf/first/a" 08:25:38,309 INFO [stdout] (default task-1) Request filter 08:25:38,309 INFO [stdout] (default task-1) FirstApplication{} 08:25:38,309 INFO [stdout] (default task-1) Print from bean 08:25:38,314 INFO [stdout] (default task-1) Response filter 08:25:38,314 INFO [stdout] (default task-1) FirstApplication{} 08:25:38,314 INFO [stdout] (default task-1) Print from bean # curl -X GET "http://localhost:8080/jaxrs-wf/second/a" 08:25:42,378 INFO [stdout] (default task-1) Request filter 08:25:42,379 INFO [stdout] (default task-1) FirstApplication{} 08:25:42,379 INFO [stdout] (default task-1) Print from bean 08:25:42,379 INFO [stdout] (default task-1) Response filter 08:25:42,380 INFO [stdout] (default task-1) FirstApplication{} 08:25:42,380 INFO [stdout] (default task-1) Print from bean Payara (with Jersey, reference jax-rs implementation): # curl -X GET "http://localhost:8080/jaxrs-wf/first/a" Request filter org.glassfish.jersey.server.ResourceConfig$WrappingResourceConfig@1679f1eb Print from bean Response filter org.glassfish.jersey.server.ResourceConfig$WrappingResourceConfig@1679f1eb Print from bean # curl -X GET "http://localhost:8080/jaxrs-wf/second/a" Request filter org.glassfish.jersey.server.ResourceConfig$WrappingResourceConfig@261dfb0d Print from bean Response filter org.glassfish.jersey.server.ResourceConfig$WrappingResourceConfig@261dfb0d Print from bean

    Description

      [I'm relatively new to WildFly/Weld/JAX-RS, so please bear with me. I will try to provide all other information you need, just ask.]

      We have a JAX-RS webapp that uses Weld for dependency injection. It doesn't define an explicit Application class, so Weld generates a org.wildfly.swarm.generated.WildFlySwarmDefaultJAXRSApplication$Proxy$_$$_WeldClientProxy instance mapped to the '/' path. Requests get mapped directly to the path defined by resources, e.g. @Path("/api/foo"). So far so good.

      I'm now trying to drop a jar with an explicitly defined Application into the mix. This is defined as:

      @ApplicationPath("/metrics")
      @Monitored  // A custom JAX-RS name binding annotation
      public class MetricsApplication extends Application {
        @Override
        public Set<Class<?>> getClasses() {
          Set<Class<?>> resources = new HashSet<>();
          // Only export MetricsResource as a resource.
          resources.add(MetricsResource.class);
          // Also measure latency for /metrics.
          resources.add(MonitoringFilter.class);
          return resources;
        }
      }
      

      This second application exports a single resource, annotated with @Path("/"), so it ends up handling the /metrics path. It also registers a request and response filter that measures latency. It should intercept all applications and/or resources annotated with @Monitored and it does it perfectly in another webapp that doesn't use Weld, only WildFly. It looks like this:

      @Provider
      @Monitored
      public class MonitoringFilter
          implements ContainerRequestFilter, ContainerResponseFilter {
      
        /** The application context, used for retrieving the {@link ApplicationPath} value. */
        @Context
        Application application;
      
        // [...]
      }
      

      In the non-Weld webapp the injected Application instance is always correct (i.e. it's a MetricsApplication for a /metrics request, the webapp's ServiceAplication class for all other requests.

      In the Weld-using webapp the injected Application instance is always the same, regardless of request: one of MetricsApplication and the Weld generated proxy. It seems to me that which of the two is injected depends on the hash of the class name, because as soon as I add an explicit Aplication subclass to the app I always get the MetricsApplication injected. Without it, I always get the Weld default application proxy.

      Interestingly enough, Weld never generates a proxy for MetricsApplication but it always does for ServiceApplication (if I define one) so I can never retrieve the @ApplicationPath annotation of the ServiceApplication (because the proxy doesn't preserve it).

      Help? Please?

      Attachments

        Activity

          People

            rsigal@redhat.com Ronald Sigal
            alin.sinpalean Alin Sinpalean (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

            Dates

              Created:
              Updated: