Quarkus СacheManager injection in integration test - kotlin

I have a Quarkus application that makes use of caches for methods.
These methods and cache eviction got to be tested somehow preferably when Quarkus context is fully operational.
This is what I figured (PostgresContainer for reference):
#QuarkusTest
class ScreeningRepositorySpec : PostgresContainer() {
private val cacheManager = CaffeineCacheSupplier().get()
init {
"test cache manager gets initialized" {
logger().info("Cache size: {}", cacheManager.size)
}
}
}
The problem arises when any kind of invocation happens for cacheManager: it fires NPE. https://github.com/im-infamou5/quarkus-cache-playground
Downstream code as follows is
#Override
public List<CaffeineCache> get() {
CacheManager cacheManager = cacheManager();
...
}
which ultimately yields:
public static CacheManager cacheManager() {
return Arc.container().instance(CacheManager.class).get();
}
And here comes that Arc.container() is null somehow.
What else was tried:
#QuarkusIntegrationTest - no bean for injection, null for arc container
Explicit #Inject for CacheManager - yields "no bean matches injection point"
Explicit Cache definition with manual CacheManager instantiation - same issue with null arc container
Variations for #Inject for default bean injection - same things about missing injection point
Looks like CacheManager bean lifecycle issue that expects it way too early and never succeeds as a result.
I've dug into quarkus tests which have quite a workaround but still hope easier approach is available to avoid this much dependensies just to test cache properly.
Version of Quarkus is 2.7.0.
Sample project can be found here with simple type of direct injection.
Output as follows:
org.acme.GreetingResourceIT > test cache size FAILED
kotlin.UninitializedPropertyAccessException at GreetingResourceIT.kt:11
Caused by: kotlin.UninitializedPropertyAccessException at GreetingResourceIT.kt:11

Related

Is it possible to add an instance of singleton framework to CDI context?

I'm trying to integrate StarMX framework (https://github.com/rogeriogentil/starmx) into a legacy web application. This framework uses JMX techonology and is initialized using the Singleton pattern: StarMXFramework.createInstance(). The web application uses Java EE 6 technologies such as EJB and CDI (also DeltaSpike). However, the way the framework is being initialized (code below) doesn't add its instance to the CDI context.
import org.starmx.StarMXException;
import org.starmx.StarMXFramework;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Singleton;
import javax.ejb.Startup;
#Startup
#Singleton
public class StarMXSingleton {
private StarMXFramework starMX;
#PostConstruct
public void postConstruct() {
try {
starMX = StarMXFramework.createInstance();
} catch (StarMXException e) {
(...)
}
}
#PreDestroy
public void preDestroy() {
if (starMX != null) {
try {
starMX.shutdown();
} catch (StarMXException e) {
(...)
}
}
}
}
I know that is possible to extend CDI, but is it possible to add an instance of singleton framework to CDI context?
There are two ways, first and easy one is a producer. Here is a link to what CDI producers are and how they work. In short, CDI will use this producer to create the instance of a bean whose types are mandated by the return type of the producer method.
The producer method has to be placed inside a CDI bean so that is it picked up by CDI. Note that the scope of the producer affects how often it will be invoked, just as it would be with standard bean. Here is how it could look like:
#ApplicationScoped
public class SomeCdiBeanInYourApplication {
#Produces //denotes producer method
#ApplicationScoped // scope of produced bean, use CDI scope (the singleton you have is EJB annotation)
public StarMXFramework produceMxFramework() {
return StarMXFramework.createInstance();
}
}
Second means is then CDI extension, namely a lifecycle observer for AfterBeanDiscovery event where you can addBean(). Here is a link to CDI 2.0 spec, feel free to browse older versions based on what version you are on.
I won't write code for that as it is rather complex and long, the producer should do the trick for you.
See also
Please explain the #Produces annotation in CDI

Java EE - dependency injection into batchlet

