'. '

JerseyInjection

From APIDesign

(Difference between revisions)
Jump to: navigation, search
(New page: I am using Jersey for a long time to implement REST services and I am relatively happy Jersey user. One thing that is problematic is the fact that I am using Jersey...)
Current revision (05:25, 15 March 2017) (edit) (undo)
(The Real Example)
 
(6 intermediate revisions not shown.)
Line 1: Line 1:
-
I am using [[Jersey]] for a long time to implement [[REST]] services and [[I]] am relatively happy [[Jersey]] user. One thing that is problematic is the fact that [[I]] am using [[Jersey]] as a standalone application (without any J2EE container) and finding tutorials how to do it isn't always easy. For example how to use [[ComponentInjection]] with such standalone [[Jersey]] was a mystery for me for a long time...
+
I am using [[Jersey]] for a long time to implement [[REST]] services and [[I]] am relatively happy [[Jersey]] user. One thing that is problematic is the fact that [[I]] am using [[Jersey]] as a standalone application (without any J2EE container) and finding tutorials how to do it isn't always easy. For example how to use [[Component Injection]] with such standalone [[Jersey]] was a mystery for me for a long time...
== Injection ==
== Injection ==
Line 15: Line 15:
</source>
</source>
-
and when preparing the application register classes implementing these components into the system:
+
That is one side of the story. The other side requires one to register classes implementing these components into the system when configuring the server:
<source lang="java">
<source lang="java">
-
static HttpServer createServer(URI u, File dir) {
+
static HttpServer createServer(URI u, File dir) {
-
ResourceConfig rc = new ResourceConfig(
+
ResourceConfig rc = new ResourceConfig(
-
TimingResource.class, ContactsResource.class, Storage.class, Main.class
+
TimingResource.class, ContactsResource.class, Storage.class, Main.class
-
);
+
);
-
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(u, rc);
+
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(u, rc);
-
return server;
+
}
-
}
+
</source>
</source>
-
that works, but it has one problem: how can I configure the instances? The system just calls their default (no argument) constructor and for example the '''Storage''' class would benefit from actually knowing the location where to store its data. So far I was solving that with a system property: Reading '''System.getProperty("storage.dir")''' inside the default constructor. That is of course quite hacky - I knew there had to be a better way, but I never invested enough time to find it out. Just today...
+
Such solution works, but it has one problem: how can the instances be configured? The system just calls their default (no argument) constructor and for example the '''Storage''' class would benefit from actually knowing the location where to store its data. So far I was solving that with a system property: Reading '''System.getProperty("storage.dir")''' inside the default constructor. That is of course quite hacky - I knew there had to be a better way, but I never invested enough time to find it out. Just today...
== Your Own Binder ==
== Your Own Binder ==
Line 34: Line 33:
<source lang="java">
<source lang="java">
-
ResourceConfig rc = new ResourceConfig(
+
ResourceConfig rc = new ResourceConfig(
-
TimingResource.class, ContactsResource.class, Main.class
+
TimingResource.class, ContactsResource.class, Main.class
-
);
+
);
-
final Storage storage = dir == null ? Storage.empty() : Storage.forDirectory(dir);
+
final Storage storage = dir == null ? Storage.empty() : Storage.forDirectory(dir);
-
rc.registerInstances(new AbstractBinder() {
+
rc.registerInstances(new AbstractBinder() {
-
@Override
+
@Override
-
protected void configure() {
+
protected void configure() {
-
bind(storage).to(Storage.class);
+
bind(storage).to(Storage.class);
-
}
+
}
-
});
+
});
-
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(u, rc);
+
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(u, rc);
-
return server;
+
</source>
</source>
Line 51: Line 49:
== The Real Example ==
== The Real Example ==
 +
 +
Here is [https://github.com/jtulach/timing/commit/e5be764bb61c1c251d3d795eb517f559f25fddfc the diff] that [[I]] applied to my [https://github.com/jtulach/timing real project] to enable the use of [[injection]]. And here you can find the change that allows me to pre-configure the instance of '''Storage''' when initializing the server: [https://github.com/jtulach/timing/commit/9e1e7398f9c26b9ca3245e50a09bf8427ccf43de the diff].
 +
 +
== The Best Tutorial ==
 +
 +
Of course, all of this is already explained in an [https://jersey.java.net/documentation/latest/ioc.html existing tutorial] - it is just not that easy to find it. It really helps if a guy who wrote significant part of [[Jersey]] is your co-worker, sitting 2m away from you...
 +
 +
Thanks Jakub!

Current revision

I am using Jersey for a long time to implement REST services and I am relatively happy Jersey user. One thing that is problematic is the fact that I am using Jersey as a standalone application (without any J2EE container) and finding tutorials how to do it isn't always easy. For example how to use Component Injection with such standalone Jersey was a mystery for me for a long time...

Contents

Injection

Of course, the trivial things are easy. One can use the javax.inject annotations to identify the injection slots:

@Path("/timing/") @Singleton
public final class TimingResource {
    @Inject
    private Storage storage;
    @Inject
    private ContactsResource contacts;
}

That is one side of the story. The other side requires one to register classes implementing these components into the system when configuring the server:

static HttpServer createServer(URI u, File dir) {
   ResourceConfig rc = new ResourceConfig(
       TimingResource.class, ContactsResource.class, Storage.class, Main.class
   );
   HttpServer server = GrizzlyHttpServerFactory.createHttpServer(u, rc);
}

Such solution works, but it has one problem: how can the instances be configured? The system just calls their default (no argument) constructor and for example the Storage class would benefit from actually knowing the location where to store its data. So far I was solving that with a system property: Reading System.getProperty("storage.dir") inside the default constructor. That is of course quite hacky - I knew there had to be a better way, but I never invested enough time to find it out. Just today...

Your Own Binder

The solution is simple. One can register own AbstractBinder:

ResourceConfig rc = new ResourceConfig(
    TimingResource.class, ContactsResource.class, Main.class
);
final Storage storage = dir == null ? Storage.empty() : Storage.forDirectory(dir);
rc.registerInstances(new AbstractBinder() {
    @Override
    protected void configure() {
        bind(storage).to(Storage.class);
    }
});
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(u, rc);

Now it is possible to properly prepare and configure the appropriate instance of Storage and then just bind it to Storage.class when the Injection is about to happen. There is just a single downside: this is a Jersey specific solution - it uses org.glassfish.hk2.utilities.binding.AbstractBinder - but yeah, great that it works and the non JAX-RS standardized code is anyway located just in the main method initialization.

The Real Example

Here is the diff that I applied to my real project to enable the use of injection. And here you can find the change that allows me to pre-configure the instance of Storage when initializing the server: the diff.

The Best Tutorial

Of course, all of this is already explained in an existing tutorial - it is just not that easy to find it. It really helps if a guy who wrote significant part of Jersey is your co-worker, sitting 2m away from you...

Thanks Jakub!

Personal tools
buy