Are CDI event observer methods compatible with EJBs? - glassfish

I have a Singleton EJB (javax.ejb.Singleton version. sigh.) which has a CDI observer method on it. When I try to deploy this to glassfish 3.1 the server fails to deploy the EAR file without any real explanation - simply saying there was an exception during deployment without any more details.
SEVERE: Exception while loading the app
SEVERE: Exception while shutting down application container
....
SEVERE: Exception while shutting down application container : java.lang.NullPointerException
This is the CDI event listener :
public void updateFromGranule(#Observes #CloudMask GranuleAvailableEvent granuleEvent) {
LOG.info("updating cloud map");
update(granuleEvent.getGranule(), CloudMask.class);
fireUpdate();
}
If I change the Singleton bean to just be an #ApplicationScoped bean the app deploys fine. Similarly, if I remove the CDI event observer method the application deploys fine.
I actually need the class to be an EJB singleton because I want the transaction, thread safety etc. of EJBs, so just leaving this as a #ApplicationScoped POJO isn't much use to me. The problem doesn't seem to be limited to Singleton beans though - I've experimented by changing the annotation to #Stateless and #Stateful and I get the same issue.
It seems to me that this might be a bug in Weld, perhaps Weld and EJB are fighting about how they proxy that method - presumably EJB needs to add an interceptor class and wrap that method to ensure thread safety, and Weld is trying to do something else to make the event listener work?
Am I misunderstanding something here, and should CDI event handlers simply not be used on EJBs (in which case there should be better error messages from glassfish) - or is this actually just a bug in the CDI or EJB implementation?

I think this is the answer :
CDI observer methods must apparently either be static or declared in the local interface of an EJB if the EJB declares a local interface. Normally if you try to declare an observer method that isn't in the local interface you get an exception from Weld like this :
org.jboss.weld.exceptions.DefinitionException: WELD-000088 Observer method must be static or local business method: [method] public org.stain.ObserverBean.testMethod(EventClass) on public#Singleton class org.stain.ObserverBean
For some reason glassfish does not report this exception properly when loading my EAR file and simply says Exception while loading the app.
Adding the method to the local interface (or removing the interface declaration on the class) fixes the problem and allows the application to load normally.

I noticed the same problem with the latest version of weld. But if you add the #LocalBean annotation it will work with #Singleton and #Singleton #Startup.

Related

Hangfire cannot find registered service

I have an issue with Hangfire, most likely because of my ignorance about some topics.
I have a host/plugins infrastructure, where each plugin is loaded at runtime and it register its interfaces.
public void ConfigureServices(IServiceCollection services, IConfigurationRoot Configuration)
{
services.AddTransient<IManager, Manager>();
services.AddTransient<IAnotherManager, AnotherManager>();
this.AddControllers(services);
}
Some plugin may add jobs using Hangfire, which are also set during runtime
public void ScheduleJobs()
{
RecurringJob.AddOrUpdate<IManager>(n => n.SayHello(), Cron.Monthly);
}
The issue I have is, while any service registered directly in the host is correctly resolved in hangfire,
all the interfaces (ex IManager) that are defined in external assemblies aren't found.
I added a customer JobActivator where I'm passing the IServiceCollection and I can actually see that those external services are registered (and I can use them anywhere else but from Hangfire), but still
in the JobActivator, when Hangfire tries to resolve the external service, it fails.
public override object ActivateJob(Type type)
{
// _serviceCollection contains the IManager service
var _provider = _serviceCollection.BuildServiceProvider();
// this will throw an Exception => No service for type '[...].IManager' has been registered.
var implementation = _provider.GetRequiredService(type);
return implementation;
}
In the same example, if I use the Default JobActivator, then the exception I get is System.MissingMethodException: Cannot create an instance of an interface.
I could enqueue the job using the Class instead of the Interface, but that's not the point and anyway if the Class has services injected, those will not be resolved as well.
What am I missing?
The problem has been solved. The solution is to add a specific IoC Container for hangfire. I used Unity. In that way dependencies are resolved correctly.
Thanks Matteo for making it clear that HF requires its own IoC container. This link makes the point too:
Hangfire needs to have it's own container with dependencies registered independently of the global UnityContainer. The reason for this is twofold; Hangfire's dependencies need to be registered with the PerResolveLifetimeManager lifetime manager. This is so that you don't get concurrency issues between workers that have resolved a dependency to the same instance. For example; with the normal HierarchicalLifetimeManager, two workers needing the same repository dependency may resolve to the same instance and share a common db context. The workers are meant to each have their own db contexts. Secondly, when the OWIN bootstrapper is run, the global UnityContainer may or may not be initialised yet and Hangfire is unable to take in a reference to the container. So giving Hangfire it's own managed container is a clear separation of purpose and behaviour in how our dependencies are resolved.