I am having issues with dependency injection in a batchlet.
#Named
public class SimpleBatchlet extends AbstractBatchlet {
#Inject
protected StorageService storageService;
...
public String process() throws Exception {
storageService.doSomething(); // this throws a null pointer exception
}
}
#Named
public class LocalFileStorageService implements StorageService {
public void doSomething() {
}
}
I have tried putting beans.xml in both META-INF and WEB-INF and removing it, all to no avail. I also tried changing the scopes of the beans to singletons, etc. I am invoking / starting the batch job through the use of an #Schedule annotation on a method that uses BatchRuntime to start the job.
I must be missing something simple as I know this should work. The actual scope of the beans I will use may need to vary, but the point I am trying to make is that I don't believe bean scope is a problem, but some other configuration issue.
I should also note that I only have 1 implementation of StorageService.
Not clear what really is your problem (NPE on injected CDI bean?), but annotating your Batchlet #Dependent should solve the problem :
#Named
#Dependent
public class SimpleBatchlet extends AbstractBatchlet {
#Inject
protected StorageService storageService;
}
Batchlet need to be #Named and #Dependent for integration with CDI.

EJB Singleton service fails at deploy

I'm rather newbie to JBoss and annotations. I have following code example. Irrelevant details are cutted out.
#Singleton
#Startup
public class SomeBean {
#Resource
TimerService timerService;
#Inject
AnotherSingleton anotherOne;
Timer timer;
#PostConstruct
private void ejbCreate() {
timer = timerService.createIntervalTimer(0, interval, tc);
}
#Timeout
public void run() throws Exception {
}
}
#Singleton
public class AnotherSingleton {
#Inject
Repository rep;
}
There is case that when war is deploying on JBoss it fails with exception from Repository producer (service in another Jboss is not available).
Caused by: java.lang.IllegalStateException: WFLYEE0042: Failed to construct component instance
So process ends with
WFLYCTL0186: Services which failed to start: service jboss.deployment.unit."someservices-view.war".component.SomeBean.START
What options do i have?
Can i tell JBoss to don't #Inject beans on startup but when code is executed by timer?
Can i somehow catch exception?
#Schedule is out of question becaouse i need to configure Timer.
Injections are handled by the CDI specification which provides a feature to "wrap" injections as it were, like so.
#Inject
Instance<AnotherSingleton> anotherOneInstance;
This basically creates a proxy around the AnotherSingleton and you can delay obtaining an actual reference to it at the time that you need it.
AnotherSingleton anotherOne = anotherOneInstance.get();
This should allow deployment to succeed and your timer to initialize, but of course if at the moment you attempt to use anotherOne and the repository is not available, the code will still break with an exception.
Alternatively, you can always do a manual lookup through the BeanManager to not have to rely on any form of dependency injection, but that should always be a last resort as it just leads to cumbersome code.

EJB Injection failure on deploy

