0

I defined an AbstractBinder and register it in my JAX-RS application. The binder specifies how the dependency injections should create my classes.

public class AppBinder extends AbstractBinder {
@Override
protected void configure() {
     bind(TranslationImportServiceImpl.class).to(TranslationImportService.class);
    bindAsContract(ImportCandidateBuilder.class);
    bind(DomainsBusinessServices.class)
            .to(IDomainsBusinessServices.class).in(Singleton.class);
    bind(CodeBusinessService.class)
            .to(ICodeBusinessService.class).in(Singleton.class);
    bind(LangBusinessService.class)
            .to(ILangBusinessService.class).in(Singleton.class);
    bind(TranslationBusinessService.class)
            .to(ITranslationBusinessService.class).in(Singleton.class);
    bind(SessionImpl.class)
            .to(Session.class).in(Singleton.class);
    bind(SessionFactoryImpl.class)
            .to(SessionFactory.class).in(Singleton.class);
    bind(CriteriaImpl.class)
            .to(Criteria.class).in(Singleton.class);
    bindAsContract(DefaultBusinessService.class);
}

I implemented the Application class (specified in the init-param of web.xml).

public class Application extends ResourceConfig {
public Application() {
    register(new AppBinder());
    packages(true, "web.rest");
}

web.xml

<servlet>
    <servlet-name>Jersey Web Application</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>jersey.config.server.provider.packages</param-name>
        <param-value>web.rest</param-value>
    </init-param>
    <init-param>
        <param-name>javax.ws.rs.Application</param-name>
        <param-value>web.rest.Application</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>Jersey Web Application</servlet-name>
    <url-pattern>/rest/*</url-pattern>
</servlet-mapping>

Then my RestService where the injection works fine

@Path("/call")
public class RestService {

@Inject
private TranslationImportService translationImportService;

@POST
@Path("/post")
@Consumes({MediaType.MULTIPART_FORM_DATA})
   public Response upload(@FormDataParam("file") InputStream fileInputStream,
                          @FormDataParam("file") FormDataContentDisposition fileMetaData) throws Exception {

        Map<String, TranslationImportResult> importResultMap = new HashMap<String, TranslationImportResult>();

        try {
            Map<String, InputStream> extractedFiles = extractFiles(fileMetaData.getFileName(), fileInputStream);

            Set<Entry<String, InputStream>> entrySet = extractedFiles.entrySet();
            TranslationImportResult importResult;
            for (Entry<String, InputStream> entry : entrySet) {
                importResult = null;

                if (entry.getKey().endsWith(".xml") || entry.getKey().endsWith(".xlf")) {

                    importResult = translationImportService.importTranslations(entry.getKey(), entry.getValue(), 1320L, "admin", true);
                } else {
                    logger.warn("Cannot process file (wrong extension): ", entry.getKey());
                    importResult = new TranslationImportResult(TranslationImportStatus.FAILURE, -1, -1);
                }
                importResultMap.put(entry.getKey(), importResult);
            }
        } catch (Exception e) {
            logger.error("A problem with the file extention occured! \n", e);
        }
        return Response.ok("File uploaded successfully").build();
    }

I annnotated interface TranslationImportService as @Contract

@Contract
public interface TranslationImportService

and the implementation as @Service

@Service
public class TranslationImportServiceImpl implements TranslationImportService{

@Inject
private ImportCandidateBuilder importCandidateBuilder;

@Override
public TranslationImportResult importTranslations(String inputName, InputStream inputStream, Long domainId,
                                                  String username, boolean allOrNothing) throws Exception {
    TranslationImportStatus ret;
    InputDeserializer inputDeserializer = InputDeserializerFactory.newInputDeserializer(inputName);
    List<InputTranslationBean> translationBeans = inputDeserializer.deserialize(inputStream);
    List<ImportCandidate> candidates = importCandidateBuilder.buildCandidates(domainId, translationBeans);
    insertStartImportEvent(candidates, inputName);
    translationImportOrchestrator.performTranslationsImport(candidates, username, allOrNothing);
    TranslationImportResult importResult = buildImportResult(translationImportOrchestrator);
    insertStopImportEvent(candidates, inputName);
    return importResult;
}

The problem is in injection importCandidateBuilder. Although i have binded it, it throws Exception:

org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee (requiredType=ImportCandidateBuilder,parent=TranslationImportServiceImpl...)

ImportCandidateBuilderClass:

@Service
public class ImportCandidateBuilder {

    private static Logger logger = LoggerFactory.getLogger(ImportCandidateBuilder.class);

    private IDomainsBusinessServices domainService;
    private ICodeBusinessService codeService;
    private ILangBusinessService vlService;
    private ITranslationBusinessService translationService;

    public ImportCandidateBuilder(IDomainsBusinessServices domainService, ICodeBusinessService codeService,
                                  ILangBusinessService vlService, ITranslationBusinessService translationService) {
        this.domainService = domainService;
        this.codeService = codeService;
        this.vlService = vlService;
        this.translationService = translationService;
    }

    public List<ImportCandidate> buildCandidates(Long domainId, List<InputTranslationBean> inputBeans) {
        Validate.notNull(domainId, "arg [domainId] is null");
        Validate.notEmpty(inputBeans, "arg [translations] is null or empty");

        List<ImportCandidate> ret = new ArrayList<>();

        Domains domain = fetchDomain(domainId);

        for (InputTranslationBean inputBean : inputBeans) {
            Tables table = fetchTable(domain, inputBean.getTableName());
            Codes code = (table == null) ? null : fetchCode(table, inputBean.getCodeName());
            Vl lang = fetchVl(inputBean.getLang());
            Trad translation = (code == null) ? null : fetchTranslation(code, lang);
            String transText = inputBean.getTranslation();
            String instructions = inputBean.getInstructions();
            ImportCandidate candidate = new ImportCandidate(domain, table, code, lang, translation, transText, instructions, inputBean);

            logger.debug("New import candidate built: [{}]", candidate);
            ret.add(candidate);
        }
        return ret;
    }
}

When i call the method getCurrentSession() throws an exception that SessionFactory cannot be injected and when i tried to create and inject a constructor for DefaultBusinessService then sessionfactory returned as null. I also binded the classes.

@Service
public class DomainsBusinessServices extends DefaultBusinessService 
                                     implements IDomainsBusinessServices
{
public Domains getDomainById(Long domainId, boolean fetchLangs,
            boolean fetchTables, boolean fetchCodes) {
        Criteria domainCriteria = getCurrentSession().createCriteria(Domains.class);
        domainCriteria.add(Restrictions.idEq(domainId));
        if(fetchLangs){
            domainCriteria.setFetchMode("Vls", FetchMode.JOIN);
        }
        if(fetchTables){
            domainCriteria.setFetchMode("Tableses", FetchMode.JOIN);            
        }
        if(fetchCodes){
            domainCriteria.setFetchMode("Codeses", FetchMode.JOIN);         
        }
        Domains domainVL = (Domains)domainCriteria.uniqueResult();
        if (domainVL != null) {
            Hibernate.initialize(domainVL.getVls());
        }
        return domainVL;
    }
}


@Service
public class DefaultBusinessService {

    @Inject
    private SessionFactory sessionFactory;

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    protected Session getCurrentSession(){
        return sessionFactory.getCurrentSession();
    }
}
kbou
  • 1
  • 4
  • Are you on Jersey v2.27 or v2.26? If so I know the solution – vikarjramun Jul 18 '18 at 19:09
  • I use Jersey v2.6 because my project uses Java 7. I would be glad if you propose some solution. – kbou Jul 19 '18 at 08:20
  • can you show me the import for `AbstractBinder`? – vikarjramun Jul 19 '18 at 13:56
  • Can you post the _complete_ stacktrace and the builder class. – Paul Samsotha Jul 19 '18 at 16:13
  • yes of course @vikarjramun , `import org.glassfish.hk2.utilities.binding.AbstractBinder;` – kbou Jul 20 '18 at 07:09
  • @paul i edited my question to contain the complete stacktrace. – kbou Jul 20 '18 at 07:41
  • And the builder class? – Paul Samsotha Jul 20 '18 at 07:49
  • i edited also @paul – kbou Jul 20 '18 at 11:46
  • @kbou I don't think it has to do with `bindToContract()` as I first thought, or with using an incorrect import, as you are using the same import I used until v2.25. I believe it may have to do with `ImportCandidateBuilder`. You mentioned posting code for that earlier, but I can't see it. I believe we will need that code to figure it out... – vikarjramun Jul 20 '18 at 16:05
  • @kbou ...You have found one of my biggest frustrations with JAX-RS: the configuration. It works very well when configured right - REST APIs are just a matter of annotating stuff, and Dependency Injection works well. However, I have spent so much time wiring together Dependency Injection and getting everything to work together. I hope you can solve the problem soon :) – vikarjramun Jul 20 '18 at 16:13
  • The only thing I can think of is that there is a problem creating the `ImportCandidateBuilder` class. Other than that, everything looks good. If can't figure it out and want to put together a minimal GitHub repo, I can take a look at it. But from the information you've provided (minus the requested builder class which you haven't provided), everything looks good. – Paul Samsotha Jul 21 '18 at 05:19
  • sorry i forgot to add it @paul..take a look at builder class also – kbou Jul 23 '18 at 14:37

1 Answers1

1

The ImportCandidateBuilder class can't be created because there is no default constructor. You have a constructor, but where are all those arguments supposed to be coming from

public ImportCandidateBuilder(IDomainsBusinessServices domainService,
                              ICodeBusinessService codeService,
                              ILangBusinessService vlService, 
                              ITranslationBusinessService translationService) {
}

When you want an injection framework to create the service, then there either needs to be a default constructor that will be called, or a constructor with arguments that needs to be annotated with @Inject and all the arguments must be also bound to the DI container.

@Inject
public ImportCandidateBuilder(IDomainsBusinessServices domainService,
                              ICodeBusinessService codeService,
                              ILangBusinessService vlService, 
                              ITranslationBusinessService translationService) {
}

register(new AbstractBinder() {
    @Override
    public void configure() {
        bind(DomainsBusinessServices.class)
                .to(IDomainsBusinessServices.class).in(Singleton.class);
        //.. bind others
    }
});
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • You are definitely right @Paul. I thought that service annotation was enough in that case. I will try it further because i have complex logic in code with a lot of DIs. – kbou Jul 25 '18 at 08:53
  • Your `@Service` and `@Contract` annotations don't do anything unless you are using [automatic service population](https://javaee.github.io/hk2/getting-started.html#automatic-service-population). And even with that, the same rule still applies in regards to how we handle constructors – Paul Samsotha Jul 25 '18 at 09:03
  • However it is a case where i cannot inject a repository class (SessionFactory.class). How can handle it @paul? – kbou Jul 25 '18 at 09:56
  • Well you need to bind the ServiceFactory to the DI system. You can use a `Factory` to create it and use `bindFactory`. Take a look at [this](https://stackoverflow.com/q/28045019/2587435) for some idea – Paul Samsotha Jul 25 '18 at 10:50
  • is it possible @paul to write some code for this case? I tried to use Factory and bindfactory but again my injection sessionFactory returned as null. – kbou Jul 26 '18 at 07:07