I have an EmailService EJB that has a very simple \'send_email\' method. I\'m receving the error in the title despite clearly having a public constructor that does not take para
The injection has not yet happened.
In short:
Thus extract the lookups, as suggested, into a postconstruct method
I would prefer to annotate your constructor with @Inject
instead:
@Stateless
@LocalBean
public class EmailService {
....
@Deprecated
public EmailService(){}
@Inject
public EmailService(ContextFinder ctf) {
username = (String) ctf.lookup(EMAIL_USERNAME_JNDI_NAME);
password = (String) ctf.lookup(EMAIL_PASSWORD_JNDI_NAME);
server = (String) ctf.lookup(SMTP_SERVER_JNDI_NAME);
port = (Integer) ctf.lookup(SMTP_PORT_JNDI_NAME);
}
...
}
>> Using @PostConstruct
is not exactly the same as you had. init()
method will not work outside the container like in JUnit test.
You still need to provide the default not parametrized constructor (part of the The EJB 3.1 spec, section 4.9.2 Bean Classes).
Just in case you are arriving here several years late as I did, it also turns out this error message may be misleading. I got the error,
[ERROR ] CNTR4006E: The EVENT_MANAGER_BEAN enterprise bean in the MySpecialEJB.jar module of the some-ear-DEVELOPMENT-SNAPSHOT application failed to start. Exception: com.ibm.ejs.container.EJBConfigurationException: EJB class com.notarealcompany.event.EventManagerEJB must have a public constructor that takes no parameters : some-ear-DEVELOPMENT-SNAPSHOT#MySpecialEJB.jar#EVENT_MANAGER_BEAN
at com.ibm.ws.ejbcontainer.jitdeploy.EJBUtils.validateEjbClass(EJBUtils.java:346)
at [internal classes]
Caused by: java.lang.NoClassDefFoundError: commonj/work/Work
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
at java.lang.Class.getConstructor0(Class.java:3075)
at java.lang.Class.getConstructor(Class.java:1825)
at com.ibm.ws.ejbcontainer.jitdeploy.EJBUtils.validateEjbClass(EJBUtils.java:337)
... 1 more
I don't think the error message is trying to say that such a method does not exist. Rather it's saying that the loader couldn't find one that does exist that can be successfully loaded. The actual error (caused by) is the NoClassDefFoundError on the class commonj.work.Work. It turns out that there is also a warning earlier in the log:
[WARNING ] CWNEN0047W: Resource annotations on the fields of the com.notarealcompany.app.ApplicationEJB class will be ignored. The annotations could not be obtained because of the exception : java.lang.NoClassDefFoundError: Lcommonj/work/WorkManager;
So the Application EJB didn't get its resources injected either because commonj.work.WorkManager could not be found. Why that's a warning and not an error is a good question. I'm sure the app will do just fine without those resources, right? Basically, the jar with the APIs for commonj.work just didn't get included with the deployment. In our case, that's because the old application server provided it whereas the new application server did not. A little adjustment to the POM file and we got past that issue. Very misleading error message in my opinion.
Why you don't remove initialization from constructor into a @PostConstruct method?
@Stateless
public class EmailService {
@EJB ContextFinder ctf;
private static final String EMAIL_PASSWORD_JNDI_NAME = "EMAIL_PASSWORD";
private static final String EMAIL_USERNAME_JNDI_NAME = "EMAIL_USERNAME";
private static final String SMTP_SERVER_JNDI_NAME = "SMTP_SERVER";
private static final String SMTP_PORT_JNDI_NAME = "SMTP_PORT";
private String username;
private String password;
private String server;
private Integer port;
@PostConstruct
public void init() {
username = (String) ctf.lookup(EMAIL_USERNAME_JNDI_NAME);
password = (String) ctf.lookup(EMAIL_PASSWORD_JNDI_NAME);
server = (String) ctf.lookup(SMTP_SERVER_JNDI_NAME);
port = (Integer) ctf.lookup(SMTP_PORT_JNDI_NAME);
}
public void sendMail(String sendTo, String subject, String message) {
// send mail
}
}