-
1. Re: ClassLoader Weirdness!
jwcarman Aug 30, 2002 2:19 PM (in response to jwcarman)Doesn't anyone ever reply to these things?
-
2. Re: ClassLoader Weirdness!
adrian.brock Aug 30, 2002 4:25 PM (in response to jwcarman)No, java:comp/env is per bean not per package.
No, nobody ever replies :-)
Regards,
Adrian -
3. Re: ClassLoader Weirdness!
jwcarman Aug 31, 2002 3:45 PM (in response to jwcarman)But, if I put a jar file that contains only classes (like log4j.jar or something like that). Then, it is handled by the UCL, correct? So, classes in that jar file will have access to a different java:comp context than classes in my EJB jar file. Well, whatever the problem, it works differently when I use another class, loaded from another jar file that's sitting in the deploy directory. So, if I try to do this...
EnvironmentNamingContext.getInstance().getEjbLocalHome( "ejb/MyBean" );
I get a name not bound exception. But, if I do this...
new Context enc = ( Context )new InitialContext().lookup( "java:comp/env" );
enc.lookup( "ejb/MyBean" );
It all works fine. How do I make it so that I can use my EnvironmentNamingContext class? Basically, the class just hides all of the ugliness of instantiating a context and performing the lookup. So, I would like to be able to use it (since it is a J2EE design pattern).
Thanks for the reply, by the way! :-) -
4. Re: ClassLoader Weirdness!
jwcarman Aug 31, 2002 3:45 PM (in response to jwcarman)forget about that little "new" word in front of Context. Typo.
-
5. Re: ClassLoader Weirdness!
adrian.brock Aug 31, 2002 8:16 PM (in response to jwcarman)I'm not sure what you are doing?
The pattern you mention is for client side, so
java:comp is invisible and you cannot use local
interfaces.
java:comp/env is per bean. It is classloader based,
but not the UnifiedClassLoader (UCL), it does have
the UCL as a parent.
The structure looks something like
UCL (At jar level)
+- java:comp classloader (bean1)
+- java:comp classloader (bean2)
+- java.comp classloader (bean3)
The java:comp classloader also called the
ENC (environment classloader)
is the thread context classloader during invocation of
the bean.
So bean1 cannot see java:comp/env/whatever
for bean2.
Regards,
Adrian -
6. Re: ClassLoader Weirdness!
jwcarman Sep 1, 2002 9:56 AM (in response to jwcarman)Okay, let's try to figure this out. I don't believe that they strictly mean client Java executable application when they refer to a "J2EE client." Any type of Java application can be a J2EE client (Applet, Webapp, Stand-alone application, another J2EE application). A J2EE client is any type of client that needs to use the J2EE application's services. Anyway, I have a class called EnvironmentNamingContext. It is in components.jar located in the deploy directory. It looks something like this...
public EnvironmentNamingContext
{
private static final EnvironmentNamingContext instance = new EnvironmentNamingContext();
private Context enc;
public static final getInstance()
{
return getInstance();
}
private Context getEnc() throws NamingException
{
if( enc == null )
{
enc = ( Context )new InitialContext().lookup( "java:comp/env" );
}
return enc;
}
public Object lookup( String name ) throws NamingException
{
return getEnc().lookup( name );
}
public EJBHome getEjbHome( String name, Class homeClass ) throws NamingException
{
return ( EJBHome )PortableRemoteObject.narrow( lookup( name ), homeClass );
}
public EJBLocalHome getEjbLocalHome( String name ) throws NamingException
{
return ( EJBLocalHome )lookup( name );
}
// More methods here for Queues, Topics, DataSources, environment entries, etc.
}
Let's not even worry about EJBs at this point. Let's just consider a simple environment entry. In a war file that's also located in the deploy directory, I have the following code (as a Cactus ServletTestCase)...
public void testEnvironmentNamingContext() throws Exception
{
getLogger().debug( EnvironmentNamingContext.getInstance().getString( "MyString" );
}
public void testDirectLookup() throws Exception
{
final Context enc = ( Context )new InitialContext().lookup( "java:comp/env" );
getLogger().debug( ( String )enc.lookup( "MyString" ) );
}
The first one will fail with a naming exception, but the second one will succeed. Now, when I rearranged things by placing components.jar inside an ear file with the war (and placing components.jar on the manifest classpath of the war) and putting a <loader-repository> tag inside my jboss-app.xml file inside the ear, all works fine. Both test cases pass. But, when I remove the <loader-repository> tag, it fails again. So, in the end, I got it working. I just wanted to know if this is the desired behavior of JBoss or if it's something weird going on? -
7. Re: ClassLoader Weirdness!
adrian.brock Sep 1, 2002 11:26 AM (in response to jwcarman)You are very persistent :-)
Here is what is happening:
Step 1
app1 does getInstance();
This is the first use of your factory, so this constructs
the static instance object.
You then do getEnc() which does lookup("java:comp/env")
for app1 and this goes into the enc variable.
Step2
app2 does getInstance(), it already exists
it does getEnc() and gets app1's naming context
stored in the enc variable.
It is the wrong one!
By adding the <loader-repository> you create a
new classloader space. With commons.jar in that
classloader space you have a new version of your class,
step 2 now becomes.
app2 does getInstance(), it doesn't already exist
it is a different version of the class with a whole
new copy of the static variables.
it does getEnc(), enc isn't filled in yet so it
looks up app2's naming context
now it works.
Except if there was also an app3 in the same ear, it
would use app2's naming context.
Try adding the following lines to getEnc();
System.out.println("Got: " + enc);
System.out.println("Expected: " + new InitialContext().lookup("java:comp/env"));
You will see the difference between what is cached and
what should be retrieved.
Regards,
Adrian -
8. Re: ClassLoader Weirdness!
jwcarman Sep 1, 2002 1:15 PM (in response to jwcarman)Well, what I was hoping would happen is that everyone can use the same instance of the javax.naming.Context variable (called enc in my case). What I've seen in other application servers is that the implementation of the JNDI provider that's used to connect to the naming service has knowledge of the context (not naming, but "execution context" if you will). In Sun's RI, it works like that. For example...
<ejb-jar>
<enterprise-beans>
<ejb-name>A</ejb-name>
AHome
A
<ejb-class>ABean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
<ejb-ref>
<ejb-ref-name>ejb/Other</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
BHome</local-home>
B
<ejb-link>B</ejb-link>
</ejb-ref>
<ejb-name>B</ejb-name>
BHome
B
<ejb-class>BBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
<ejb-ref>
<ejb-ref-name>ejb/Other</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
AHome</local-home>
A
<ejb-link>A</ejb-link>
</ejb-ref>
</enterprise-beans>
</ejb-jar>
Both beans in this case use the EnvironmentNamingContext class to lookup the references to each other using the name "ejb/Other". In Sun's RI, this works fine, and there is only one instance of javax.naming.Context shared among the two beans. So, somehow the JNDI provider's implementation is concious of which beans "execution context" is actually executing at the time the lookup is performed. In JBoss, from what you've said, if the A session bean does a lookup using the shared instance first (thus causing the creation of the instance) it will receive the BHome object. Then, when the B session bean does a lookup next, it will receive the BHome object also, since the javax.naming.Context object was created using the B session bean's java:comp naming context. I don't even think that changing the EnvironmentNamingContext class to instantiate a new InitialContext for each lookup will help.
What I think is happening in my case is that the naming context that is created when components.jar is just sitting by itself in the deploy directory is created using some sort of default view of the naming system, with no customizations at all, since regular jars (non ejb-jars, wars, or ears) don't really have the concept of an environment naming context. The static enc instance is obviously not being created using the WAR's environment naming context (as the WAR is the only application using EnvironmentNamingContext), since it cannot find the environment entries that I declared in the web.xml file. Correct me if I'm wrong, but I'm going to do some exploration now to confirm. -
9. Re: ClassLoader Weirdness!
adrian.brock Sep 1, 2002 2:25 PM (in response to jwcarman)Print out the result of
Thread.currentThread().getContextClassLoader()
in getEnc().
This is what determines the naming context.
Are you saying that for the RI
Context ctx = new InitialContext().lookup("java:comp/env");
String s = ctx.lookup("MyString");
The second lookup, effectively repeats the first one.
Determining the execution context on every lookup
regardless of the context you start from?
Regards,
Adrian -
10. Re: ClassLoader Weirdness!
jwcarman Sep 1, 2002 3:27 PM (in response to jwcarman)What I'm saying is that in the RI, the same exact Context object will return different objects depending upon who (which J2EE client, whether that be an EBJ or a Java application) called it. So, the statically shared enc variable is created one time. However, when its lookup method is called somehow knows that it is being called from Session B, so it will return a reference to AHome. Check out the attached ear file (the source is in the ejb jar file).
-
11. Re: ClassLoader Weirdness!
jwcarman Sep 1, 2002 3:28 PM (in response to jwcarman)Oh, you should have no problem just deploying this ear file in the Sun J2EE RI. Then, execute the application client to illustrate the EnvironmentNamingContext class working the way I had intended it.
-
12. Re: ClassLoader Weirdness!
adrian.brock Sep 1, 2002 4:32 PM (in response to jwcarman)Hi,
You seem to have found a difference in behaviour
between jboss and the RI.
The spec doesn't specify either mechanism.
Tomcat works the same way as JBoss and Tomcat IS the
reference implementation for a servlet container.
I would guess the J2ee RI is doing some thread local
processing on every lookup (inefficient).
Sorry, I'm not going to download, install, configure
the RI just to test this.
JBoss (and Tomcat) just does the thread local stuff when
it looks up "java:comp/env".
After that you've just got a normal naming context.
Storing the naming context for use outside the bean
that retrieved it is not defined by the spec.
If you don't like this behaviour, you can modify
org.jboss.naming. Post a patch that allows your
alternative behaviour to be configured in
jboss.xml/standardjboss.xml
Regards,
Adrian -
13. Re: ClassLoader Weirdness!
jwcarman Sep 3, 2002 12:34 PM (in response to jwcarman)Maybe I should just change my implementation of EnvironmentNamingContext to not be a singleton, as an environment naming context is not global in intent. It is specific to a particular component within a J2EE application. Maybe every component should create their own EnvironmentNamingContext object to support their own lookups. Maybe that would be sufficient, hopefully. Thank you again for your help, Adrian. I guess it's back to the drawing board for me! :-)