ClassCastException while reading from Infinispan Cache after redeployment on Wildfly 8.2 - java

I have a simple Infinispan local-cache (also tried distributed cache) on Wildfly 8.2. Everything is working fine until I do redeployment of my .WAR. After redeployment of my .WAR I get the following error:
Caused by: java.lang.ClassCastException: my.package.MyClass cannot be cast to my.package.MyClass
Full stacktrace: https://gist.github.com/bagges/07af1842a874f7c99ef3
I lookup the Cache in a CDI Bean like this:
#Path("/mypath")
#Stateless
public class MyServiceClass {
#Resource(lookup = "java:jboss/infinispan/myContainer")
private CacheContainer container;
private Cache<Integer, MyCacheObject> myCache;
#PostConstruct
public void start() {
myCache = container.getCache("myCache");
}
#GET
public String get() {
if(!myCache.containsKey(1)) {
myCache.put(1, new MyCacheObject(1, "Hello Cache"));
}
return myCache.get(1).getName();
}
}
Wildfly-Config:
<cache-container name="myContainer" jndi-name="java:jboss/infinispan/myContainer" start="EAGER">
<local-cache name="myCache"/>
</cache-container>
I know that the error occured because off different class loaders. Infinispan tries to cast the entity stored with the previous classloader which cannot work. But how to avoid this?

Don't use start="EAGER". That will fix your problem.
We've removed this from WildFly 9, since its misuse has been the source of many user headaches.
Also, I recommend injecting your cache directly (instead of just the cache container). That way the cache lifecycle will be bound to the lifecycle of your deployment.
e.g.
#Resource(lookup = "java:jboss/infinispan/cache/myContainer/myCache")
private Cache<Integer, MyCacheObject> myCache;
Lastly, feel free to use a resource-ref to avoid referencing a vendor-specific jndi namespace in your application.

You should be able to share the cache if you enable store-as-binary in the Infinispan configuration and you force the cache to use the application's classloader instead of the one in the GlobalConfiguration:
Cache appSpecificCache = cacheFromJndi.getAdvancedCache().with(applicationClassLoader)

Related

Tomcat context is empty when accessed via executor and runnable

Hello I have a web application running on apache-tomee-plus-8.0.1. My problem is about getting an Environment variable from a runnable in a custom executor. The variable is defined in /conf/context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<!-- Default set of monitored resources. If one of these changes, the -->
<!-- web application will be reloaded. -->
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>
<WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
<!-- disable the scan in order to avoid errors at startup due to ora18n.jar-->
<JarScanner scanManifest="false" scanClassPath="false" scanBootstrapClassPath="false"></JarScanner>
<!-- base64 from user:pass -->
<Environment name="myCreds"
value="toto" type="java.lang.String" />
</Context>
The function I use to get the variable "myCreds"
private static String getCredentialsFromContext() throws NamingException {
Context initialContext = new InitialContext();
Context environmentContext = (Context) initialContext.lookup("java:comp/env");
return (String) environmentContext.lookup("myCreds");
}
This function is called via a JAX-RS endpoint which is used to start long background maintenance tasks of the server application. The progress of the background task is then available on another endpoint.
If I do
#GET
#Path("/testOK")
public static String testOK() {
return getCredentialsFromContext(); // works fine
}
But when I use an executor, the lookup fails with
javax.naming.NameNotFoundException: Name [comp/env] is not bound in this Context. Unable to find [comp].
private static ExecutorService index_executor;
#GET
#Path("/testKO")
public static Response testKO() {
if (index_executor == null){
index_executor = Executors.newFixedThreadPool(5);
}
index_executor.submit(new Runnable() {
#Override
public void run() {
System.out.println(getCredentialsFromContext()); // FAIL
}
});
return Response.ok.build()
}
It looks like the InitialContext is not the same when called from the runnable. I would like to avoid to pass through args the value of "myCreds". I tried to move the declaration of "myCreds" in the context.xml of the webapp but it didn't help. Using JNDIContext also fails.
Do you understand what is the problem and why the context is different?
Thanks :)
JNDI lookups depend on some context information on the running thread, usually the context class loader.
On a Java EE/Jakarta EE server you should not spawn new (unmanaged) threads yourself, but use the ManagedExecutorService provided by the container. This service automatically propagates some kinds of contexts from the calling thread:
The types of contexts to be propagated from a contextualizing application component include JNDI naming context, classloader, and security information. Containers must support propagation of these context types.
(Jakarta Concurrency Specification, emphasis mine)
You can inject a ManagedExecutorService using a #Resource annotation:
#Resource
private ManagedExecutorService executorService;
Using a ManagedExecutorService works on Wildfly, but on TomEE there is IMHO a bug that prevents the propagation of the naming context: JAX-RS resources use CxfContainerClassLoader as context classloader, which wraps the real classloader, preventing it from propagating to the managed thread.
A workaround would consist in switching temporarily to the wrapped classloader:
final ClassLoader tccl = Thread.currentThread().getContextClassLoader();
if (tccl instanceof org.apache.openejb.util.classloader.Unwrappable) {
final ClassLoader cl = ((org.apache.openejb.util.classloader.Unwrappable) tccl).unwrap();
Thread.currentThread().setContextClassLoader(cl);
}
executorService.submit(...);
Thread.currentThread().setContextClassLoader(tccl);
Edit: actually, it is enough to mark the JAX-RS resource as #Stateless for the correct propagation of the JNDI naming context.

