Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  package com.jetdrone.vertx.yoke.middleware;
  
  import static org.vertx.java.core.http.HttpHeaders.CONTENT_LENGTH;
  
  import java.net.URI;
 import java.util.List;
 
 
 

A middleware that can forward requests to JAX-RS annotated resources.

Use it as the last middleware and do not use it with a body consuming middleware!

Don't forget to add Jersey to your project's classpath! For Maven, this would be done with this addition to the POM:

 <dependency>
   <groupId>org.glassfish.jersey.core</groupId>
   <artifactId>jersey-server</artifactId>
   <version>2.11</version>
 </dependency>
 
 

Example usage:

 yoke.use(new ErrorHandler(false))
     .use(new Timeout(10000L))
     .use(new Logger())
     .use(new Favicon())
     .use(new Router().get("/other/", r -> handleGetOther(r)))
     .use(new Jersey()
              .withPackages("com.acme.jaxrs.resources")
              .withInjectables(dependency1, dependency2));
 
 

The context classes available in the JAX-RS resources are: com.jetdrone.vertx.yoke.middleware.YokeRequest, com.jetdrone.vertx.yoke.middleware.YokeResponse, org.vertx.java.core.Vertx and org.vertx.java.platform.Container (only if provided with withVertxContainer(org.vertx.java.platform.Container)).

Any object provided with withInjectables(java.lang.Object[]) will be available via a standard javax.inject.Inject annotation.

Implementation inspired by Jersey's SimpleContainer and Englishtown's Jersey Mod.