Error creating bean named `conversionServicePostProcessor` when using spring-boot-admin-server

I was trying to enable Spring boot admin server for my application. The default settings work perfectly fine but when I attempt to enable security, I am getting following error:
APPLICATION FAILED TO START
Description:
The bean 'conversionServicePostProcessor', defined in class path
resource
[org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class],
could not be registered. A bean with that name has already been
defined in class path resource
[org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.class]
and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting
spring.main.allow-bean-definition-overriding=true
Process finished with exit code 1
I am using the latest SNAPSHOT version of spring-boot-admin-starter-server (2.2.0-SNAPSHOT). Here is my security configuration:
#EnableAdminServer
#EnableWebFluxSecurity
#Configuration(proxyBeanMethods = false)
class AdminServerSecurityConfigurations(val adminServerProperties: AdminServerProperties) {
#Bean
fun adminServerSecurityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain = http
// #formatter:off
.authorizeExchange()
.pathMatchers("${adminServerProperties.contextPath}/assets/**").permitAll()
.pathMatchers("${adminServerProperties.contextPath}/login").permitAll()
.anyExchange().authenticated().and()
.formLogin().loginPage("${adminServerProperties.contextPath}/login").and()
.logout().logoutUrl("${adminServerProperties.contextPath}/logout").and()
.httpBasic().and()
// #formatter:on
.csrf().disable()
.build()
#Bean
fun notifyLogger(instanceRepository: InstanceRepository) = LoggingNotifier(instanceRepository)
}
I found a pull request to update the documentation: https://github.com/spring-projects/spring-boot/issues/14069
For Reactive WebSockets,
{spring-reference}web-reactive.html#webflux-websocket[Spring WebFlux] offers rich support,
which is accessible through the spring-boot-starter-webflux module.
See the spring-boot-sample-websocket-reactive sample project to see how WebSockets may
be implemented using Spring WebFlux.
it turns out that using webflux and websocket leads to conflicts.
also in this pull request was denied in the resolution of the conflict
https://github.com/spring-projects/spring-boot/issues/14810
for reactive websocket see this sample https://www.baeldung.com/spring-5-reactive-websockets
I had the same issue and was able solve it by adding
spring.main.allow-bean-definition-overriding=true
to my application.properties.
Sounds like a workaround and it was also only necessary if I deployed it as WAR -- as a standalone application the exception never occured.
I also faced this error, after Reimport All Mavne Projects(Intellij IDE) it works fine for me. Here my detailed input on this issue here

Glassfish 3: Calling method on ejb in ServletContextListener.contextDestroyed method leads to exception

I have an #Singleton ejb which will be called in a ServletContextListener contextInitialized and contextDestroyed method. The call in the contextDestroyed method leads to an Exception:
javax.ejb.EJBException: Attempt to invoke when container is in STOPPED
It looks as if the contextDestroyed method is called after the container went down leaving
me with no chance to access any resources like my ejb.
Does anyone know if there is any way to know about application shutdown before the container
is down?
Thanx in advance.
Cheers, Sven
You could use the #PreDestroy annotation on a method directly in your #Singleton bean. Thus marked, the method will be called before the bean destruction, during the application shutdown.

Differences in calling JMX MBean remotely or from servlet deployed in the same JVM

I have several applications deployed on WebLogic server. Those applications expose some JMX MBeans. When I call operations on those MBeans remotely through JConsole or JRMC, they work fine.
But since JMX is not really firewall-friendly I have created another simple Servlet application that is deployed on the same server and that invokes operations on a set local MBeans. From within servlet I use ManagementFactory.getPlatformMBeanServer() to find and call MBeans of other applications deployed in the same JVM, but for some operations I get either ClassCastException or ClassNotFoundException depending on the operation that is called, but some operations work fine.
Any ideas?
I suspect what is happening here is that your servlet thread has a context classloader that is different from that of the MBeans that you are invoking against. Consequently, if the MBean attributes, operation parameters or return values contain types that are not core JVM classes (or classes not shared from the same root classloader), you will get ClassCast, ClassNotFound and ClassDefNotFound Exceptions.
This procedure may work for you. What you need to do is temporarily change the context classloader of the servlet thread to the same classloader as the MBean was loaded from. Once the invocation is complete, you set it back again. Since you know the ObjectName of the target MBean, the MBeanServer will supply you the correct classloader.
Here's a basic example:
public void callMBean() throws MalformedObjectNameException, NullPointerException, InstanceNotFoundException {
final ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
try {
ObjectName targetObjectName = new ObjectName(".....");
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
ClassLoader tmpClassLoader = server.getClassLoaderFor(targetObjectName);
Thread.currentThread().setContextClassLoader(tmpClassLoader);
// ==========================================
// Invoke operations here
// ==========================================
} finally {
Thread.currentThread().setContextClassLoader(currentClassLoader);
}
}

