You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@struts.apache.org by Ken Miller <kl...@shetlandsoftware.com> on 2007/04/17 04:10:32 UTC

Struts 1.3.8: "Sidebar: Sharing JAR Files Across Web Applications"

I have a question regarding the above section in the user guide for 
Struts 1.3.8, which can be found here:

    http://struts.apache.org/1.3.8/userGuide/configuration.html#config_add

In particular, I'm interested in the following paragraph:

-----------------
When a class is loaded from a shared class loader, static variables used 
within that class become global as well. This can cause inter-webapp 
conflicts when the underlying code assumes that the statics are global 
only within a particular web applicaiton (which would be true if the 
class was loaded from the webapp class loader). There are many cases 
where the framework, and the Commons libraries it relies on, use static 
variables to maintain information that is presumed to be visible only 
within a single web application. Sharing these JAR files can cause 
unwanted interactions, and probably cause incorrect behavior.
-----------------

Can someone point me (or at least give me a clue) where a potential 
problem may exist?  I've configured my server to have a shared set of 
Commons and Struts jars (something I did before reading the above 
paragraph), and it appears to work, but I'd like to investigate a few 
situations which may cause problems to better understand the possible 
pitfalls.

One possible problem that I've run into is that the tag library 
descriptors can't be 'found' when the Struts jar files are controlled by 
another class loader.  In the environment I'm using (SAP Netweaver - 
please, no laughter :-) ) the Struts jar files are loaded by one class 
loader, and the application is loaded by another.  There's a reference 
from the application class loader to the Struts class loader, so all the 
jar files can be seen; I can even use the class loader in the 
application to load META-INF/tld/*.tld from Struts, so I can see the tag 
lib descriptors; however, Struts seems unable to find the files, so I've 
had to copy the descriptors into WEB-INF/tld - it works, but I don't 
particularly like it.  If I'm going to share the jar files, I may as 
well share the taglibs too...

Thanks for your help!


---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org


Re: Struts 1.3.8: "Sidebar: Sharing JAR Files Across Web Applications"

Posted by Ken Miller <Ke...@huskyenergy.ca>.
>>> On 4/20/2007 at 9:16 AM, in message
<f8...@mail.gmail.com>, "Craig
McClanahan" <cr...@apache.org> wrote:
> On 4/20/07, Ken Miller <Ke...@huskyenergy.ca> wrote:
>>

...

>> When I first started getting struts working in my environment, I deployed 
> the application with the struts jar files in the lib directory, and I was 
> able to use the taglibs without making any changes to web.xml to create a 
> uri-to-location mapping.  (I take it the struts code looks for these tlds 
> automatically upon startup, yes?)
>>
> 
> Actually, it is the JSP page compiler (part of the servlet container)
> that looks for these things, but yes, a modern container will scan JAR
> files for "*.tld" files in the META-INF directory.

Ah.  So perhaps the container isn't  looking in META-INF directory; only in WEB-INF.  I'll have to do a bit more digging on that one.

>> However, when I moved the struts jars into their own 'app', I was no longer 
> able to do this.  Placing a mapping in web.xml didn't help, and the only 
> thing I could do was place the tlds in the WEB-INF directory in the web app.  
> The strange this was that I was able to load the tlds via the class loader, 
> although I didn't try it through the context class loader.
>>
> 
> What does "moved the struts jars into their own 'app'" mean?  If you
> moved them to a different web application, they won't be accessible at
> all.  If you put them someplace like Tomcat's
> $CATALINA_HOME/shared/lib directory, the JAR files should be
> accessible ... but I have no idea whether Tomcat scans those JARs as
> well as the ones in WEB-INF/lib.

Yeah, I didn't explain that very well, did I?  In the environment I'm working in, you can create classloader references from one application to another. Class loading is therefore enhanced in the following way: if a class cannot be loaded by the parent class loader chain, nor by the source class, references are then checked for the class.  Note that a reference is only followed one level, so if the class (or resource) can't be found in the referenced application, an exception is thrown.

In the case I described above, I placed the struts libraries into their own application (technically called a library), and deployed that to the server.  I then set a reference from my application (which contains the action code) to the struts library container.  The application will run correctly as long as the tld's are placed in the WEB-INF/tld directory of the source application; for some reason, the servlet container cannot location the tlds when they reside in the struts library.  However, I just ran a quick test, and neither the class loader nor the context class loader (which happen to be the same object, btw) can load the objects from the jar files.

So, I guess I have to store the tld's in the source application.   What's strange is that even though I create a mapping from URI -> META-INF/tld/XX.tld, the container still can't load the files....

Thanks again for your help Craig.

-- 

Cheers!

      -klm.

-----------------------------------------------------------------------------
Kenneth (Ken) L. Miller
Technical Specialist, Enterprise Web Portals
    Cell: 403.680.3785
Office: 403.750.1790



---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org


Re: Struts 1.3.8: "Sidebar: Sharing JAR Files Across Web Applications"

Posted by Craig McClanahan <cr...@apache.org>.
On 4/20/07, Ken Miller <Ke...@huskyenergy.ca> wrote:
>
> >>> "Craig McClanahan" <cr...@apache.org> 4/17/2007 4:33 PM >>>
> > > Craig McClanahan wrote:
> > > Yep ... it's magic :-).
>
> Ah, magic.  Lots of hand waving, followed gasps and applause :-)
>
> Guys, thanks for the great info - this has all been really helpful.  However, I do have one question regarding the loading of the tag descriptors.
>
> When I first started getting struts working in my environment, I deployed the application with the struts jar files in the lib directory, and I was able to use the taglibs without making any changes to web.xml to create a uri-to-location mapping.  (I take it the struts code looks for these tlds automatically upon startup, yes?)
>

Actually, it is the JSP page compiler (part of the servlet container)
that looks for these things, but yes, a modern container will scan JAR
files for "*.tld" files in the META-INF directory.

> However, when I moved the struts jars into their own 'app', I was no longer able to do this.  Placing a mapping in web.xml didn't help, and the only thing I could do was place the tlds in the WEB-INF directory in the web app.  The strange this was that I was able to load the tlds via the class loader, although I didn't try it through the context class loader.
>

What does "moved the struts jars into their own 'app'" mean?  If you
moved them to a different web application, they won't be accessible at
all.  If you put them someplace like Tomcat's
$CATALINA_HOME/shared/lib directory, the JAR files should be
accessible ... but I have no idea whether Tomcat scans those JARs as
well as the ones in WEB-INF/lib.

> Is this an example of the issues I may have to deal with by trying to share the struts libraries?
>
>
> Cheers!
>
>       -klm.

Craig

---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org


Re: Struts 1.3.8: "Sidebar: Sharing JAR Files Across Web Applications"

Posted by Ken Miller <Ke...@huskyenergy.ca>.
>>> "Craig McClanahan" <cr...@apache.org> 4/17/2007 4:33 PM >>>
> > Craig McClanahan wrote:
> > Yep ... it's magic :-).
 
Ah, magic.  Lots of hand waving, followed gasps and applause :-)
 
Guys, thanks for the great info - this has all been really helpful.  However, I do have one question regarding the loading of the tag descriptors.
 
When I first started getting struts working in my environment, I deployed the application with the struts jar files in the lib directory, and I was able to use the taglibs without making any changes to web.xml to create a uri-to-location mapping.  (I take it the struts code looks for these tlds automatically upon startup, yes?)
 
However, when I moved the struts jars into their own 'app', I was no longer able to do this.  Placing a mapping in web.xml didn't help, and the only thing I could do was place the tlds in the WEB-INF directory in the web app.  The strange this was that I was able to load the tlds via the class loader, although I didn't try it through the context class loader.
 
Is this an example of the issues I may have to deal with by trying to share the struts libraries?
 
 
Cheers!
 
      -klm.
 
-----------------------------------------------------------------------------
Kenneth (Ken) L. Miller
Technical Specialist, Enterprise Web Portals
    Cell: 403.680.3785
Office: 403.750.1790

Re: Struts 1.3.8: "Sidebar: Sharing JAR Files Across Web Applications"

Posted by Craig McClanahan <cr...@apache.org>.
On 4/17/07, Christopher Schultz <ch...@christopherschultz.net> wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Craig,
>
> Craig McClanahan wrote:
> > Craig McClanahan wrote:
> > Yep ... it's magic :-).
> >
> > Actually, what happens is that the servlet container provides a
> > mechanism to acquire the ClassLoader instance for the web application
> > itself, from which you can load application classes no matter where
> > the caller is located.  For instance, when Struts loads an action
> > class, it does something like this (plus some exception catching, of
> > course):
> >
> >    String actionClassName = ...;
> >    ClassLoader cl = Thread.currentThread().getContextClassLoader();
> >    Class actionClass = cl.loadClass(actionClassName);
>
> I thought that the server created and started those threads
> independently of the webapp being used.

Yes, it does ... although there is no restriction that a particular
thread be used *only* for a particular application.  Generally, the
server will pool the threads across all active apps.

> I've never used
> Thread.getContextClassLoader... is that something that the server sets
> when it handles a request -- for purposes such as this?

Yes, that's exactly what it is for.

Craig

>
> > The container guarantees that the context class loader for a thread is
> > set correctly, before it enters your servlet, filter, or listener.
>
> I guess so... ;)
>
> > There is indeed no way to search down the hierarchy, which is why the
> > context class loader convention was created.  As you examine libraries
> > for potentially putting them into a shared class loader, look for code
> > that does this kind of thing.
>
> Cool. Thanks for the explanation, Craig.
>
> - -chris
>
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.7 (MingW32)
> Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
>
> iD8DBQFGJS4d9CaO5/Lv0PARAqzKAKC4t0HR31h8KaLX471socJ7rbNphQCfWpAM
> IrvvDVTjDYBpzob6TtwoGl4=
> =Qq2L
> -----END PGP SIGNATURE-----
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
> For additional commands, e-mail: user-help@struts.apache.org
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org


Re: Struts 1.3.8: "Sidebar: Sharing JAR Files Across Web Applications"

Posted by Christopher Schultz <ch...@christopherschultz.net>.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Craig,

Craig McClanahan wrote:
> Craig McClanahan wrote:
> Yep ... it's magic :-).
> 
> Actually, what happens is that the servlet container provides a
> mechanism to acquire the ClassLoader instance for the web application
> itself, from which you can load application classes no matter where
> the caller is located.  For instance, when Struts loads an action
> class, it does something like this (plus some exception catching, of
> course):
> 
>    String actionClassName = ...;
>    ClassLoader cl = Thread.currentThread().getContextClassLoader();
>    Class actionClass = cl.loadClass(actionClassName);

I thought that the server created and started those threads
independently of the webapp being used. I've never used
Thread.getContextClassLoader... is that something that the server sets
when it handles a request -- for purposes such as this?

> The container guarantees that the context class loader for a thread is
> set correctly, before it enters your servlet, filter, or listener.

I guess so... ;)

> There is indeed no way to search down the hierarchy, which is why the
> context class loader convention was created.  As you examine libraries
> for potentially putting them into a shared class loader, look for code
> that does this kind of thing.

Cool. Thanks for the explanation, Craig.

- -chris

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (MingW32)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFGJS4d9CaO5/Lv0PARAqzKAKC4t0HR31h8KaLX471socJ7rbNphQCfWpAM
IrvvDVTjDYBpzob6TtwoGl4=
=Qq2L
-----END PGP SIGNATURE-----

---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org


Re: Struts 1.3.8: "Sidebar: Sharing JAR Files Across Web Applications"

Posted by Craig McClanahan <cr...@apache.org>.
On 4/17/07, Christopher Schultz <ch...@christopherschultz.net> wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Craig,
>
> Craig McClanahan wrote:
> > The naive way for a library to do this is:
> >
> >    String className = ...; // Calculate the name of the class you want
> >    Class clazz = Class.forName(className);
> >
> > * If commons-foo.jar is stored in a shared classloader provided by your
> >  container, you'll get a ClassNotFoundException.  That happens because
> >  Class.forName() and friends start from the classloader that loaded the
> >  calling class itself (i.e. the commons-foo class doing this work) ... but
> >  your application class is not visible because you can only look *up* a
> >  classloader hierarchy, not down.
>
> Can you briefly explain how struts even works when loaded from a shared
> ClassLoader? My reading of this suggests that shared struts would not be
> able to load actions found in the webapp's ClassLoader(s), but it sounds
> like the OP has done this successfully.
>

Yep ... it's magic :-).

Actually, what happens is that the servlet container provides a
mechanism to acquire the ClassLoader instance for the web application
itself, from which you can load application classes no matter where
the caller is located.  For instance, when Struts loads an action
class, it does something like this (plus some exception catching, of
course):

    String actionClassName = ...;
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    Class actionClass = cl.loadClass(actionClassName);

The container guarantees that the context class loader for a thread is
set correctly, before it enters your servlet, filter, or listener.

> I'm just curious how it is done because you say above that this is a
> naive implementation... how does one do this in a smart way? It's not
> too relevant to the question... just thought I'd take the opportunity to
> find out since I've never heard of a good way to search "down" the
> ClassLoader hierarchy.
>

There is indeed no way to search down the hierarchy, which is why the
context class loader convention was created.  As you examine libraries
for potentially putting them into a shared class loader, look for code
that does this kind of thing.

> Thanks,
> - -chris
>

Craig


> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.7 (MingW32)
> Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
>
> iD8DBQFGJMBM9CaO5/Lv0PARAqcPAJ9vnk8pj9El1gWtmqPv2smLYkSyxgCbBjOx
> 1nEpmu62YlqqDaqjHYLEkN8=
> =HxZg
> -----END PGP SIGNATURE-----
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
> For additional commands, e-mail: user-help@struts.apache.org
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org


Re: Struts 1.3.8: "Sidebar: Sharing JAR Files Across Web Applications"

Posted by Christopher Schultz <ch...@christopherschultz.net>.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Craig,

Craig McClanahan wrote:
> The naive way for a library to do this is:
>
>    String className = ...; // Calculate the name of the class you want
>    Class clazz = Class.forName(className);
> 
> * If commons-foo.jar is stored in a shared classloader provided by your
>  container, you'll get a ClassNotFoundException.  That happens because
>  Class.forName() and friends start from the classloader that loaded the
>  calling class itself (i.e. the commons-foo class doing this work) ... but
>  your application class is not visible because you can only look *up* a
>  classloader hierarchy, not down.

Can you briefly explain how struts even works when loaded from a shared
ClassLoader? My reading of this suggests that shared struts would not be
able to load actions found in the webapp's ClassLoader(s), but it sounds
like the OP has done this successfully.

I'm just curious how it is done because you say above that this is a
naive implementation... how does one do this in a smart way? It's not
too relevant to the question... just thought I'd take the opportunity to
find out since I've never heard of a good way to search "down" the
ClassLoader hierarchy.

Thanks,
- -chris

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (MingW32)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFGJMBM9CaO5/Lv0PARAqcPAJ9vnk8pj9El1gWtmqPv2smLYkSyxgCbBjOx
1nEpmu62YlqqDaqjHYLEkN8=
=HxZg
-----END PGP SIGNATURE-----

---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org


Re: Struts 1.3.8: "Sidebar: Sharing JAR Files Across Web Applications"

Posted by Craig McClanahan <cr...@apache.org>.
On 4/16/07, Ken Miller <kl...@shetlandsoftware.com> wrote:
>
> Can someone point me (or at least give me a clue) where a potential
> problem may exist?  I've configured my server to have a shared set of
> Commons and Struts jars (something I did before reading the above
> paragraph), and it appears to work, but I'd like to investigate a few
> situations which may cause problems to better understand the possible
> pitfalls.
>

There are at least two potential scenarios to watch out for.

First, consider a case where you have a library commons-foo that your
webapp depends on.  Now, assume this library has a static variable
"bar" defined in one of its classes.  The challenge you have different
semantics depending on where commons-foo.jar is located.

* If commons-foo.jar is within /WEB-INF/lib, the static variable is
  global only to that particular web application.  If two different webapps
  use this library, their uses of the static variable do not conflict.

* If commons-foo.jar is stored in a shared classloader provided by your
  container (for example, in the shared/lib or common/lib directory for Tomcat)
  the global is shared *across* webapps, and any changes from webapp A
  can adversely impact webapp B.

Second, consider what happens if library commons-foo needs to
dynamically instantiate an instance of an application-provided class.
The naive way for a library to do this is:

    String className = ...; // Calculate the name of the class you want
    Class clazz = Class.forName(className);

Again, there are two different semantics (assuming the class to be
loaded is part of your webapp, not part of the shared hierarchy):

* If commons-foo.jar is within /WEB-INF/lib, the classloader can see your
  application class, and load it with no problems.

* If commons-foo.jar is stored in a shared classloader provided by your
  container, you'll get a ClassNotFoundException.  That happens because
  Class.forName() and friends start from the classloader that loaded the
  calling class itself (i.e. the commons-foo class doing this work) ... but
  your application class is not visible because you can only look *up* a
  classloader hierarchy, not down.

The libraries that Struts itself depends on have likely shaken out
most of the bugs like this since I wrote those warnings several years
ago :-).  But the only way to figure out if an arbitrary library will
work in a shared environment is to exhaustively test and/or audit the
code for statics and dynamic object creation.

Craig

---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org