Giter VIP home page Giter VIP logo

Comments (16)

mnesarco avatar mnesarco commented on June 27, 2024 1

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.

mnesarco avatar mnesarco commented on June 27, 2024 1

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.

mnesarco avatar mnesarco commented on June 27, 2024

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.

craftone avatar craftone commented on June 27, 2024

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.

hazendaz avatar hazendaz commented on June 27, 2024

from cdi.

craftone avatar craftone commented on June 27, 2024

I think JNDI look up can be used all the time since cdi 1.0.
How about using JNDI lookup only?

from cdi.

mnesarco avatar mnesarco commented on June 27, 2024

JNDI Lookup is also not portable, at least in the immature period around CDI 1.0 and 1.1.

from cdi.

craftone avatar craftone commented on June 27, 2024

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.

mnesarco avatar mnesarco commented on June 27, 2024

@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.

mnesarco avatar mnesarco commented on June 27, 2024

@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.

craftone avatar craftone commented on June 27, 2024

@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.

mnesarco avatar mnesarco commented on June 27, 2024

@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.

mnesarco avatar mnesarco commented on June 27, 2024

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.

craftone avatar craftone commented on June 27, 2024

@mnesarco Oh yes, finally I understood. Thank you for your exlanation.

from cdi.

mnesarco avatar mnesarco commented on June 27, 2024

@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.

mwlynch avatar mwlynch commented on June 27, 2024

+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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.