users@glassfish.java.net

Re: JackRabbit JCA Classloading issues

From: Gustavo Henrique Orair <gustavo.orair_at_gmail.com>
Date: Fri, 23 Dec 2011 15:51:35 -0200

I've just created the 18082 issue:
http://java.net/jira/browse/GLASSFISH-18082

BR,
---------------------------------------------------------------------------------------------------------------------
                               Gustavo Henrique Orair
               Celular/Cell phone: 55-31-85157887
------------------------------------------------------------------------------------------------------------------


2011/12/23 Gustavo Henrique Orair <gustavo.orair_at_gmail.com>

> Jagadish and Sahoo,
> first of all, thanks for your attention.
>
> Sahoo, I tried to use a different classloader, but it did not the trick.
> After our discussion, I removed all jackrabbit dependencies from the EAR
> client except jackrabbit-jcr-commons.
> When I use JcrUtils.getRepository from my EJB is pass the uri parameter
> with jndi scheme, JcrUtils search for provider and it finds
> JndiRepositoryFactory provider and the explicit JNDI lookup is performed.
> The JCARepositoryManager invokes JcrUtils.getRepository again but it pass
> a Map parameter containing the parameters found in Connection pool
> configuration. Then,
> ServiceRegistry.lookupProviders(RepositoryFactory.class) is called and it
> doesn't find the org.apache.jackrabbit.client.RepositoryFactoryImpl. So,
> the code that uses Thread's context class loader (inside
> org.apache.jackrabbit.client.RepositoryFactoryImpl) is not reached since
> the classloader problem happens before, it happens when the search for
> providers does not find the
> org.apache.jackrabbit.client.RepositoryFactoryImpl. Anyway, I tried to
> insert the jackrabbit-jcr-client inside my EAR and changed the code of
> org.apache.jackrabbit.client.RepositoryFactoryImpl class to use default
> classloader and it didn't work.
>
> I will do as Sahoo suggested, I will create an issue, attach the
> jackrabbit-jca and the step by step instructions to reproduce the problems.
>
> Meanwhile, I will read about --libraries and see if I understand how it
> could be used to solve this issue.
>
> Thanks a lot!
>
>
> ---------------------------------------------------------------------------------------------------------------------
> Gustavo Henrique Orair
>
> ------------------------------------------------------------------------------------------------------------------
>
>
> 2011/12/22 Sahoo <sanjeeb.sahoo_at_oracle.com>
>
>> **
>> Have you tried changing jackrabbit to not use Thread's context class
>> loader? If not, try that first. If you have,
>> I suggest you do the following:
>>
>> File an issue.
>> Attach your test cases.
>> Attach jackrabbit rar (after and before modification).
>> Add instructions to reproduce what you are observing.
>>
>> Sahoo
>>
>>
>> On Friday 23 December 2011 01:31 AM, Gustavo Henrique Orair wrote:
>>
>> Dear all,
>>
>> I tried to write both glassfish-application.xml and
>> glassfish-resources.xml to make jackrabbit-jca work together to derived
>> classloading policy.
>> But, these files didn't change the classloader behaviour.
>>
>> I am senting attached the Sample Maven Project I've used to test these
>> concerns.
>> I've attached again the examples EAR generated. Notice my use case is
>> EAR3 module (ear with only jackrabbit-jcr-commons dependency)... The
>> JndiRepositoryFactory class inside jackrabbit-jcr-commons performed an
>> explicit JNDI lookup. It found the JNDI resource, got the connection pool
>> parameters and tried to create the Repository, but the creation failed
>> because the provider's classes inside jackrabbit-jca were not found (you
>> can see in stacktrace the unique provider found was JndiRepositoryFactory
>> from jackrabbit-jcr-commons module installed inside the ear's lib
>> directory).
>>
>> These are the content of glassfish-application.xml and
>> glassfish-resources.xml files:
>> glassfish-application.xml:
>> <!DOCTYPE glassfish-application PUBLIC "-//GlassFish.org//DTD
>> GlassFish Application Server 3.1 Java EE Application 6.0//EN"
>> "http://glassfish.org/dtds/glassfish-application_6_0-1.dtd">
>> <glassfish-application>
>> <resource-ref>
>> <res-ref-name>jcr/Repository</res-ref-name>
>> <jndi-name>jcr/Repository</jndi-name>
>> </resource-ref>
>> </glassfish-application>
>>
>> glassfish-resources.xml:
>> <?xml version="1.0" encoding="UTF-8"?>
>> <!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application
>> Server 3.1 Resource Definitions//EN" "
>> http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
>> <resources>
>> <connector-connection-pool name="cp_jcr_repository"
>> resource-adapter-name="jackrabbit-jca"
>> connection-definition-name="javax.jcr.Repository">
>> <property name="HomeDir" value="/tmp/jcr_repository"></property>
>> </connector-connection-pool>
>> <connector-resource pool-name="cp_jcr_repository"
>> jndi-name="jcr/Repository"></connector-resource>
>> </resources>
>>
>>
>> Stack trace:
>> Caused by: javax.jcr.RepositoryException: Unable to access a repository
>> with the following settings:
>> org.apache.jackrabbit.repository.home: /tmp/jcr_repository
>> The following RepositoryFactory classes were consulted:
>> org.apache.jackrabbit.commons.JndiRepositoryFactory: declined
>> Perhaps the repository you are trying to access is not available at the
>> moment.
>> at
>> org.apache.jackrabbit.commons.JcrUtils.getRepository(JcrUtils.java:215)
>> at
>> org.apache.jackrabbit.jca.JCARepositoryManager.createRepository(JCARepositoryManager.java:71)
>> at
>> org.apache.jackrabbit.jca.JCAManagedConnectionFactory.getRepository(JCAManagedConnectionFactory.java:195)
>> at
>> org.apache.jackrabbit.jca.JCAManagedConnection.openSession(JCAManagedConnection.java:100)
>> ... 82 more
>>
>>
>> Best regards,
>>
>> ---------------------------------------------------------------------------------------------------------------------
>> Gustavo Henrique Orair
>>
>> ------------------------------------------------------------------------------------------------------------------
>>
>>
>> 2011/12/21 Gustavo Henrique Orair <gustavo.orair_at_gmail.com>
>>
>>> Hi Jagadish and Sahoo,
>>>
>>> I changed the class loading policy to global.
>>> It resulted in many conflicts because the classes and resources inside
>>> the resource-adapter were "preferred" against my own libraries and
>>> implementations. These are some of the conflicts:
>>> 1- Derby dependence conflict
>>> Jackrabbit-jca has a derby library, I had to remove the derby library
>>> dependency from jackrabbit-jca that was conflicting.
>>> 2 - commons-io conflict
>>> My code uses new commons-io version 2 API. The code failed because
>>> JackRabbit JCA is shipped with commons-io version 1.4. I replaced
>>> commons-io 1.4 by the commons-io 2.0.1 inside jackrabbit-jca.
>>>
>>> After solving these conflicts, there were unsolved issues with Logging
>>> because jackrabbit-jca uses slf4j and logback and it broke my Logging also
>>> based on slf4j and logback but I am ignoring this problems for a while.
>>>
>>> But the client was able to get the Jcr Repository correctly and the
>>> Crawlers were executed ok.
>>>
>>> So, using classloader global policy did the trick, but I really would
>>> like to solve the problem and keep the derived classloading policy.
>>>
>>> I would really prefer a workaround such as inserting some xml inside
>>> ear to inform to glassfish to make jackrabbit-jca resource adapter should
>>> be available just to this specific application.
>>>
>>> Best regards,
>>>
>>>
>>> ---------------------------------------------------------------------------------------------------------------------
>>> Gustavo Henrique Orair
>>> Mestre em Ciência da Computação - MSc in Computer Science
>>> Universidade Federal de Minas Gerais
>>> Celular/Cell phone: 55-31-85157887
>>>
>>> ------------------------------------------------------------------------------------------------------------------
>>>
>>>
>>> 2011/12/21 Gustavo Henrique Orair <gustavo.orair_at_gmail.com>
>>>
>>>> Hi Jagadish,
>>>>
>>>> I really can't inject these resources in my ejb, this is motivated
>>>> because in fact I am using a StorageFacade that may use a JcrStorage
>>>> implementation or another storage implementation such as FSStorage. This is
>>>> decided dynamically based on properties files. So, I really want to know
>>>> how to get the JCR repository from the JackRabbit JCA using explicit JNDI
>>>> lookup.
>>>>
>>>> If I really understand the problem, the best workaround from me would
>>>> be insert in EAR's descriptor application.xml (or glassfish-resources.xml
>>>> ???) a "fake" injection just to tell Glassfish that I want to get resources
>>>> using explicit JNDI lookup that references the JackRabbit JCA Resource
>>>> Adapter inside the EAR's modules.
>>>>
>>>> I read
>>>> http://docs.oracle.com/cd/E18930_01/html/821-2418/bealr.html#gjjyy and
>>>> searched for this resource-adapter-mid but I couldn't realize how exactly
>>>> could I do that.
>>>> May someone point me how to do that?
>>>>
>>>>
>>>>
>>>>
>>>> Anyway, If you are really concerned why I need to make the explicit
>>>> JNDI lookups, I will try to explain better the problem.
>>>> My code has a Crawler class used to download documents from internet,
>>>> these crawlers instances may be used both in a J2EE and J2SE environment.
>>>> These crawlers just download the documents and storage them using a
>>>> StorageFacade. There are two implementations of the StorageFacade:
>>>> JCRStorage and FSStorage. The crawlers read a properties file to chose
>>>> which implementation to be used. This feature is important for us because I
>>>> do not need to change and compile code if I use different environments, I
>>>> just need to change the properties file.
>>>>
>>>> If the JCRStorage is used, it get from another properties file the URI
>>>> to be accessed and use the JcrUtils.getRepository method to access the JCR
>>>> Repository. Again, this approach make possible to use the same binary
>>>> without changing the code and compiling in both J2EE environment and J2SE
>>>> environment. If I decided to change from a JackRabbit JCA get using
>>>> explicit JNDI lookup to a Jackrabbit WebDav Server, I just need to change
>>>> one properties file. Notice that this code used in the JCRStorage
>>>> implementation uses just code from jackrabbit-jcr-commons module that is
>>>> not tied to the JackRabbit implementation. It makes my framework very easy
>>>> to change the JCR implementation. I would need just the
>>>> jackrabbit-jcr-commons dependency inside my EAR and this would be the
>>>> unique JackRabbit dependency inside the EAR.
>>>>
>>>> Actually, in production environment, I have a CrawlerManager EJB that
>>>> creates, executes and manage the lifecycle of the Crawlers instances. In
>>>> this environment I configure the StorageFacade to use JCRStorage
>>>> implementation and the uri to be accessed:
>>>> # Inform the Storage facade to get the JCR repository by an explicit
>>>> Jndi lookup by the name jcr/Repository
>>>> uri=jndi:jcr/Repository
>>>> It results in an explicit JNDI lookup for the JCR Repository.
>>>> The Jcr Repository is really accessed inside a class that may be used
>>>> as J2SE and I cannot inject the repository.
>>>> I need just make the explicit JNDI lookup works.
>>>>
>>>>
>>>>
>>>> ---------------------------------------------------------------------------------------------------------------------
>>>> Gustavo Henrique Orair
>>>>
>>>> ------------------------------------------------------------------------------------------------------------------
>>>>
>>>>
>>>> 2011/12/21 Jagadish Prasath Ramu <jagadish.ramu_at_oracle.com>
>>>>
>>>>> Hi Gustavo,
>>>>>
>>>>> If you have a <resource-ref> to a connector-resource of JackRabbit-RAR
>>>>> in the application (either via @Resource or via the descriptor as Sahoo
>>>>> stated), the application will be able to see the RAR classes. (Thread
>>>>> context classloader should be application's classloader in this case
>>>>> and
>>>>> it will have RAR's classloader in the parent's chain).
>>>>>
>>>>> Yes, "derived" is the default class-loading policy. You can try setting
>>>>> it to "global" to see whether it works fine. Later, once you have the
>>>>> <resource-ref> defined the application's descriptor (or @Resource), the
>>>>> RAR will be available to the application.
>>>>>
>>>>> Reference:
>>>>> http://docs.oracle.com/cd/E18930_01/html/821-2418/bealr.html#gjjyy
>>>>>
>>>>>
>>>>> Thanks,
>>>>> -Jagadish
>>>>> On Wed, 2011-12-21 at 11:23 -0200, Gustavo Henrique Orair wrote:
>>>>> > Hi Sahoo, thanks for your prompt response!
>>>>> >
>>>>> > Regarding your comment about my ejb seems to be accessing jackrabbit
>>>>> > jca rar classes directly, my ejb uses just the JcrUtils.getRepository
>>>>> > code. This class is provided by the jackrabbit-jcr-commons module.
>>>>> > Inside this jackrabbit-jcr-commons module there is also a
>>>>> > JndiRepositoryFactory class, this class should be found by the
>>>>> > ServiceRegistry.lookupProviders(RepositoryFactory.class) and make an
>>>>> > explicit JNDI lookup (as I used an URI with jndi: scheme). My ejb
>>>>> just
>>>>> > need the jackrabbit-jcr-commons dependency and in fact, using this
>>>>> > approach, I am not tied to JackRabbit implementation and could use
>>>>> any
>>>>> > other JCR implementation (jackrabbit-jcr-commons module just use JCR
>>>>> > API code). This is exactly the EAR3 use case I have provided in my
>>>>> > first e-mail. Based on your concerns, this use case seems to fail
>>>>> > because the explicit JNDI lookup couldn't get the JCR Repository.
>>>>> >
>>>>> > The problem seems to be really concerned (as you said in your first
>>>>> > e-mail) as how org.apache.jackrabbit.client.RepositoryFactoryImpl
>>>>> > class (inside jackrabbit-api module shipped with JackRabbit-JCA) try
>>>>> > to load the org.apache.jackrabbit.core.RepositoryFactoryImpl class
>>>>> > (inside jackrabbit-core module shipped with JackRabbit-JCA) since it
>>>>> > uses Thread.currentThread().getContextClassLoader() instead of the
>>>>> > default classLoader. Notice that the
>>>>> > org.apache.jackrabbit.client.RepositoryFactoryImpl class is used in
>>>>> > multiple different environments, not just J2EE or Glassfish, etc.
>>>>> >
>>>>> > My conclusion is that Glassfish's default class loading policy
>>>>> > (derived) is incompatible with classloading policy expected by the
>>>>> > jackrabbit-api module.
>>>>> >
>>>>> > Anyway, I will give your suggestion a try and change the code from
>>>>> > org.apache.jackrabbit.client.RepositoryFactoryImpl class inside
>>>>> > jackrabbit-api module to use the default classloader instead of
>>>>> > Thread.currentThread().getContextClassLoader() while loading the
>>>>> > org.apache.jackrabbit.core.RepositoryFactoryImpl class. But Apache
>>>>> > Jackrabbit developers will probably argue this would break all other
>>>>> > uses of this class in other environments.
>>>>> >
>>>>> >
>>>>> ---------------------------------------------------------------------------------------------------------------------
>>>>> > Gustavo Henrique Orair
>>>>> > Mestre em Ciência da Computação - MSc in Computer Science
>>>>> > Universidade Federal de Minas
>>>>> > Gerais
>>>>> > Celular/Cell phone: 55-31-85157887
>>>>> >
>>>>> ------------------------------------------------------------------------------------------------------------------
>>>>> >
>>>>> >
>>>>> > 2011/12/20 Sahoo <sanjeeb.sahoo_at_oracle.com>
>>>>> > No, it is not a bug. Your ejb modules does not have any
>>>>> > resource references that are satisfied by jackrabbit rar.
>>>>> Your
>>>>> > ejb seems to be accessing jackrabbit jca rar classes directly
>>>>> > using jackrabbit API called JCRUtils and GlassFish has no
>>>>> idea
>>>>> > that this dependency comes from jackrabbit rar. Add @Resource
>>>>> > to your ejb to a jackrabbit rar supplied connection factory
>>>>> > for the class loader to detect the dependency.
>>>>> >
>>>>> > Sahoo
>>>>> >
>>>>> > On Tuesday 20 December 2011 11:39 PM, Gustavo Henrique Orair
>>>>> > wrote:
>>>>> > > I checked the j2EE6 specification for classloading
>>>>> > > behaviour.
>>>>> > > In section 8.3.2 - EJB Container Class Loading Requirements
>>>>> > > it states:
>>>>> > >
>>>>> > > "Components in the EJB container must have access to the
>>>>> > > following classes and resources.
>>>>> > > (...)
>>>>> > > * The contents of all jar files included in each resource
>>>>> > > adapter archive (rar file) deployed separately to the
>>>>> > > application server, if that resource adapter is used to
>>>>> > > satisfy any resource references in the module.
>>>>> > > (...)"
>>>>> > >
>>>>> > > So, the behaviour I found in Glassfish seems like not J2EE6
>>>>> > > compliant.
>>>>> > >
>>>>> > > Should I file a bug?
>>>>> >
>>>>> >
>>>>> >
>>>>>
>>>>>
>>>>>
>>>>
>>>
>>
>>
>