Servlet Exception + Class Cast Exception + Glassfish + Netbeans + JPA Entities + Vaadin

I get this error:
StandardWrapperValve[Vaadin Servlet]: PWC1406: Servlet.service() for servlet Vaadin Servlet threw exception
java.lang.ClassCastException: com.delhi.entities.Category cannot be cast to com.delhi.entities.Category
when I try to run my webapps on glassfish v2.
Category is a JPA entity object
the offending code according to the server log is:
for (Category c : categories) {
mymethod();
}
categories is derived from:
List<Category> categories = q.getResultList();
Any idea what went wrong?
This is a class loader issue. If a class is loaded by different class loaders, it's objects cannot be assigned to each other. You have probably passed an object from one WAR into another one. There are several options to resolve this:
Put all your code into a single WAR.
Use some form of remoting between your WARs. Serialization takes care of the class loader problem.
Try putting all you WARs into a single EAR. If that doesn't work, put all code into JARs that are on the EAR's Classpath in the MANIFEST.MF.
I once had the same problem and the environment I had was following:
I had Glassfish v4
Netbeans with following projects
webpage war project containing entities
and ear project with that webpage war project
The problem was that in war's project settings I had checked [x] Run>Deploy on save. This was causing deploying war project everyime I hit save. It was sometimes leading to PermGen (memory) problems and unability to deploy EAR correctly (because e.g. in between undeploying and deploying EAR - this "crazy" Netbeans was deploying this war).
Solution: If Netbeans && using EAR, then uncheck deploy on save in project properties.
EDIT:
it seems that this error is connected with
SEVERE: The web application [/faces] created a ThreadLocal with key of type [org.glassfish.pfl.dynamic.codegen.impl.CurrentClassLoader$1] (value [org.glassfish.pfl.dynamic.codegen.impl.CurrentClassLoader$1#249ea63a]) and a value of type [org.glassfish.web.loader.WebappClassLoader] (value [WebappClassLoader (delegate=true; repositories=WEB-INF/classes/)]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
I've had same problem today. Solution was closing EntityManagerFactory after use.
This answer helped me:
https://stackoverflow.com/a/13823219/2455506
I'm experiencing this problem too with Glassfish v2 and Glassfish v3.
Can I ask you a question: Are you attempting to initialize any persistence object when the application is deployed (through a servlet loaded on startup or a context listener)?
Like bguiz, I've noticed this problem only happens on redeploy. A new deploy to a freshly restarted Glassfish server, never has this problem.
Like FelixM mentioned, I'm convinced this is a class loader issue, however I don't believe it's an issue with multiple wars (I only have 1 deployed to my server). In Glassfish 3, I can see that my WAR is utilizing 2 Glassfish "engines". One for the web(war) and one for the jpa. From what I understand, these are different containers each with their own classloader. I'm guessing Glassfish v2 works in the same manner.
I'm using Spring and (re)initialize some persistence objects on (re)deploy. What I'm thinking, is that while the web engine is reinitializing the war, the jpa engine is still using the old class definitions. Often if I retry the redeploy after this initial failure, it may succeed (sometimes it may take more than one retry but eventually I can get it to succeed without a restart - having better success with Glassfish v3 than v2).
At this point I'm thinking that either these two classloaders are out of sync or there is some sort of race condition on redeploy allowing this operation to sometimes succeed. I've tried to force the classloader, writing code like this
HashMap<Object, Object> properties = new HashMap<Object, Object>();
properties.put(PersistenceUnitProperties.CLASSLOADER, this.getClass().getClassLoader());
entityManagerFactory = Persistence.createEntityManagerFactory(jpaContext, properties);
but it didn't seem to have any affect.
I'm also wondering if eliminating the initialization at startup could fix the problem, giving the appserver time to resynchronize both engines before using any jpa classes (which is why I asked my follow up question).
My observation is that it only happens when using a hot redeploy or a static redeploy. This only applies, of course, if you get a class cast exception where both the to and from classes are the same.
Workarounds:
Don't use undeploy and deploy instead of redeploy
Restart app server
Remove static members of the affected classes
Use a remote interface (serialization makes this go away)
IMO I think the class loader was unable to reload the class and the old version was reused, resulting in the error.
This article doesn't talk about this error directly, but it is good background info on how the class loader works.