public class Jersey extends Middleware implements Container
    private static final Logger LOGGER = LoggerFactory.getLogger(Jersey.class);
    private static class YokeOutputStream extends OutputStream
    {
        final YokeResponse response;
        Buffer buffer = new Buffer();
        boolean isClosed;
        private YokeOutputStream(final YokeResponse response)
        {
            this. = response;
        }
        @Override
        public void write(final int bthrows IOException
        {
            checkState();
            .appendByte((byteb);
        }
        @Override
        public void write(final byte[] bthrows IOException
        {
            checkState();
            .appendBytes(b);
        }
        @Override
        public void write(final byte[] bfinal int offfinal int lenthrows IOException
        {
            checkState();
            if (off == 0 && len == b.length)
            {
                .appendBytes(b);
            }
            else
            {
                .appendBytes(Arrays.copyOfRange(boffoff + len));
            }
        }
        @Override
        public void flush() throws IOException
        {
            checkState();
            // Only flush to underlying very.x response if the content-length has been set
            if (.length() > 0 && .headers().contains())
            {
                .write();
                 = new Buffer();
            }
        }
        @Override
        public void close() throws IOException
        {
            // Write any remaining buffer to the vert.x response
            // Set content-length if not set yet
            if ( != null && .length() > 0)
            {
                if (!.headers().contains(.))
                {
                    .headers().add(., String.valueOf(.length()));
                }
                .write();
            }
             = null;
             = true;
        }
        void checkState()
        {
            if ()
            {
                throw new IllegalStateException("Stream is closed");
            }
        }
    }
    private static class YokeChunkedOutputStream extends OutputStream
    {
        private final YokeResponse response;
        private boolean isClosed;
        private YokeChunkedOutputStream(final YokeResponse response)
        {
            this. = response;
        }
        @Override
        public void write(final int bthrows IOException
        {
            checkState();
            final Buffer buffer = new Buffer();
            buffer.appendByte((byteb);
            .write(buffer);
        }
        @Override
        public void write(final byte[] bthrows IOException
        {
            checkState();
            .write(new Buffer(b));
        }
        @Override
        public void write(final byte[] bfinal int offfinal int lenthrows IOException
        {
            checkState();
            final Buffer buffer = new Buffer();
            if (off == 0 && len == b.length)
            {
                buffer.appendBytes(b);
            }
            else
            {
                buffer.appendBytes(Arrays.copyOfRange(boffoff + len));
            }
            .write(buffer);
        }
        @Override
        public void close() throws IOException
        {
             = true;
        }
        void checkState()
        {
            if ()
            {
                throw new IllegalStateException("Stream is closed");
            }
        }
    }
    private static class YokePrincipal implements Principal
    {
        private final String user;
        public YokePrincipal(final String user)
        {
            if (user == null)
            {
                throw new IllegalArgumentException("user can't be null");
            }
            this. = user;
        }
        @Override
        public String getName()
        {
            return ;
        }
        @Override
        public String toString()
        {
            return getClass().getSimpleName() + ":" + ;
        }
        @Override
        public int hashCode()
        {
            return 31 + (( == null) ? 0 : .hashCode());
        }
        @Override
        public boolean equals(final Object obj)
        {
            if (this == objreturn true;
            if (obj == nullreturn false;
            if (getClass() != obj.getClass()) return false;
            final YokePrincipal other = (YokePrincipalobj;
            if ( == null)
            {
                if (other.user != nullreturn false;
            }
            else if (!.equals(other.user)) return false;
            return true;
        }
    }
    private static final Type YOKE_REQUEST_TYPE = (new TypeLiteral<Ref<YokeRequest>>()
    {
    }).getType();
    private static final Type YOKE_RESPONSE_TYPE = (new TypeLiteral<Ref<YokeResponse>>()
    {
    }).getType();
    private static final Type VERTX_TYPE = (new TypeLiteral<Ref<Vertx>>()
    {
    }).getType();
    private static final Type CONTAINER_TYPE = (new TypeLiteral<Ref<org.vertx.java.platform.Container>>()
    {
    }).getType();
    private static class YokeRequestReferencingFactory extends ReferencingFactory<YokeRequest>
    {
        @Inject
        public YokeRequestReferencingFactory(final Provider<Ref<YokeRequest>> referenceFactory)
        {
            super(referenceFactory);
        }
    }
    private static class YokeResponseReferencingFactory extends ReferencingFactory<YokeResponse>
    {
        @Inject
        public YokeResponseReferencingFactory(final Provider<Ref<YokeResponse>> referenceFactory)
        {
            super(referenceFactory);
        }
    }
    private static class VertxReferencingFactory extends ReferencingFactory<Vertx>
    {
        @Inject
        public VertxReferencingFactory(final Provider<Ref<Vertx>> referenceFactory)
        {
            super(referenceFactory);
        }
    }
    private static class ContainerReferencingFactory extends
    {
        @Inject
        public ContainerReferencingFactory(final Provider<Ref<org.vertx.java.platform.Container>> referenceFactory)
        {
            super(referenceFactory);
        }
    }
    private static class YokeBinder extends AbstractBinder
    {
        @Override
        protected void configure()
        {
            bindFactory(YokeRequestReferencingFactory.class).to(YokeRequest.class)
                .proxy(true)
                .proxyForSameScope(false)
                .in(RequestScoped.class);
            bindFactory(ReferencingFactory.<YokeRequestreferenceFactory()).to(
                new TypeLiteral<Ref<YokeRequest>>()
                {
                }).in(RequestScoped.class);
            bindFactory(YokeResponseReferencingFactory.class).to(YokeResponse.class)
                .proxy(true)
                .proxyForSameScope(false)
                .in(RequestScoped.class);
            bindFactory(ReferencingFactory.<YokeResponsereferenceFactory()).to(
                new TypeLiteral<Ref<YokeResponse>>()
                {
                }).in(RequestScoped.class);
            bindFactory(VertxReferencingFactory.class).to(Vertx.class)
                .proxy(true)
                .proxyForSameScope(false)
                .in(RequestScoped.class);
            bindFactory(ReferencingFactory.<VertxreferenceFactory()).to(new TypeLiteral<Ref<Vertx>>()
            {
            }).in(RequestScoped.class);
                .proxy(true)
                .proxyForSameScope(false)
                .in(RequestScoped.class);
            bindFactory(ReferencingFactory.<org.vertx.java.platform.ContainerreferenceFactory()).to(
                new TypeLiteral<Ref<org.vertx.java.platform.Container>>()
                {
                }).in(RequestScoped.class);
        }
    }
    private static final class YokeResponseWriter implements ContainerResponseWriter
    {
        private final YokeResponse response;
        private final Vertx vertx;
        private TimeoutHandler timeoutHandler;
        private long suspendTimerId;
        public YokeResponseWriter(final YokeResponse responsefinal Vertx vertx)
        {
            this. = response;
            this. = vertx;
            this. = 0;
        }
        @Override
        public OutputStream writeResponseStatusAndHeaders(final long contentLength,
                                                          final ContainerResponse responseContext)
            throws ContainerException
        {
            .setStatusCode(responseContext.getStatus());
            .setStatusMessage(responseContext.getStatusInfo().getReasonPhrase());
            if (contentLength != -1)
            {
                .putHeader(, Long.toString(contentLength));
            }
            for (final Entry<StringList<String>> header : responseContext.getStringHeaders().entrySet())
            {
                for (final String value : header.getValue())
                {
                    .putHeader(header.getKey(), value);
                }
            }
            if (responseContext.isChunked())
            {
                .setChunked(true);
                return new YokeChunkedOutputStream();
            }
            else
            {
                return new YokeOutputStream();
            }
        }
        @Override
        public void commit()
        {
            endResponse();
        }
        @Override
        public void failure(final Throwable t)
        {
            .error(t.getMessage(), t);
            try
            {
                .setStatusCode(500);
                .setStatusMessage("Internal Server Error");
                .end();
            }
            catch (final Exception e)
            {
                .error("Failed to write failure response"e);
            }
        }
        @Override
        public boolean suspend(final long timeOut,
                               final TimeUnit timeUnit,
                               final TimeoutHandler timeoutHandler)
        {
            if (timeoutHandler == null)
            {
                throw new IllegalArgumentException("TimeoutHandler can't be null");
            }
            // if already suspended should return false according to documentation
            if (this. != null)
            {
                return false;
            }
            this. = timeoutHandler;
            doSuspend(timeOuttimeUnit);
            return true;
        }
        @Override
        public void setSuspendTimeout(final long timeOutfinal TimeUnit timeUnit)
            throws IllegalStateException
        {
            if ( == null)
            {
                throw new IllegalStateException("Request not currently suspended");
            }
            if ( != 0)
            {
                .cancelTimer();
            }
            doSuspend(timeOuttimeUnit);
        }
        private void doSuspend(final long timeOutfinal TimeUnit timeUnit)
        {
            // if timeout <= 0, then it suspends indefinitely
            if (timeOut <= 0)
            {
                return;
            }
            final long ms = timeUnit.toMillis(timeOut);
             = .setTimer(msnew Handler<Long>()
            {
                @Override
                public void handle(final Long $)
                {
                    YokeResponseWriter.this..onTimeout(YokeResponseWriter.this);
                }
            });
        }
        @Override
        public boolean enableResponseBuffering()
        {
            return false;
        }
    }
    private final ResourceConfig resourceConfig;
    public Jersey()
    {
         = new ResourceConfig();
    }
    public Jersey withVertxContainer(final org.vertx.java.platform.Container vertxContainer)
    {
        this. = vertxContainer;
        return this;
    }
    public Jersey withPackages(final String... packages)
    {
        .packages(packages);
        return this;
    }
    public Jersey withClasses(final Class<?>... classes)
    {
        .registerClasses(classes);
        return this;
    }
    public Jersey withInstances(final Object... instances)
    {
        .registerInstances(instances);
        return this;
    }
    public Jersey withName(final String name)
    {
        .setApplicationName(name);
        return this;
    }
    public Jersey withProperty(final String namefinal Object value)
    {
        .property(namevalue);
        return this;
    }
    public Jersey withInjectables(final Object... instances)
    {
        {
            @SuppressWarnings("unchecked")
            @Override
            protected void configure()
            {
                for (final Object instance : instances)
                {
                    bind(instance).to((Class<Object>) instance.getClass());
                }
            }
        });
        return this;
    }

    
Exposed in case some advanced feature is needed and not exposed by the with... methods.
    {
        return ;
    }
    @Override
    public Middleware init(@NotNull final Yoke yoke, @NotNull final String mount)
    {
        super.init(yokemount);
        initJersey();
        .onStartup(this);
        return this;
    }
    private void initJersey()
    {
    }
    @Override
    {
        return ;
    }
    @Override
    {
        return ;
    }
    @Override
    public void reload()
    {
        reload(getConfiguration());
    }
    @Override
    public void reload(final ResourceConfig configuration)
    {
        .onShutdown(this);
        initJersey();
        .onReload(this);
        .onStartup(this);
    }
    @Override
    public void handle(@NotNull final YokeRequest request, @NotNull final Handler<Objectnext)
    {
        final YokeResponse response = request.response();
        final YokeResponseWriter responseWriter = new YokeResponseWriter(responsevertx());
        final URI baseUri = getBaseUri(request);
        try
        {
            final ContainerRequest requestContext = new ContainerRequest(baseUrirequest.absoluteURI(),
                request.method(), getSecurityContext(request), new MapPropertiesDelegate());
            for (final String headerName : request.headers().names())
            {
                requestContext.headers(headerNamerequest.headers().get(headerName));
            }
            requestContext.setWriter(responseWriter);
            requestContext.setRequestScopedInitializer(new RequestScopedInitializer()
            {
                @Override
                public void initialize(final ServiceLocator locator)
                {
                    locator.<Ref<YokeRequest>> getService().set(request);
                    locator.<Ref<YokeResponse>> getService().set(response);
                    locator.<Ref<Vertx>> getService().set(vertx());
                    locator.<Ref<org.vertx.java.platform.Container>> getService().set(
                        );
                }
            });
            if (request.hasBody())
            {
                request.bodyHandler(new Handler<Buffer>()
                {
                    @Override
                    public void handle(final Buffer body)
                    {
                        // TODO review this to handle large payloads gracefully
                        requestContext.setEntityStream(new ByteArrayInputStream(body.getBytes()));
                        .handle(requestContext);
                    }
                });
            }
            else
            {
                .handle(requestContext);
            }
        }
        catch (final Exception ex)
        {
            next.handle(ex);
        }
    }
    private static URI getBaseUri(final YokeRequest request)
    {
        try
        {
            final URI uri = request.absoluteURI();
            return new URI(uri.getScheme(), nulluri.getHost(), uri.getPort(), "/"nullnull);
        }
        catch (final URISyntaxException ex)
        {
            throw new IllegalArgumentException(ex);
        }
    }
    private static void endResponse(final YokeResponse response)
    {
        try
        {
            response.end();
        }
        catch (final Exception e)
        {
            .error("Failed to commit response"e);
        }
    }
    private static SecurityContext getSecurityContext(final YokeRequest request)
    {
        return new SecurityContext()
        {
            @Override
            public boolean isUserInRole(final String role)
            {
                return false;
            }
            @Override
            public boolean isSecure()
            {
                return request.isSecure();
            }
            @Override
            public Principal getUserPrincipal()
            {
                // detect the user injected by the BasicAuth Yoke middleware
                final String user = request.get("user");
                return user == null ? null : new YokePrincipal(user);
            }
            @Override
            public String getAuthenticationScheme()
            {
                return null;
            }
        };
    }
New to GrepCode? Check out our FAQ X