GithubHelp home page GithubHelp logo

bootique / bootique-jersey Goto Github PK

View Code? Open in Web Editor NEW
7.0 7.0 7.0 1.16 MB

Provides Jersey JAX-RS integration with Bootique.

Home Page: https://bootique.io

License: Apache License 2.0

Java 100.00%

bootique-jersey's Introduction

build test deploy Maven Central

Bootique is a minimally opinionated java launcher and integration technology. It is intended for building container-less runnable Java applications. With Bootique you can create REST services, webapps, jobs, DB migration tasks, etc. and run them as if they were simple commands. No JavaEE container required! Among other things Bootique is an ideal platform for Java microservices, as it allows you to create a fully-functional app with minimal setup.

Each Bootique app is a collection of modules interacting with each other via dependency injection. This GitHub project provides Bootique core. Bootique team also develops a number of important modules. A full list is available here.

Quick Links

Support

You have two options:

  • Open an issue on GitHub with a label of "help wanted" or "question" (or "bug" if you think you found a bug).
  • Post a question on the Bootique forum.

TL;DR

For the impatient, here is how to get started with Bootique:

  • Declare the official module collection:
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>io.bootique.bom</groupId>
            <artifactId>bootique-bom</artifactId>
            <version>3.0-M4</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency> 
    </dependencies>
</dependencyManagement>
  • Include the modules that you need:
<dependencies>
    <dependency>
        <groupId>io.bootique.jersey</groupId>
        <artifactId>bootique-jersey</artifactId>
    </dependency>
    <dependency>
        <groupId>io.bootique.logback</groupId>
        <artifactId>bootique-logback</artifactId>
    </dependency>
</dependencies>
  • Write your app:
package com.foo;

import io.bootique.Bootique;

public class Application {
    public static void main(String[] args) {
        Bootique
            .app(args)
            .autoLoadModules()
            .exec()
            .exit();
    }
}

It has main() method, so you can run it!

For a more detailed tutorial proceed to this link.

Upgrading

See the "maven-central" badge above for the current production version of bootique-bom. When upgrading, don't forget to check upgrade notes specific to your version.

bootique-jersey's People

Contributors

aarrsseni avatar andrus avatar const1993 avatar elena-bondareva avatar irus avatar mikhailovseychuk avatar stariy95 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bootique-jersey's Issues

Set<Package> bound in JerseyModule must be qualified with annotation

Set<java.lang.Package> bound in JerseyModule must be qualified with some internal annotation, or otherwise it will create key conflict with other modules that might want to bind a collection of packages.

Upgrade Notes

However unlikely, this has a small chance to breaking downstream code. This will happen if such code explicitly injects a Set<Package>. To upgrade it, annotate the injection point with @JerseyResource.

Need a way to register Feature during JAX-RS runtime

There is a case when our application REST endpoint consumes a MediaType.MULTIPART_FORM_DATA type parameter:

import org.glassfish.jersey.media.multipart.FormDataParam;
...
@Path("mypath")
public class MyResource {

@POST
@Path("upload")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadMultiPart(@FormDataParam("upload") InputStream inputStream) {...}
...
}

so org.glassfish.jersey.media.multipart.MultiPartFeature feature should be activated during Jersey runtime.
This feature injects necessary message body readers, writers to our Jersey application.

Currently there is the only way to register a Feature is to contribute it when our application Module is being configured by using JerseyModule.contributeFeatures() API:

import org.glassfish.jersey.media.multipart.MultiPartFeature;
...
@Overrride
public void configure(Binder binder) {
...
JerseyModule.contributeFeatures(binder).addBinding().to(MultiPartFeature.class);
...}

But it's seems that it's too late and the application fails with the following error:

