2

Using Jersey 3.0.1, I am struggling to get binding working.

I have this binding module with the factories below:

public static class MyBinder extends AbstractBinder {
    @Override
    protected void configure() {
      LOG.info("Attempting to configure binder");
      bindFactory(DataSourceFactory.class).to(HikariDataSource.class).in(Singleton.class);
      bindFactory(JooqConfigFactory.class).to(Configuration.class).in(Singleton.class);
      bindFactory(DSLContextFactory.class).to(DSLContext.class).in(Singleton.class);
      LOG.info("Configured binder");
    }
}

public static class DataSourceFactory implements Supplier<HikariDataSource> {
    @Override
    public HikariDataSource get() {
      ...
      return new HikariDataSource(config);
    }
}

public static class JooqConfigFactory implements Supplier<Configuration> {
    @Inject
    HikariDataSource dataSource;

    @Override
    public Configuration get() {
      ...   
      return conf;
    }

}

public static class DSLContextFactory implements Supplier<DSLContext> {
    @Inject
    Configuration config;

    @Override
    public DSLContext get() {
      return DSL.using(config);
    }
}

Then I have the setup for my Servlet using embedded Jetty:

public void start() throws Exception {
    int port = appConfig.getProperty("http.port", 9998);
    Server server = new Server(port);

    ServletContextHandler ctx =
        new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
    ctx.setContextPath("/");
    server.setHandler(ctx);
    ResourceConfig config = new JerseyConfig();
    ServletHolder servlet = new ServletHolder(new ServletContainer(config));
    servlet.setInitOrder(1);
    ctx.addServlet(servlet, "/*");

    server.start();
    server.join();
}

public static class JerseyConfig extends ResourceConfig {
    public JerseyConfig() {
      packages("com.sodonnell.jersey", "jersey.config.server.provider.packages");
      register(new MyBinder());
    }
}

And in my Rest service I simply try to inject a private instance variable:

public MyClass {

  @Inject  // javax.inject.Inject
  private DSLContext dslContext;

}

However this dslContext is always null. I can see from the logs, that it prints the LOG.info("Configured binder"); message. However putting similar logs in my factory classes show they never get called.

Has anyone got any idea what I am missing?

EDIT

To make things simpler, I created this class:

public class SimpleClass {

  private static Logger LOG = LoggerFactory.getLogger(SimpleClass.class);

  public SimpleClass() {
    LOG.info("Call the simple class constructor");
  }

Changed my binder module:

import com.google.inject.Injector;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.jersey.internal.inject.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;
import org.jooq.Configuration;
import org.jooq.DSLContext;
import org.jooq.SQLDialect;
import org.jooq.impl.DSL;
import org.jooq.impl.DefaultConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.IOException;
import java.util.Properties;
import java.util.function.Supplier;
...


 // This is a nested class 
 public static class MyBinder extends AbstractBinder {
    @Override
    protected void configure() {
      LOG.info("Attempting to configure binder");
      bind(new SimpleClass()).to(SimpleClass.class); 
    }
  }

Then attempted to inject just SimpleClass:

package com.sodonnell.hdfs3.rest;

import com.sodonnell.hdfs3.SimpleClass;
import com.zaxxer.hikari.HikariDataSource;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.HEAD;
import jakarta.ws.rs.core.HttpHeaders;

import jakarta.ws.rs.Path;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.Response;
import org.jooq.DSLContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;

  @Inject
  private SimpleClass simpleClass;
...

But its still null, although I see both the log messages. There must be some fundamental setup I am missing.

Full cut down code with the SimpleClass example at:

github.com/sodonnel/jerseyBind

Stephen ODonnell
  • 4,441
  • 17
  • 19
  • The first thing I would do in this situation is to try to just get injection working. Start with just _one_ thing you are trying to inject. Get injection working first, then narrow down your problem. – Paul Samsotha Mar 10 '21 at 12:31
  • Do you have some bad circular dependencies? If `config` is `Configuration`, then it looks to me that you have some circular dependencies. The HikariDataSource needs a Configuration and a Configuration needs an HikariDataSource. – Paul Samsotha Mar 10 '21 at 12:39
  • The config objects are different types. I have stripped this back to something simpler and its still not working. I will updated the description above. – Stephen ODonnell Mar 10 '21 at 12:44
  • Hard to tell what's wrong. The second example looks fine. I would need to see a complete working example to be able to debug it. – Paul Samsotha Mar 10 '21 at 12:51
  • @PaulSamsotha I pushed a completely cut down app to https://github.com/sodonnel/jerseyBind - if you can have a look I will be forever grateful! – Stephen ODonnell Mar 10 '21 at 13:12
  • It shouldn't be Spring Boot. To my knowledge I have not tried to use Spring - just plain Jersey with embedded Jetty. I have pulled the pom dependencies together from various sources to get things working (and this is my first Jersey app). What makes it seem like a Spring project? – Stephen ODonnell Mar 10 '21 at 13:18
  • I was looking at the wrong project. Sorry. – Paul Samsotha Mar 10 '21 at 13:37

1 Answers1

4

The answer is quite simple. You are using Jersey 3.0 which has switched to the new Jakarta naming. javax is thrown out the window - this includes javax.inject. All the javax package names have now been changed to jakarta. So to get the inject to work, the @Inject import should be

import jakarta.inject.Inject;

This change is part of the change of Java EE to Jakarta EE Starting from Jakarta EE 8 to Jakarta EE 9, all the namespacing has changed from javax to jakarta. So things like javax.servlet will now be jakarta.servlet. Weird, yes a huge breaking change with no backward compatibility.

In your case you have all the correct components to work with Jakarta (i.e. Jersey 3.0 and Jett 11), but you just need to make use of the new namespacing. Notice all the JAX-RS imports are now jakarta also.

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720