I was trying to migrate from payara/micro:4.181 to payara/micro:5.2022.3 and I noticed that the initialization method with observer [@initialized(ApplicationScoped.class) ServletContext init] is not invoked inside Jar files.
public void init(@Observes @Initialized(ApplicationScoped.class) ServletContext init)
It is invoked correctly when using payara/micro:4.181 though.
To reproduce the described behaviour:
#FROM payara/micro:5.2022.3
FROM payara/micro:4.181
COPY app.war $DEPLOY_DIR
uncomment the line corresponding to the version of payara/micro you want to run the app with.
package mylib;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Initialized;
import javax.enterprise.event.Observes;
import javax.servlet.ServletContext;
@ApplicationScoped
public class Library {
public boolean someLibraryMethod() {
return true;
}
public void init(@Observes @Initialized(ApplicationScoped.class) Object init) {
System.out.println(" ### log-1 mylib.Library.init(java.lang.Object) called ###");
}
public void init(@Observes @Initialized(ApplicationScoped.class) ServletContext init) {
System.out.println(" ### log-2 mylib.Library.init(javax.servlet.ServletContext) invoked ###");
}
}
Thanks in advance for any eventual reply/hint
I think there is a misunderstanding about what to expect from @Observes @Initialized(ApplicationScoped.class)
. The JakartaEE specs (8) say:
An event with this qualifier is fired when a context is initialized, i.e. ready for use.
And "context" here is meant in the sense of CDI context, which ServletContext
is definitely not.
Also I found no documentation whatsoever about what the type of the event message will be. Hence this is totally implementation specific and there should be made no assumptions about it (if you want to keep your code portable).
If your Library
object received that event twice (once with Object
and once with ServletContext
) in Payara 4, then this can be considered a mistake (not to say bug) of that platform.
I'd recommend to either implement a javax.servlet.ServletContainerInitializer
or a javax.servlet.ServletContextListener
, if you want to be notified about server startup and get hold of ServletContext
during that phase. The big advantage of ServletContextListener
(over the other) is that you can easily interact with CDI beans (@ApplicationScoped
or @Singleton
).
Here is an example:
package mylib;
import javax.inject.Inject;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@WebListener
public class MyListener implements ServletContextListener {
@Inject
Library library;
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println(" ### log-4 mylib.MyListener.contextInitialized() invoked ###");
System.out.println(sce.getServletContext());
System.out.println(library);
System.out.println(library.someLibraryMethod());
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println(" ### log-5 mylib.MyListener.contextDestroyed() invoked ###");
System.out.println(sce.getServletContext());
}
}
(tested with your Docker project)
Hope this helps.
I also found the following Gist which seems to be exactly what you want to achieve: https://gist.github.com/mojavelinux/637959
Note that here @Initialized
and @Destroyed
are custom qualifiers and not the ones from package javax.enterprise.context
.
Additionally here is an updated version of that Gist using the qualifiers from javax.enterprise.context
: https://gist.github.com/nineninesevenfour/d9c643ff5a7f98302f89687720d0a138. With that your original code of Library
can to stay unchanged.
With the updated Gist however these two methods are called twice (once from Payara and once triggered from ServletContextLifecycleNotifier
:
// Library.java
public void init(@Observes @Initialized(ApplicationScoped.class) Object init) {
System.out.println(" ### log-1 mylib.Library.init(java.lang.Object) called ###");
}
// App.java
public static void init(@Observes @Initialized(ApplicationScoped.class) Object init) {
System.out.println(new App().getGreeting());
}