user3141525
user3141525

Reputation: 1

Dependency injeection at runtime in Java

I have read through the dependency capabilities through Java CDI but could not figure out so far how to inject a class by runtime. Let me explain the scenario first.

Lets assume I have a JSF web app with a central Email service.

I am defining an interface

public interface EmailService {

public String sendEmail(Email email);
}

Then next I have a concrete implementation of the EmailService using Smtp:

public class SmtpEmailServiceImpl implements EmailService {

@Override
    public String sendEmail(Email email) {
    // concrete implementation using Smtp
    }

}

Now in my web app I am having a JSF backing bean that should get the EmailService injected in order to sende the e-mail

public class JSFBackingBean {
    // This is the EmailService to be injected
    private EmailService emailService;


    public String sendEmail(){
    emailService.sendEmail(new Email());
    }
}

Now lets assume the Smtp-Server is down for maintenance. In this scenario I would like to spool all the Emails in a Database and process them later when the Smtp server is up and running. In this case I would like to have a second implementation of the EmailService:

public class DatabaseEmailService implements EmailService {

@Override
    public String sendEmail(Email email) {
    // concrete implementation writing the email to a database
    }

}

Now I understand from CDI that I can use Annotations to inject the proper service implementation but that would mean that I would have to re-build and deploy my classes in case I would like to change the appropriate service. Is there a better solution where I can use e.g. a configuration file in order to change the injection at runtime of the application?

Thanks for your answers in advance Pred

Upvotes: 0

Views: 99

Answers (2)

Jan Galinski
Jan Galinski

Reputation: 11993

In cases like this, you could write a custom Producer and Qualifier. Instead of injecting the EmailService, inject for example a "@Failsafe EmailService".

Then write a producer

@Produces
@Failsafe
private EmailService failsafeEmailService() {
   // here you can check if the Mail Server is available and then decide 
   // to return the "real" Service or the DB-Queue. 
}

Instead of creating/looking up the Services inside the method body, you could also let CDI inject both alternatives (directly or via Instance<>) and then decide which one to propagate.

@Produces
@Failsafe
private EmailService failsafeEmailService(MailServiceBean bean, DBQueue queue) {
   return (check_if_mail_server_is_running) ? bean : queue
}

(of course both DBQueue and Bean have to implement EmailService).

Upvotes: 1

John Ament
John Ament

Reputation: 11723

Well, I doubt you want to make it so that every client to this email service is aware that the mail service needs to be switched out, even if you used annotations and instance selectors, e.g.:

@Inject
private Instance<EmailService> emailServiceInstance;

// few lines down

emailServiceInstance.select(new SmtpLiteral()).get();

Which is how you would do it in a CDI fashion. What I would recommend is that this logic belongs in your EmailService and itself injects a reference to some DB class that persist the message to the database until the SMTP server is back online.

Upvotes: 0

Related Questions