org.glassfish.jersey.server.model.ModelValidationException: Validation of the application resource model has failed during application initialization.
[FATAL] No injection source found for a parameter of type ...
at org.glassfish.jersey.server.ApplicationHandler.initialize(ApplicationHandler.java:553) ~[jersey-server-2.21.jar:na]
at org.glassfish.jersey.server.ApplicationHandler.access$500(ApplicationHandler.java:182) ~[jersey-server-2.21.jar:na]
at org.glassfish.jersey.server.ApplicationHandler$3.call(ApplicationHandler.java:348) ~[jersey-server-2.21.jar:na]
at org.glassfish.jersey.server.ApplicationHandler$3.call(ApplicationHandler.java:345) ~[jersey-server-2.21.jar:na]
at org.glassfish.jersey.internal.Errors.process(Errors.java:315) ~[jersey-common-2.22.1.jar:na]
at org.glassfish.jersey.internal.Errors.process(Errors.java:297) ~[jersey-common-2.22.1.jar:na]
at org.glassfish.jersey.internal.Errors.processWithException(Errors.java:255) ~[jersey-common-2.22.1.jar:na]
at org.glassfish.jersey.server.ApplicationHandler.(ApplicationHandler.java:345) ~[jersey-server-2.21.jar:na]
at org.glassfish.jersey.servlet.WebComponent.(WebComponent.java:390) ~[jersey-container-servlet-core-2.21.jar:na]
at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:170) ~[jersey-container-servlet-core-2.21.jar:na]
at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:362) ~[jersey-container-servlet-core-2.21.jar:na]
at javax.servlet.GenericServlet.init(GenericServlet.java:244) ~[javax.servlet-3.0.0.v201112011016.jar:na]
...
at org.eclipse.jetty.server.Server.doStart(Server.java:372) [jetty-server-9.3.6.v20151106.jar:9.3.6.v20151106]
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68) [jetty-util-9.3.6.v20151106.jar:9.3.6.v20151106]
at com.nhl.bootique.jetty.command.ServerCommand.run(ServerCommand.java:37) [bootique-jetty-0.12.jar:0.12]
at com.nhl.bootique.run.DefaultRunner.run(DefaultRunner.java:22) [bootique-0.12.jar:0.12]

Zero config by inferring resource package from the bootique call stack

Currently Jersey module bootstrap looks like this:

    public static void main(String[] args) {
        JerseyModule jersey = new JerseyModule().packageRoot(Main.class);
        Bootique.app(args).module(jersey).autoLoadModules().run();
    }

We can optionally bypass explicit root package setting by assuming that the class containing the main() method is the root of our Jersey app, and then we can inspect the calling stack to figure it out:

Thread.currentThread().getStackTrace();

getting MessageBodyWriter not found for media type=application/json

Hello

I have a simple resource that returns a List or a simple Object (Lookup) but I'm getting a MessageBodyWriter error.

Here is the code ....

@XmlRootElement
public class Lookup extends BaseDatabaseObject implements Serializable {


    String  code;
    String  type;
    String  description;

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

}

...

@Path("/lookups")
@Produces(MediaType.APPLICATION_JSON)
public class LookupResource {


    @GET
    public Response getAll() {

        List<Lookup> lookups = LookupDAO.getAllLookups();

        return Response.ok().entity(lookups).build();

    }

    @GET
    @Path("/one")
    public Response getOne() {

        List<Lookup> lookups = LookupDAO.getAllLookups();
        Lookup lookup = lookups.get(0);
        return Response.ok().entity(lookup).build();

    }
}

calling either resource will yield..

