1

I'm creating a REST web application with Java, Tomcat and Jersey. I'm using annotations (no web.xml!) I ended up using this application configuration:

package com.my_own.server;

import java.util.Properties;
import javax.ws.rs.ApplicationPath;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.my_own.db.PostgreSQLDb;

@ApplicationPath("/api")
public class Application extends javax.ws.rs.core.Application {
    private static Logger logger = LogManager.getLogger(Application.class);
    public static Application application = null;

    public final Properties properties;
    public final PostgreSQLDb adminDb;

    public Application() throws Exception {
        logger.debug("Loading properties from ",
                    getClass().getClassLoader().getResource("config.properties"));
        properties = new Properties();
        properties.load(getClass().getClassLoader().getResourceAsStream("config.properties"));
        adminDb = new PostgreSQLDb(properties, "admin");
        application = this; // Setting the global application object here
    }

}

Here is my problem. There is a single global application objects for the web container. I'm saving it into a static field, from the application constructor. I need to access it from other classes later (it holds the global configuration, a global database connection factory, and probably other things.)

Am I doing this right? I suspect that there must be a better way: save a reference to the application when annotations are processed. But I'm not sure how. Can I be sure that the Application's constructor will be called exactly once, and the Application.application reference can be accessed later, from any REST call?

nagylzs
  • 3,954
  • 6
  • 39
  • 70
  • See https://stackoverflow.com/questions/16216759/dependency-injection-with-jersey-2-0 it covers injecting application object using jersey. – AlexC Dec 16 '17 at 14:52

2 Answers2

2

Use dependency injection in jersey, bind your application when initializing:

public class MyApplication extends ResourceConfig {
    public MyApplication() {
        super(MyApplication.class);

        register(new MyBinder());
        packages(true, "location.of.my.jersey.classes");
    }

    /* Bind is used to let jersey know what can be injected */
    private static class MyBinder extends AbstractBinder {
        @Override
        protected void configure() {
            bind(MyApplication.class).to(MyApplication.class);
        }
    }
}

Then in your code:

@Path("myapi")
@Produces(MediaType.APPLICATION_JSON)
public class ServerRoutes {
    @Inject
    MyApplication application;

//... your rest code here
}

Now you can access MyApplication from within your code without needing any statics or singletons, jersey handles it.

AlexC
  • 1,395
  • 14
  • 26
  • I'm a bit confused. My application extends javax.ws.rs.core.Application. – nagylzs Dec 16 '17 at 15:11
  • ResourceConfig extends Application and adds some jersey functionality. You probably should extend ResourceConfig if you plan on staying with jersey. – AlexC Dec 16 '17 at 15:16
  • Sorry, it compiles, but accessing the injected application object thows NullPointerException – nagylzs Dec 16 '17 at 15:21
  • Finally, it REALLY works. First I used com.sun.jersey.spi.inject.Inject which is deprecated. The other option I had was javax.inject, and with that, it works perfectly. – nagylzs Dec 16 '17 at 15:29
1

Let me share my opinion: you can use of course a well-known Singleton pattern to store a "global" static object, however, it's really an antipattern these days.

If it's not a homework or something then storing global "static" objects is always a bad idea design-wise. If you want to know why there are many sources that answer this question, like this discussion for example

So I suggest considering using a Dependency Injection container instead, There are many really good containers out there: Spring, Guice to name a few.

These containers can store these objects as beans and if your "pieces" of functionality are also managed by these containers, you'll be able to "inject" application beans right into the controller.

It effectively solves all the issues introduced by singleton pattern.

halfer
  • 19,824
  • 17
  • 99
  • 186
Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97
  • Latest version of jersey has built-in DI with HK2. Otherwise, I would pick dagger2 over spring or guice for easier of debugging. – AlexC Dec 16 '17 at 14:42
  • Hey, I'm not into DI containers war here :) I've just stated that using DI container is much better than using global singleton objects. If HK2 /dagger2 will be good enough for author's needs - that's great. – Mark Bramnik Dec 16 '17 at 14:44
  • Wasn't meant as a war, just pointing out that jersey already has DI built-in. – AlexC Dec 16 '17 at 14:57
  • Finally I had to move to Guice + Sitebricks because I had to use another library that was tied to Guice. But I was not aware of dependency injection just a few days ago, so your answer helped a lot! – nagylzs Dec 19 '17 at 19:38