users@glassfish.java.net

Re: JackRabbit JCA Classloading issues

From: Gustavo Henrique Orair <gustavo.orair_at_gmail.com>
Date: Thu, 22 Dec 2011 18:01:39 -0200

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#gjjyyand 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?
>>> >
>>> >
>>> >
>>>
>>>
>>>
>>
>