I've got a problem exxh EJB's.
First of all, my setup: I am using GlassFish & JEE6. I have got a REST-Service packaged as a WAR and a bean packaged as an EJB-Jar. They are not inside an EAR.
The EJB should be used from the REST-WAR via #EJB, but when I try to deploy the WAR, GlassFish shows this error:
Error occurred during deployment:
Exception while deploying the app [exx-upload-1.0] : Cannot resolve reference Local ejb-ref name=com.ex.exx.model.FileUpload/ocr,Local 3.x interface =com.ex.exx.api.IOCRService,ejb-link=null,lookup=,mappedName=,jndi-name=,refType=Session. Please see server.log for more details.
(The EJB was deployed before without any erros).
I have no clue why. Here is the EJB Code:
Interface:
#Local
public interface IOCRService {
public String performOCRonImage(BufferedImage input);
}
and Implementation:
#Stateless
#LocalBean
public class OCRScanner implements IOCRService {
private Logger logger = Logger.getLogger(this.getClass().getName());
private final static String NOT_RECOGNIZED = "Can not regocnize text";
/**
* Default constructor.
*/
public OCRScanner() {
logger.log(Level.INFO, "### OCR SCANNER BUILD" + this);
}
public String performOCRonImage(BufferedImage input) {
logger.log(Level.INFO, "### OCR SCANNER CALLED" + this);
}
...
And here is the important part in the WAR:
public class FileUpload {
private final File PROPERTIES_FILE = new File(
"fileUploadProperties.properties");
private final String PARAMETER_NAME = "file";
private final Logger logger = Logger.getLogger(this.getClass().getName());
#EJB
private IOCRService ocr;
public Response uploadFile(...) {
// do some stuff
logger.log(Level.INFO, "### EJB" + ocr.toString())
}
Anny suggestions? I can not find my failure here.
Solved this, by replaceing #Local with #Remote.
This works, however, I am not satisfied as I do not understand why.
Basically, given the specs (eg. explained in the tutorial), an application can only access other application's EJB, if they are decorated with #Remote.
Thus, you have 3 options:
decorate your EJB with #Remote (what you have done),
package both together inside an ear (as they would reside in the
same application then). But if you intent to deploy them in seperate
applications or even seperate servers, use 1.)
use CDI with #Inject, but this will still only discover the EJB if
either in the same application, or decorated as #Remote if not.
HTH,
Alex
You should not use #EJB if the target is not an EJB. I guess this is your case because you are trying to inject into a class in your WAR.
Instead use:
#Inject
private IOCRService ocr;
Basically, #Inject is better in most cases, because:
it is more typesafe,
it supports #Alternatives
it is aware of the scope of the injected object.
Another solution it's to add #Stateless(name=""), this worked form

Annotation JCacheResult is not working in Infinispan and Glassfish 3.1.1

I am trying to integrate JCache from Infinispan into my existing EJB project.
I have added Infinispan 5.0.1 CDI and Core packages to Maven pom.
Added Infinispan Interceptors in beans.xml and able to use the CacheResult annotation.
I am deploying the app in Glassfish 3.1.1. I have checked the Weld jar version, which is
Module : org.jboss.weld.osgi-bundle:1.1.1.Final
In the runtime, the CacheResult Method interceptor is not caching the method result and its always called.
My code looks like this,
public void cacheTest() {
Thread.currentThread().setContextClassLoader(
this.getClass().getClassLoader());
EmbeddedCacheManager manager = createCacheConfig();
Set<String> cacheList = manager.getCacheNames(); // new
// DefaultCacheManager().getCacheNames();
for (String cache : cacheList) {
System.out.println("Cache name " + cache);
}
defaultCache = manager.getCache("test-cache");
defaultCache.put("aa", "AA");
String user = "User";
greet(user);
Set<String> keys = defaultCache.keySet();
for (String key : keys) {
System.out.println("Key is -" + key + "Value is -"
+ defaultCache.get(key));
}
}
#CacheResult(cacheName = "test-cache")
public String greet(#CacheKeyParam String user) {
user += "Hello";
return user;
}
public EmbeddedCacheManager createCacheConfig() {
EmbeddedCacheManager manager = new DefaultCacheManager();
Configuration conf = new Configuration();
conf.fluent().eviction().strategy(EvictionStrategy.FIFO).maxEntries(10)
.expiration().maxIdle(1200000L).build();
conf.fluent().clustering().sync();
manager.start();
manager.defineConfiguration("test-cache", conf);
return manager;
}
greet() method gets called but it will never add the method result to the test-cache. I feel am I missing some configuration or...I dont know. Please help me on this.
when I Inject the classes, they wont get constructed and they are null. The code is like this,
#Inject
private static org.infinispan.Cache<String, String> defaultCache;
#Inject
private static EmbeddedCacheManager defaultCacheManager;
These gets executed without any error, but they wont get initialized.
I have no clue...But I am able to inject other EJBs with in this class easily. By the way I am trying to add Jcache functionality in one of EJBs.
I would appreciate your help...
Thanks...
Raj S
Your greet method is in a CDI bean or in an EJB, right?
The cache defined in JCache annotations is looked up in the cache manager provided by Infinispan CDI. This cache manager contains the cache configured with CDI (for more information, see https://docs.jboss.org/author/display/ISPN/CDI+Support). In your example the test-cache configuration will have no effect.
Another thing, if your cacheTest and greet methods are in the same class the greet method cannot be intercepted. If that's not the case maybe you're hitting GLASSFISH-17184.
For the Cache and EmbeddedCacheManager injections the problem is that you're doing a static injection, not supported by CDI. From CDI (JSR-299) specification
An injected field is a non-static, non-final field of a bean class, or of any Java EE component class supporting injection.
If your method result is not cached, I think it's because the CacheResultInterceptor is not called. I've just made the test with the Infinispan CDI quickstart. If the interceptors are in a lib they are not enabled. I think it's a bug in Glassfish.
Btw, you can see an example of code in the Infinispan CDI quickstart here.
Hope this help!