Singleton Pattern with Jooby

307 views
Skip to first unread message

Kevin Sheppard

unread,
Jun 14, 2016, 10:38:38 PM6/14/16
to jooby-project
I have a general purpose database class that I use with my other apps. It's basically as follows

public static void init(String, url, String username, String password) { ... } //Initialize all the static properties

public static Database getInstance() { ... } //Singleton method to return an instance of an already configured Database


I also have some DAOs that call the getInstance() method.

I would initialize my Database using the main method of my application

public static void main(final String[] args) throws Throwable {
 
Database.init("jdbc:postgresql://localhost/database", "username", "password"); //Configures and initializes the database properly.
  run
(JoobyApplication::new, args);
}


However when I call the getInstance() method in my JoobyApplication class, it throws an error that basically says the database was not initialized.

public class JoobyApplication extends Jooby {
 
{
   
get("/", () -> {
   
Database.getInstance().runQuery("select..."); //ERROR: Database was not initialized. Run init() method first.
   
});
 
}
}

All of my tests pass as long as I don't try to use the class within the context of a Jooby application.

How do I go about fixing this?

Kevin Sheppard

unread,
Jun 15, 2016, 12:50:07 AM6/15/16
to jooby-project
SOLVED: It was a problem with Hikari and the PostgreSQL driver. Apparently once I'm configuring Hikari inside of a Jooby app/module, I need to use the setDriverClassName() method or use the Class.forName("driver") utility. Neither of these steps are needed once invoking the methods from regular Java classes. I wonder why that is?

Edgar Espina

unread,
Jun 15, 2016, 7:51:43 AM6/15/16
to jooby-project
General suggestion about Singletons: avoid them ;) Why? They are usually hard to test and hard to identify who is calling/depends-on your singleton, bc you add a global/static dependency somewhere.

Instead of singleton, you can let Guice to handle the lifecycle of that object as singleton. For example, here is a high level usage of what you should do:

{
  bind
(Database.class -> new Database());

  onStart
(registry -> {
   
Config conf = registry.require(Config.class);
   
Database db = registry.require(Database.class);
    db
.init(conf.getString("db.url"), conf.getString("db.user", conf.getString("db.password"));
 
});
}

That is what you need to create a singleton in Guice and start/initialize.

Clients of database looks like:

{
   
get("/db", req -> {
     
Database db = req.require(Database.class);
     // ....
   
});
}

Or from mvc route: 

public class Controller {

 
@Inject
 
public Controller(Database db) {
   
// ...
 
}
}


Client/dependency of your singleton are clearly identifiable and testable (specially the mvc route).

Did you try jooby jdbc-module? Is there a bug there with postgreSQL?

Thanks

Kevin Sheppard

unread,
Jun 15, 2016, 2:03:35 PM6/15/16
to jooby-project
I took your advice and began reworking my classes to use dependency injection. I'm now getting used to this :-) Oh and there is no bug in your JDBC module (at least, I haven't tried it yet). I wanted to build my database stuff from scratch using Sql2o as a coding exercise.

Edgar Espina

unread,
Jun 15, 2016, 2:48:55 PM6/15/16
to jooby-project
Fair enough!

Kevin Sheppard

unread,
Jun 15, 2016, 6:10:15 PM6/15/16
to jooby-project
So some progress. I've managed to reconstruct the classes to make use of dependency injection, however I'm having difficulty using it in the context of a module.

In my main Application.java file, I have:
...

use((env, conf, binder) -> {
      binder
.bind(DatabaseService.class).to(PostgreSQLDatabase.class);
   
});
use("/admin", new Admin());

...


Then in Admin.java I have

...

private MockDAO mock; //MockDAO is a class that gets a DatabaseService injected into it.

{
 mock
= require(MockDAO.class); //Throws an error: "App didn't start yet"
 
get("/", req -> req.require(MockDAO.class) ... ) //Works fine
 
}

...

Any ideas?

Kevin Sheppard

unread,
Jun 15, 2016, 6:16:52 PM6/15/16
to jooby-project
PS. Yes did see the onStart() method call in your example, but is that the only way to instantiate DI classes outside of a request?

Edgar Espina

unread,
Jun 15, 2016, 6:55:10 PM6/15/16
to jooby-project
Right.

You got the error bc at the time the Admin app is instantiated (use("/admin", new Admin()) the application didn't start is just being assemble.

Why do you need a instance variable? It is (almost) always a bad idea. A route handler is executed in a separated thread so you might run into big troubles if you don't keep attention to this. The correct way is to ask Guice for a service inside a route (via req.require or require) and let Guice figure it out if the object is a singleton or not (by default they aren't).

Still, if you are 100% sure state of the object wont change after initialization and don't like the require call then yes onStart is the solution:

DAO dao;

{
  onStart
(registry -> {
    dao
= registry.require(DAO);
 
});
}

If you go this path, make sure to add a clear documentation and be absolutely sure DAO is a stateless object.

make sense?

Kevin Sheppard

unread,
Jun 15, 2016, 8:26:25 PM6/15/16
to jooby-project
My approach was mainly centered around code brevity and the DRY principle. Having to write require(MockDAO.class) every time I needed it was what I was trying to get away from. Ah well, if that's the Jooby way, then so be it.
Reply all
Reply to author
Forward
0 new messages