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;
 
 
 import  org.jetbrains.annotations.NotNull;
 import  org.vertx.java.core.http.HttpHeaders;
 
 import  com.jetdrone.vertx.yoke.Middleware;
 import  com.jetdrone.vertx.yoke.Yoke;

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: YokeRequest, YokeResponse, Vertx and org.vertx.java.platform.Container (only if provided with withVertxContainer(org.vertx.java.platform.Container)).

Any object provided with withInjectables(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 AbstractMiddleware 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(CONTENT_LENGTH)) {
                .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(HttpHeaders.CONTENT_LENGTH)) {
                    .headers().add(HttpHeaders.CONTENT_LENGTH, 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 + (.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 (!.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.<YokeRequest>referenceFactory()).to(
                    new TypeLiteral<Ref<YokeRequest>>() {
                    }).in(RequestScoped.class);
            bindFactory(YokeResponseReferencingFactory.class).to(YokeResponse.class)
                    .proxy(true)
                    .proxyForSameScope(false)
                    .in(RequestScoped.class);
            bindFactory(ReferencingFactory.<YokeResponse>referenceFactory()).to(
                    new TypeLiteral<Ref<YokeResponse>>() {
                    }).in(RequestScoped.class);
            bindFactory(VertxReferencingFactory.class).to(Vertx.class)
                    .proxy(true)
                    .proxyForSameScope(false)
                    .in(RequestScoped.class);
            bindFactory(ReferencingFactory.<Vertx>referenceFactory()).to(new TypeLiteral<Ref<Vertx>>() {
            }).in(RequestScoped.class);
                    .proxy(true)
                    .proxyForSameScope(false)
                    .in(RequestScoped.class);
            bindFactory(ReferencingFactory.<org.vertx.java.platform.Container>referenceFactory()).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(CONTENT_LENGTH, 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) {
        .register(new AbstractBinder() {
            @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.
    public ResourceConfig resourceConfig() {
        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
    public ResourceConfig getConfiguration() {
        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