10:44:32.929 [yank-default housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - yank-default - Pool stats (total=0, active=0, idle=0, waiting=0)
10:44:32.965 [yank-default connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - yank-default - Added connection com.mysql.jdbc.JDBC4Connection@7467cb00
10:44:32.992 [yank-default connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - yank-default - Added connection com.mysql.jdbc.JDBC4Connection@650a71c2
10:44:32.992 [yank-default connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - yank-default - After adding stats (total=2, active=0, idle=2, waiting=0)
INFO  [2017-03-07 13:44:35,220] main o.e.jetty.util.log: Logging initialized @5052ms
INFO  [2017-03-07 13:44:35,315] main i.b.j.s.ServerFactory: Adding listener io.bootique.jetty.servlet.DefaultServletEnvironment
INFO  [2017-03-07 13:44:35,329] main i.b.j.s.ServletFactory: Adding servlet 'jersey' mapped to /*
INFO  [2017-03-07 13:44:35,382] main i.b.j.s.ServerLifecycleLogger: Starting jetty...
INFO  [2017-03-07 13:44:35,386] main o.e.j.server.Server: jetty-9.3.6.v20151106
INFO  [2017-03-07 13:44:36,672] main o.e.j.s.h.ContextHandler: Started o.e.j.s.ServletContextHandler@18d910b3{/,null,AVAILABLE}
INFO  [2017-03-07 13:44:36,734] main o.e.j.s.ServerConnector: Started ServerConnector@2cae9b8{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
INFO  [2017-03-07 13:44:36,734] main o.e.j.server.Server: Started @6566ms
INFO  [2017-03-07 13:44:36,738] main i.b.j.s.ServerLifecycleLogger: Started Jetty in 1352 ms. Base URL: http://192.168.2.10:8080/
ERROR [2017-03-07 13:44:44,892] bootique-http-28 o.g.j.m.i.WriterInterceptorExecutor: MessageBodyWriter not found for media type=application/json, type=class java.util.ArrayList, genericType=class java.util.ArrayList.
INFO  [2017-03-07 13:44:44,918] bootique-http-28 i.b.j.s.RequestLogger: 192.168.10.50 - - [07/Mar/2017:13:44:44 +0000] "GET //192.168.10.50:8080/lookups HTTP/1.1" 500 335 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"
ERROR [2017-03-07 13:44:48,204] bootique-http-29 o.g.j.m.i.WriterInterceptorExecutor: MessageBodyWriter not found for media type=application/json, type=class com.eCondo.com.eCondo.model.Lookup, genericType=class com.eCondo.com.eCondo.model.Lookup.
INFO  [2017-03-07 13:44:48,210] bootique-http-29 i.b.j.s.RequestLogger: 192.168.10.50 - - [07/Mar/2017:13:44:48 +0000] "GET //192.168.10.50:8080/lookups/one HTTP/1.1" 500 339 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"

In a standard project I would probably add some jackson dependencies and telling my web.xml how to process this to fix this, but I'm not sure how to go at it in this setup (I'm new to bootique)

TIA

Integrate 'jersey-bean-validation'

It is often convenient to use javax.validation in the context of Jersey. Common OpenAPI model generators include validating annotations, so we need the ability to turn on their processing.

Configurable JSON serialization

jersey-jackson-module should provide an extender that allows to switch between including and excluding null values in JSON responses.

bootique-jersey-jackson: Support java.time serialization

Proper (de)serialization of java.time objects requires installing Jackson extensions. Doing it in the context of bootique-jersey-jackson is a real pain. So let's enable this feature by default, but also do some refactoring to make ObjectMapper creation factory-driven, so that a single mapper can be configured for the entire app with some common policies:

jerseyjackson:
   // same as "JerseyJacksonModule.extend(b).skipNullProperties()"
   skipNullProperties: true

Support javax.inject.Inject annotation in Jersey resources

Currently only @com.google.inject.Inject annotation is resolved in Jersey resources and this can lead to confusion.
@javax.inject.Inject annotation is always on classpath (as compile dependency of Guice) and working fine in other Bootique parts.
It could be implemented exactly as Guice annotation resolver we already have.

Expose application REST resources

It may be useful to expose available application REST endpoints. bootique-swagger helps with API exposure to the client, but sometimes all we may need is just listing the resources in the app log, or building a simple web console listing high-level app capabilities. Or tying resources to metrics (how many requests per resources, response time statistics, etc.)

We still need to think on a better focus and use cases for this feature. But the algorithm for collecting a list of resources is pretty simple as mentioned in this forum post.

Unify injection into resources

Currently Jersey-provided objects (e.g. HttpServletRequest) require javax.inject.Inject for resource injection, while Bootique-provided objects (e.g. some user services), require com.google.inject.Inject for injection, creating lots of room for confusion.

Let's attempt to support both annotations interchangeably in Jersey. Especially since Guice seems to support javax.inject already.

Allow empty JerseyModule

to be consistent with other modules, we should make JerseyModule constructor public, so that Bootique.module(JerseyModule.class) doesn't explode.

Jersey still includes "javax.inject" dependency

Looks like there is still one or more places in bootique-jersey dependency tree where Jersey's own javax.inject is included (while we weed them out in other places). This is a minor problem. The only manifestation so far is this warning when running maven-shade-plugin. Still worth fixing.

[WARNING] javax.inject-2.5.0-b42.jar, javax.inject-1.jar define 6 overlapping classes: 
[WARNING]   - javax.inject.Inject
[WARNING]   - javax.inject.Singleton
[WARNING]   - javax.inject.Scope
[WARNING]   - javax.inject.Named
[WARNING]   - javax.inject.Provider
[WARNING]   - javax.inject.Qualifier
[WARNING] maven-shade-plugin has detected that some class files are
[WARNING] present in two or more JARs. When this happens, only one
[WARNING] single version of the class is copied to the uber jar.
[WARNING] Usually this is not harmful and you can skip these warnings,
[WARNING] otherwise try to manually exclude artifacts based on
[WARNING] mvn dependency:tree -Ddetail=true and the above output.
[WARNING] See http://maven.apache.org/plugins/maven-shade-plugin/
[INFO] Replacing original artifact with shaded artifact.

bootique-jersey-jackson: a submodule to handle JSON serialization

We need to simplify adding auto-serialization to JSON. The issue is described in #28. It can likely be solved by adding an optional bootique-jersey-jackson module that resolves all jackson versions to be compatible with the Bootique core (with a set of unit tests that will guard it against future upgrade regression).

StackOverflowError in Jersey 2.21

When I use trace log level, I got StakOverflowError when application starting.

Problem can be solved by upgrading jersey-container-servlet to 2.21.1. Btw latest vesrion 2.26 doen't work with this module.

DEBUG [2017-12-20 12:07:59,647] main org.jvnet.hk2.logger: ServiceLocator ServiceLocatorImpl(jersey-server-rd-locator,1,831993944) has been shutdown
INFO  [2017-12-20 12:07:59,673] main o.e.j.s.ServerConnector: Started ServerConnector@68ef01a5{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
Error running command '--server --config=run.yml': Command exception.
java.lang.StackOverflowError
	at java.base/java.lang.StringBuilder.<init>(StringBuilder.java:91)
	at java.base/java.lang.Object.toString(Object.java:246)
	at java.base/java.lang.String.valueOf(String.java:2801)
	at java.base/java.lang.StringBuilder.append(StringBuilder.java:135)
	at java.base/java.util.AbstractCollection.toString(AbstractCollection.java:473)
	at java.base/java.lang.String.valueOf(String.java:2801)
	at java.base/java.lang.StringBuilder.append(StringBuilder.java:135)
	at org.glassfish.jersey.server.model.Resource$Builder.toString(Resource.java:676)
	at java.base/java.lang.String.valueOf(String.java:2801)
	at java.base/java.lang.StringBuilder.append(StringBuilder.java:135)
	at java.base/java.util.AbstractCollection.toString(AbstractCollection.java:473)
	at java.base/java.lang.String.valueOf(String.java:2801)
	at java.base/java.lang.StringBuilder.append(StringBuilder.java:135)
	at org.glassfish.jersey.server.model.Resource$Builder.toString(Resource.java:676)
	at java.base/java.lang.String.valueOf(String.java:2801)
	at java.base/java.lang.StringBuilder.append(StringBuilder.java:135)

https://github.com/jersey/jersey/issues/3225

Upgrade to JAX-RS 2.1 / Jersey 2.27

Per #38 we upgraded to 2.25.1... 2.26 features JAX-RS 2.1 support (reactive APIs and stuff) as well as internal refactoring of the HK container (that will eventually allow using Guice).

Upgrading to 2.26 turned out to be challenging - classes managed by Guice were not seen by Jersey. Need to figure out how the injection bridge is supposed to function now. Perhaps also worth waiting for some time for the new DI API to stabilize, so that there is a new official way for third-party DI integration.

Upgarde to bq-jetty 0.13 and bq 0.15

Lots of good stuff in new Bootique and Jetty releases. Moreover there seem to be some integration conflicts when running against jetty 0.13. Need to resolve.

JerseyModule - switch to "contribute" methods for config instead of "builder"

JerseyModule.builder() is now used to register resources. I guess we should discourage building modules manually. They should other be configured via YAML or contributed to, or overridden. In this case we'll provide the corresponding contribute methods for individual resource types and resource packages.

Combine bootique-jersey with bootique-jersey-client in one project

A pattern in Bootique is to organize a single project around a technology we'd like to integrate. In this respect Jersey is different. We have both "bootique-jersey" (this project) and "bootique-jersey-client" projects. Let's combine them together.

Upgrade notes:

Everything can stay the same from the user perspective except for the artifacts "groupId". It will be changed from "io.bootique.jersey.client" to "io.bootique.jersey" for the client.

Builder-style API for configuring JerseyModule

Instead of using constructor and modifying module via setters, let's refactor to make it an immutable module that has a builder. Similar to the recent BQCoreModule. E.g.:

Module jersey = JerseyModule.builder().packageRoot(Resource1.class).build();

Resources with dynamically defined paths

Sometimes certain resources need their paths to be defined dynamically from configuration. E.g. would be nice if bootique-swagger-openapi could be mapped to a custom URL (currently hardcoded as /swagger.json|yaml). Overriding resource annotated path is possible via Jersey API. Let's integrate this option in bootique-jersey.

Usage example:

b -> JerseyModule.extend(b)
    .addResource(R1.class, "p")
    // same resource, two different paths
    .addResource(R2.class, "p1")
    .addResource(R2.class, "p2")

Java 10 issue

Per bootique/bootique#225 bootique-jersey starts with exceptions on java 10. The fix seems to be the following (already applied to bootique-swagger)

<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.0</version>
</dependency>
<dependency>
    <groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>1.1.1</version>
</dependency>

Do we actually need "activation" lib?? Didn't realize it was in the base JDK before.

Can't inject dependencies with generics to Jersey resources

This was first reported on the Bootique forum. Bootique Jersey injection bridge doesn't work for resources that inject services with generics parameters. I can reproduce in a unit test. From ignored ResourceInjection_GenericsIT.testFieldInjected :

@Path("/f")
public static class FieldInjectedResource {

    @Inject
    private S1<Integer> intService;
    @Inject
    private S1<String> stringService;

    @GET
    public String get() {
        return "f_" + intService.asString() + "_" + stringService.asString();
    }
}

This results in the following exception on startup:

[pool-1-thread-1] WARN / - unavailable
java.lang.IllegalStateException: Can't process injection point: io.bootique.jersey.ResourceInjection_GenericsIT.io.bootique.jersey.ResourceInjection_GenericsIT$S1<java.lang.Integer>
	at io.bootique.jersey.GuiceInjectInjector.resolve(GuiceInjectInjector.java:60)
	at org.jvnet.hk2.internal.Utilities.justInject(Utilities.java:946)
	at org.jvnet.hk2.internal.ServiceLocatorImpl.inject(ServiceLocatorImpl.java:981)
	at org.jvnet.hk2.internal.ServiceLocatorImpl.inject(ServiceLocatorImpl.java:971)
	at org.glassfish.jersey.server.ApplicationHandler.initialize(ApplicationHandler.java:602)
	at org.glassfish.jersey.server.ApplicationHandler.access$500(ApplicationHandler.java:182)
	at org.glassfish.jersey.server.ApplicationHandler$3.call(ApplicationHandler.java:348)
	at org.glassfish.jersey.server.ApplicationHandler$3.call(ApplicationHandler.java:345)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
	at org.glassfish.jersey.internal.Errors.processWithException(Errors.java:255)
	at org.glassfish.jersey.server.ApplicationHandler.<init>(ApplicationHandler.java:345)

At the same time injection of S1<Integer> and S1<String> elsewhere in Guice works, so there's an easy workaround - a provider method.

Don't forget to un-@ignore the test in question once this is fixed

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.