Comments (16)
Hi @craftone , I have a theory:
The problem could be the classloader. The reflective trick to cache the current() method uses the whatever classloader that loaded the CDIUtils, this reflective code runs once when the class is loaded, but maybe the getBeanManager() method is dependent on the current thread, so the reflective cached method should be ThreadLocal instead of static.
If my theory is right, the solution is to move the static cached methods to ThreadLocal variables..... but I think we can avoid all this problems if we just drop CDI 1.0 support.
Hey @hazendaz what do you think about dropping CDI 1.0 support?
from cdi.
Ok we have +3 votes (@hazendaz , @mwlynch , @mnesarco )
Next release will be mybatis-cdi-1.1.0 and will support cdi-1.1+
from cdi.
javax.enterprise.inject.spi.CDI is not portable across all CDI implementations prior to 1.1, that is why the reflective access is used. The problem reported here is very strange because if javax.enterprise.inject.spi.CDI is available, it is selected for beanManager lookup. And it is more strange because if it works for some time and stops working later it means the javax.enterprise.inject.spi.CDI.current() implementation has changed at some point.
from cdi.
Yes, It is very strange.
To investigate, I added logging to CDIUtils.CDI#getBeanManager()
.
public final class CDIUtils {
private static class CDI {
// ...
static BeanManager getBeanManager() {
if (current != null) {
try {
// ADD logging : start
Object cdi2 = javax.enterprise.inject.spi.CDI.current();
LOGGER.log(Level.INFO, "javax.enterprise.inject.spi.CDI.current() : {0}", cdi2);
BeanManager bm2 = javax.enterprise.inject.spi.CDI.current().getBeanManager();
LOGGER.log(Level.INFO, "javax.enterprise.inject.spi.CDI.current().getBeanManager() : {0}", bm2);
Object cdi = current.invoke(null);
LOGGER.log(Level.INFO, "current.invoke(null) : {0}", cdi);
BeanManager bm = (BeanManager) getBeanManager.invoke(cdi);
LOGGER.log(Level.INFO, "getBeanManager.invoke(cdi) : {0}", bm);
// ADD logging : end
return javax.enterprise.inject.spi.CDI.current().getBeanManager();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
At first 8 requests, the logs was below.
[2018-10-19T23:17:28.484+0900] [Payara 4.1] [INFO] [] [org.mybatis.cdi.CDIUtils$CDI] [tid: _ThreadID=26 _ThreadName=http-thread-pool::http-listener-1(2)] [timeMillis: 1539958648484] [levelValue: 800] [[
javax.enterprise.inject.spi.CDI.current() : Weld]]
[2018-10-19T23:17:28.494+0900] [Payara 4.1] [INFO] [] [org.mybatis.cdi.CDIUtils$CDI] [tid: _ThreadID=26 _ThreadName=http-thread-pool::http-listener-1(2)] [timeMillis: 1539958648494] [levelValue: 800] [[
javax.enterprise.inject.spi.CDI.current().getBeanManager() : Weld BeanManager for miniSpCore.war/WEB-INF/lib/cdi [bean count=34]]]
[2018-10-19T23:17:28.496+0900] [Payara 4.1] [INFO] [] [org.mybatis.cdi.CDIUtils$CDI] [tid: _ThreadID=26 _ThreadName=http-thread-pool::http-listener-1(2)] [timeMillis: 1539958648496] [levelValue: 800] [[
current.invoke(null) : Weld]]
[2018-10-19T23:17:28.500+0900] [Payara 4.1] [INFO] [] [org.mybatis.cdi.CDIUtils$CDI] [tid: _ThreadID=26 _ThreadName=http-thread-pool::http-listener-1(2)] [timeMillis: 1539958648500] [levelValue: 800] [[
getBeanManager.invoke(cdi) : Weld BeanManager for root_miniSpCore.war [bean count=28]]]
[2018-10-19T23:17:28.501+0900] [Payara 4.1] [INFO] [] [org.mybatis.cdi.CDIUtils$CDI] [tid: _ThreadID=26 _ThreadName=http-thread-pool::http-listener-1(2)] [timeMillis: 1539958648501] [levelValue: 800] [[
javax.enterprise.inject.spi.CDI.current() : Weld]]
[2018-10-19T23:17:28.502+0900] [Payara 4.1] [INFO] [] [org.mybatis.cdi.CDIUtils$CDI] [tid: _ThreadID=26 _ThreadName=http-thread-pool::http-listener-1(2)] [timeMillis: 1539958648502] [levelValue: 800] [[
javax.enterprise.inject.spi.CDI.current().getBeanManager() : Weld BeanManager for miniSpCore.war/WEB-INF/lib/cdi [bean count=34]]]
[2018-10-19T23:17:28.503+0900] [Payara 4.1] [INFO] [] [org.mybatis.cdi.CDIUtils$CDI] [tid: _ThreadID=26 _ThreadName=http-thread-pool::http-listener-1(2)] [timeMillis: 1539958648503] [levelValue: 800] [[
current.invoke(null) : Weld]]
[2018-10-19T23:17:28.504+0900] [Payara 4.1] [INFO] [] [org.mybatis.cdi.CDIUtils$CDI] [tid: _ThreadID=26 _ThreadName=http-thread-pool::http-listener-1(2)] [timeMillis: 1539958648504] [levelValue: 800] [[
getBeanManager.invoke(cdi) : Weld BeanManager for root_miniSpCore.war [bean count=28]]]
[2018-10-19T23:17:28.513+0900] [Payara 4.1] [INFO] [] [] [tid: _ThreadID=26 _ThreadName=http-thread-pool::http-listener-1(2)] [timeMillis: 1539958648513] [levelValue: 800] [[
23:17:28.513 [http-thread-pool::http-listener-1(2)] DEBUG o.a.i.t.jdbc.JdbcTransaction - Opening JDBC Connection
]]
As shown above logs, reflective and non-reflective accesses to getBeanManager()
don't return same bean manager.
And at 9th request, the log was below.
[2018-10-19T23:37:25.532+0900] [Payara 4.1] [INFO] [] [org.mybatis.cdi.CDIUtils$CDI] [tid: _ThreadID=29 _ThreadName=http-thread-pool::http-listener-1(5)] [timeMillis: 1539959845532] [levelValue: 800] [[
javax.enterprise.inject.spi.CDI.current() : Weld]]
[2018-10-19T23:37:25.534+0900] [Payara 4.1] [INFO] [] [org.mybatis.cdi.CDIUtils$CDI] [tid: _ThreadID=29 _ThreadName=http-thread-pool::http-listener-1(5)] [timeMillis: 1539959845534] [levelValue: 800] [[
javax.enterprise.inject.spi.CDI.current().getBeanManager() : Weld BeanManager for miniSpCore.war/WEB-INF/lib/cdi [bean count=34]]]
[2018-10-19T23:37:25.535+0900] [Payara 4.1] [INFO] [] [org.mybatis.cdi.CDIUtils$CDI] [tid: _ThreadID=29 _ThreadName=http-thread-pool::http-listener-1(5)] [timeMillis: 1539959845535] [levelValue: 800] [[
current.invoke(null) : Weld]]
[2018-10-19T23:37:25.561+0900] [Payara 4.1] [WARN] [] [org.glassfish.jersey.internal.Errors] [tid: _ThreadID=29 _ThreadName=http-thread-pool::http-listener-1(5)] [timeMillis: 1539959845561] [levelValue: 900] [[
The following warnings have been detected: WARNING: Unknown HK2 failure detected:
MultiException stack 1 of 1
java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at org.mybatis.cdi.CDIUtils$CDI.getBeanManager(CDIUtils.java:101)
at org.mybatis.cdi.CDIUtils.getBeanManager(CDIUtils.java:123)
at org.mybatis.cdi.CDIUtils.findSqlSessionFactory(CDIUtils.java:149)
at org.mybatis.cdi.MyBatisBean.findSqlSessionManager(MyBatisBean.java:137)
at org.mybatis.cdi.MyBatisBean.create(MyBatisBean.java:124)
at org.jboss.weld.context.unbound.DependentContextImpl.get(DependentContextImpl.java:70)
at org.jboss.weld.bean.ContextualInstanceStrategy$DefaultContextualInstanceStrategy.get(ContextualInstanceStrategy.java:100)
at org.jboss.weld.bean.ContextualInstance.get(ContextualInstance.java:50)
at org.jboss.weld.manager.BeanManagerImpl.getReference(BeanManagerImpl.java:786)
at org.jboss.weld.manager.BeanManagerImpl.getInjectableReference(BeanManagerImpl.java:886)
The exception occurred at getBeanManager.invoke(cdi)
.
from cdi.
from cdi.
I think JNDI look up can be used all the time since cdi 1.0.
How about using JNDI lookup only?
from cdi.
JNDI Lookup is also not portable, at least in the immature period around CDI 1.0 and 1.1.
from cdi.
Umm... How about this code?
Reflective access is used for only checking if CDI utitliy is available.
This is working in my environment.
private static class CDI {
private static final String DEFAULT_JNDI_NAME = "java:comp/BeanManager";
private static final String NAME;
private static final boolean canCDIGetBeanManager;
static {
// Portable 1.1+ CDI Lookup ----------------------------------------------
boolean canCDIGetBeanManagerTemp;
try {
Class<?> c = Class.forName("javax.enterprise.inject.spi.CDI");
c.getMethod("current");
c.getMethod("getBeanManager");
canCDIGetBeanManagerTemp = true;
} catch (Exception ex) {
canCDIGetBeanManagerTemp = false;
}
canCDIGetBeanManager = canCDIGetBeanManagerTemp;
// JNDI Based Lookup fallback --------------------------------------------
if (!canCDIGetBeanManager) {
// ...
}
}
static BeanManager getBeanManager() {
if (canCDIGetBeanManager) {
try {
return javax.enterprise.inject.spi.CDI.current().getBeanManager();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
try {
return InitialContext.doLookup(NAME);
} catch (NamingException e) {
throw new RuntimeException(e);
}
}
}
from cdi.
@craftone I think it will not work in cdi 1.0 environment just because the class javax.enterprise.inject.spi.CDI is not in the classpath. So the system will fail immediately trying to load CDIUtils. It works in your environment because it is not CDI 1.0.
But i can be wrong. Maybe the JVM just defer the load of javax.enterprise.inject.spi.CDI until the call.
from cdi.
@craftone according to this: https://www.programcreek.com/2013/01/when-and-how-a-java-class-is-loaded-and-initialized/
your idea could work even with cdi 1.0 and the code can be simplified a lot. The risk is that as mentioned in the article, different jvms can load classes in different ways. So no guarantees.
I am still very inclined to drop CDI 1.0 support and move on.
from cdi.
@mnesarco I think if the class javax.enterprise.inject.spi.CDI is not in the classpath, java.lang.ClassNotFoundException will occur at Class.forName("javax.enterprise.inject.spi.CDI")
. And the exception will be caught and canCDIGetBeanManager
will be false. After that the system will use JNDI lookup only.
So the code will work in CDI 1.0 environment, I think.
from cdi.
@craftone you are right about the exception catch at Class.forName call. But my concern is not there but in the static reference to the class here:
try {
return javax.enterprise.inject.spi.CDI.current().getBeanManager();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
If the JVM loads the javax.enterprise.inject.spi.CDI class before the call to Class.forName, the ClassNotFoundException will be propagated hopelessly. The Class Loading process does not follow the code execution path. It has its own rules. In case of reflection there is no risk because the class loading is controlled by the programmer, but with static references like the one in your code the JVM decides when to load the class by their own.
from cdi.
My proposal is to just drop cdi 1.0 support, remove all the tricky code and just call javax.enterprise.inject.spi.CDI.current().getBeanManager(). Then release 1.0.4.
If there are someone using cdi 1.0 + mybatis + mybatis-cdi, they should use 1.0.3 or create a fork.
from cdi.
@mnesarco Oh yes, finally I understood. Thank you for your exlanation.
from cdi.
@iwangxiaodong, @hazendaz, @craftone, @mwlynch
Please vote +1/-1 to merge the PR and move on, or if you can, provide a new PR to fix this issue and keep cdi1.0 support.
See also: #74
My vote is +1 to remove cdi-1.0 support and release mybatis-cdi 1.0.4
from cdi.
+1,
If I'm in a position where I need CDI 1.0 support again (heaven forbid...) I'll send a pull request or fork:-)
from cdi.
Related Issues (20)
- UnsatisfiedResolutionException: Api type [] is not found with the qualifiers... HOT 7
- Consider support JNDI obtain UserTransaction?
- Weld 3.1.7.Final Proxy changes has broken mybatis-cdi support HOT 1
- Create annotation to rollback exception by default HOT 1
- Upgrade to jakarta Namespace HOT 17
- UnsatisfiedResolutionException using aries-cdi 1.1.5 with karaf 4.3.7 HOT 1
- Dependency Dashboard
- Migrate from atomikos to narayana HOT 1
- Site plugin and 'clirr' usage issue with jakarta
- Clirr: Remove profile from maven.config file after no longer needing to compare to javax namespace
- missing @Any qualifier on MybatisBean if user provided qualifier is present
- How to modify java:comp/BeanManager to java:comp/env/BeanManager? HOT 13
- issue with micro profile standard HOT 1
- Release 1.0.3 HOT 4
- NoSuchElementException in test with Arquillian and embedded container HOT 10
- Test issues - 8 of 13 can run concurrently / recent updates do not support oldest support revision of cdi/weld HOT 5
- github is behaving very strange... HOT 1
- mybatis-cdi-1.0.3 crashes in repeated requests HOT 2
- Release mybatis-cdi 1.1.0 HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from cdi.