EJB with CMT when migrate from JBoss 7 to WildFly 9

I'm migrating my application from JBoss 7 to WildFly (v9.0.1) and it is not deployed because of bean transaction management error.
Caused by: javax.naming.NamingException: WFLYNAM0062: Failed to lookup env/com.component.eventmgt.EventServiceImpl/transaction [Root exception is java.lang.RuntimeException: WFLYNAM0059: Resource lookup for injection failed: java:jboss/UserTransaction]
at org.jboss.as.naming.ServiceBasedNamingStore.lookup(ServiceBasedNamingStore.java:157)
at org.jboss.as.naming.ServiceBasedNamingStore.lookup(ServiceBasedNamingStore.java:83)
at org.jboss.as.naming.NamingContext.lookup(NamingContext.java:207)
at org.jboss.as.naming.NamingContext.lookup(NamingContext.java:193)
at org.jboss.as.naming.NamingContext.lookup(NamingContext.java:189)
at org.jboss.as.naming.deployment.ContextNames$BindInfo$1$1.getReference(ContextNames.java:316)
... 90 more
Caused by: java.lang.RuntimeException: WFLYNAM0059: Resource lookup for injection failed: java:jboss/UserTransaction
at org.jboss.as.naming.deployment.ContextNames$BindInfo$1$1.getReference(ContextNames.java:319)
at org.jboss.as.naming.ServiceBasedNamingStore.lookup(ServiceBasedNamingStore.java:143)
... 95 more
Caused by: javax.naming.NameNotFoundException: UserTransaction [Root exception is java.lang.IllegalStateException: WFLYEJB0137: Only session and message-driven beans with bean-managed transaction demarcation are allowed to access UserTransaction]
at org.jboss.as.naming.ServiceBasedNamingStore.lookup(ServiceBasedNamingStore.java:153)
at org.jboss.as.naming.ServiceBasedNamingStore.lookup(ServiceBasedNamingStore.java:83)
at org.jboss.as.naming.NamingContext.lookup(NamingContext.java:207)
at org.jboss.as.naming.NamingContext.lookup(NamingContext.java:193)
at org.jboss.as.naming.NamingContext.lookup(NamingContext.java:189)
at org.jboss.as.naming.deployment.ContextNames$BindInfo$1$1.getReference(ContextNames.java:316)
... 96 more
Here is the EventServiceImpl class.
#Stateless
#Remote(EventService.class)
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public class EventServiceImpl implements EventService {
/**
* Logger
*/
private static Logger log = LoggerFactory.getLogger(EventService.class);
private EventTableDAO eventDao;
#PersistenceContext(unitName = "SOMF-GT")
private EntityManager entityManager;
#Resource
private UserTransaction transaction;
public List<Map> loadEvents() throws EventsException {
Configuration configurationEntry = new Configuration();
try {
Map configuration = configurationService.getConfiguration();
if (configuration != null) {
eventDao = new EventTableDAO(Event.class, entityManager, transaction);
List<Map> eventsMapList = new ArrayList();
}
}
I know that if i changed the transaction management to BMT with #TransactionManagement(TransactionManagementType.BEAN) but then the following error emerge
WFLYJPA0060: Transaction is required to perform this operation (either use a transaction or extended persistence context)
I want to know why we have to change this in the first place ?
Any information, please !
These changes rolled out in Wildfly 8 and were (as noted below) based on standardization of the global JNDI namespace in EJB 3.1.
From the Wildfly 8 Developer Guide:
EJB 3.1 introduced a standardized global JNDI namespace and a series of related namespaces that map to the various scopes of a Java EE application. The three JNDI namespaces used for portable JNDI lookups are java:global, java:module, and java:app. If you use JNDI lookups in your application, you will need to change them to follow the new standardized JNDI namespace convention.
To conform to the new portable JNDI namespace rules, you will need to review the JNDI namespace rules and modify the application code to follow these rules.
The guide further notes:
WildFly 8 has tightened up on JNDI namespace names to provide predictable and consistent rules for every name bound in the application server and to prevent future compatibility issues. This means you might run into issues with the current namespaces in your application if they don't follow the new rules.
Here's a snippet from the table showing Examples of JNDI mappings in previous releases and how they might look now specific to the UserTransaction:
Previous Namespace New Namespaces
------------------ --------------
java:comp/UserTransaction java:comp/UserTransaction (This will not be accessible for non EE threads, e.g. Threads your application directly creates)
java:comp/UserTransaction java:jboss/UserTransaction (Globally accessible, use this if java:comp/UserTransaction is not available)
Edit re: WFLYEJB0137:
This is theory-craft and may be worthless - let me know and I'll delete it. Java EE 6 Tutorial - Container-Managed Transactions says:
Enterprise beans that use container-managed transaction demarcation also must not use the javax.transaction.UserTransaction interface.
Further:
(Transaction) Required Attribute
If the client is running within a transaction and invokes the enterprise bean’s method, the method executes within the client’s transaction. If the client is not associated with a transaction, the container starts a new transaction before running the method.
The Required attribute is the implicit transaction attribute for all enterprise bean methods running with container-managed transaction demarcation. You typically do not set the Required attribute unless you need to override another transaction attribute. Because transaction attributes are declarative, you can easily change them later.
The exception message pretty much says it all:
WFLYEJB0137: Only session and message-driven beans with bean-managed transaction demarcation are allowed to access UserTransaction
Your EJB is using container managed transaction (CMT) demarcation which does not inter-operate with bean managed transaction (BMT) demarcation wherein UserTransaction lies.
Regarding a switch to BMT and
WFLYJPA0060: Transaction is required to perform this operation (either use a transaction or extended persistence context)
I found Transaction is required to perform this operation (either use a transaction or extended persistence context) which seems to indicate that the transaction is to be managed by you, as you noted in your comments #Marco. It appears that you have made the appropriate modification.

JCA Connector Classloading

in my Scenario, i try to use JCA Adapters to connect to an external storage - just to try this feature of J2EE.
I use JBoss EAP 7 and its packed implementation ironjacamar.
i deploy an adapter.rar, which contains an adapter.jar (this contains the Connection and ConnectionFactory Interfaces and all implementations) and META-INF/ironjacamar.xml.
I then deploy a app.war file, containing a Bean with an annotated field:
#RequestScoped
public class Bean {
...
#Resource(lookup = "java:/eis/StorageConnectionFactory")
private StorageConnectionFactory connectionFactory;
}
The war also contains the adapter.jar as library - as it needs to know of all the classes at runtime (NoClassDefFound etc.)
To my amazement, the Connector itself seems to work - as is get the Exception:
java.lang.IllegalArgumentException: Can not set conn.StorageConnectionFactoryImpl field Bean.connectionFactory to conn.HsmConnectionFactoryImp
and on ommitting the interfaces even:
#Resource(lookup = "java:/eis/StorageConnectionFactory")
private StorageConnectionFactoryImpl connectionFactory;
still
java.lang.IllegalArgumentException: Can not set conn.StorageConnectionFactoryImpl field Bean.connectionFactory to conn.HsmConnectionFactoryImp
I see that the Problem is, that the adapter.rar does nto share the same classloader as the app.war and both contain the corresponding classes, leading to a sort of ClassCastException - how do i solve this issue correctly?
It seems you haven't configure resource adapter properly.
See the below guide, it will help you to configure:
https://access.redhat.com/documentation/en/red-hat-jboss-enterprise-application-platform/version-7.0/configuration-guide/#configuring_jca_subsystem

Wildfly - Infinispan Transactions configuration

I am using Wildfly 8.2 with its included Infinispan (6.0.2) and I am trying to cache all values from some Oracle database table in an Infispan cache. In most cases, it seems to work, but sometimes it does not. When accessing the cache.values() (which also may not be a good idea for performance, but is an example) it appears sometime to be empty, sometimes it contains the values correctly. Therefore I think it might a problem with the configuration of the transactions. When making the Infinispan cache non-transactional, the problem disappears.
The service which accesses the cache and the DB is an EJB bean with container-managed transactions. On initialzation of the Service, all data is loaded from the DB (it does not contain many entries).
According to what's new in ejb 3.2 it should be possible to access the DB transactionally in a EJB Singleton bean.
Is the configuration of the data source and the Infinispan cache correct? Can I use a non-XA datasource with Infinispan and expect it work consistently? According to the Infinispan doc, NON_XA means that Infinispan is registering as a Synchronization, which should be ok, shouldn't it?
The cache is configured in the standalone-full.xml as follows (when removing <transaction mode="NON_XA" locking="PESSIMISTIC"/> the problem disappears, at the price of having no transactional cache):
<cache-container name="cacheContainer" start="EAGER">
<local-cache name="my_table_cache">
<locking isolation="REPEATABLE_READ"/>
<transaction mode="NON_XA" locking="PESSIMISTIC"/>
</local-cache>
</cache-container>
The Oracle DS is defined as follows
<datasource jndi-name="java:jboss/datasources/myDataSource" pool-name="dataSource" enabled="true">
<connection-url>jdbc:oracle:thin:#127.0.0.1:1523:orcl</connection-url>
<driver>ojdbc7.jar</driver>
<pool>
<max-pool-size>25</max-pool-size>
</pool>
<security>
<user-name>myuser</user-name>
<password>myuser</password>
</security>
<timeout>
<blocking-timeout-millis>5000</blocking-timeout-millis>
</timeout>
</datasource>
My Service (the dao is using simple JDBC operations, not Hibernate or similar)
#Singleton
#ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class MyService {
#Resource(lookup = "java:jboss/infinispan/container/cacheContainer")
protected EmbeddedCacheManager cm;
protected Cache<String, MyEntity> cache;
#PostConstruct
private void init() {
try {
cache = getCache();
} catch (SQLException ex) {
log.fatal("could not initialize caches", ex);
throw new IllegalStateException(ex);
}
}
public Cache<String, MyEntity> getCache() {
Cache<String, MyEntity> cache = cm.getCache(getCacheName(), true);
fillCache(cache);
return cache;
}
protected void fillCache(Cache<String, MyEntity> cache) {
List<MyEntity> entities = myDao.getEntities();
for (MyEntity e : entities) {
cache.put(e.getKey, e);
}
}
public MyEntity getEntity(String key) {
return cache.get(key);
}
public void insert(MyEntity entity) {
myDao.insert(entity);
cache.put(entity.getKey(), entity);
}
public void debug() {
log.debug(cache.values());
}
}
When using NON_XA transactions, failure to commit TX in cache may let the transaction to commit, and you would not get any exception that would tell you that the cache is inconsistent.
As for cache.values(), prior to Infinispan 7.0 it returns only local entries, however that should not matter in your case - with local cache all entries are local. The transactional consistency of this operation should hold. I don't see anything wrong in your configuration.
Generally I would recommend to use Infinispan module in Hibernate ORM rather than trying to do the caching on your own, as you show here.
According to the accepted answer, the configuration is correct.
If anyone has similar problems:
The problem seemed to be that one of the read methods like MyService.getEntity() is in some application-specific contexts being called extremly often (which I was not aware of), so using optimistic locking and READ_COMMITTED instead of REPEATABLE_READ seems to make it working as expected.

NoClassDefFoundError with spring cache and aspectj

I am getting this error in my spring webapp (spring 3.1), and I don't know why.
org.springframework.web.util.NestedServletException: Handler
processing failed; nested exception is java.lang.NoClassDefFoundError:
mypackage/TestCache$AjcClosure1
(the $AjcClosure1 is weird)
If I comment the annotation #Cacheable in the class below the error is gone.
public class TestCache {
#Cacheable(value ="myCache")
public List<String> getDummyList(){
Logger l = Logger.getLogger(this.getClass());
l.error("calling getDummyList");
ArrayList<String> foo = new ArrayList<String>();
foo.add("foo");
foo.add("bar");
return foo;
}
}
My controller class (simplified):
#Controller
#RequestMapping("/mypage")
public class MyController {
#RequestMapping
public String index(ModelMap model, Locale locale) {
TestCache tc = new TestCache();
...
}
}
Application Context (only cache part):
<cache:annotation-driven mode="aspectj"/>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cache-manager-ref="ehcache"/>
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:config-location="classpath:ehcache.xml"/>
I tried proxy and aspectj mode (in proxy mode less error but the cache was doing nothing)
This web application was built initially with roo and use spring mvc and webflow. So there is quite a lot of xml in the applicationContext.xml or webmvc-config.xml (and I am not able to understand what some beans are doing).
I am running the wepapps in eclipse with m2e-wtp and the pom.xml is using the plug-in aspectj-maven-plugin (but no idea what it does)
It looks like the issue is related with aspectj, but I never used aspectJ.
If anyone spring/java/aspectj guru can explain me what is making this error and how I can make my cache working it would be awesome! (I could find only tutorial but no sample project using the cacheable annotation).
It seems that the problem comes from the fact that all the class$AjcClosure[n].class aren't published and the only way to do it is remove, the webapps, clean start and republish the webapp.
One problem (may not the one that causes the NoClassDefFoundError) is that you can use Spring Functions like #Cacheable only for Spring Beans. So if you create an class via new (and this class it not annotated by #Configurable) then this is a normal class and not a Spring bean. Therefore the annotation is ignored in proxy mode. -- May this will also result in this stange error in AspectJ mode, but I don't know.

Categories