Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 353165 - WebAppClassLoader excludes symlinked jars from classpath (regression)
Summary: WebAppClassLoader excludes symlinked jars from classpath (regression)
Status: CLOSED FIXED
Alias: None
Product: Jetty
Classification: RT
Component: server (show other bugs)
Version: 7.4.3   Edit
Hardware: PC Linux
: P3 normal (vote)
Target Milestone: 7.5.x   Edit
Assignee: Jesse McConnell CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-07-26 17:04 EDT by Phil Clay CLA
Modified: 2011-07-29 12:25 EDT (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Phil Clay CLA 2011-07-26 17:04:38 EDT
Build Identifier: jetty-7.4.3.v20110701

In Revision 1932...
http://dev.eclipse.org/viewcvs/viewvc.cgi/jetty/trunk/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppClassLoader.java?root=RT_JETTY&r1=1627&r2=1932

... a change was made to WebAppClassLoader.addJars() that made it so that directories within the war's lib directory are not added to the classpath.


Specifically, line 214 changed from...

    if (isFileSupported(fnlc))

to...

    if (!fn.isDirectory() && isFileSupported(fnlc))


This change broke my dev team's development process.  We noticed this because we are upgrading from jetty 7.0.1.v20091125 to jetty-7.4.3.v20110701

In our development environment, to enable speedy development, we have a script
that replaces our jars in the WEB-INF/lib directory with symlinks to our eclipse
project output directories.  This enables us to use rapid development without
having to re-jar everything in order to test it.

For example...

$ ls -l WEB-INF/lib/
-rw-rw-r--  1 someuser somegroup   96221 Jul 26 16:15 commons-pool.jar
lrwxrwxrwx  1 someuser somegroup      44 Jul 26  2011 custom-module1.jar -> ../../../../../custom-module1/target/package
lrwxrwxrwx  1 someuser somegroup      37 Jul 26  2011 custom-module2.jar -> ../../../../../custom-module1/target/package
-rw-rw-r--  1 someuser somegroup 3083100 Jul 26 16:15 hibernate-core.jar


Since the symlinks are reported as directories, our jars (or more specifically, our symlinks that point to build output dirs) are no longer being included in the webapp classpath, which completely breaks our webapp during development.


I would like to understand why this change was made.  If possible, I would like that line reverted.  Otherwise, I would like some way to configure the WebAppClassLoader to allow directories within the lib dir to be placed on the classpath.

Reproducible: Always

Steps to Reproduce:
1. Create an expanded war with a symlink in the lib directory pointing to a directory containing the contents of a jar file
2. Start deploy the war on jetty
3. Ensure jar directory is on the webapp classpath
Comment 1 Jesse McConnell CLA 2011-07-28 17:21:27 EDT
How are you running your app, embedded or as the distribution?  I just cobbled together some unit testing for this and its working fine, I can't reproduce your stated case here.  createSymLink is a call out to Process since we its java 1.6 so I am a bit loathe to commit this as an honest unit test...but this works when tacked onto the end if the testWebAppLoad() test case.  I am putting the jetty-test-policy jar file into the target directory with the dependency maven plugin so I have  jar to work with on this.  I am resolving (not closing) as works for me but am open to discussing further.



        File symlinkTestDir = MavenTestingUtils.getTargetTestingDir("symlinkTest");
        symlinkTestDir.mkdirs();
        createSymLink(MavenTestingUtils.getTargetFile("jetty-test-policy.jar").toString(),symlinkTestDir.toString() + "/jetty-test-policy.jar");
     
        _loader.addClassPath(symlinkTestDir.toString() + "/jetty-test-policy.jar");
        //_loader.addJars(Resource.newResource(symlinkTestDir.toString()));
        
        assertTrue(canLoadClass("org.eclipse.jetty.toolchain.test.policy.Tester"));
Comment 2 Phil Clay CLA 2011-07-28 22:22:49 EDT
(In reply to comment #1)

Thanks for taking a look at this so quickly!

The problem is actually with the addJars method, not addClassPath.


The following line in addJars is what is causing the problem.

    if (!fn.isDirectory() && isFileSupported(fnlc))


During war deployment, the addJars method is called with the META-INF/lib dir as the parameter.  With the !fn.isDirectory() check, the end result is that addClasspath is not called for the symlink. 


In your test case, it looks like you thought about that, but you have it commented out.

If you reverse the comments on these lines, you should be able to reproduce the behavior I am seeing.  (i.e. comment the addClassPath call, and uncomment the addJars call)


>         _loader.addClassPath(symlinkTestDir.toString() +
> "/jetty-test-policy.jar");
>         //_loader.addJars(Resource.newResource(symlinkTestDir.toString()));
> 
>        
> assertTrue(canLoadClass("org.eclipse.jetty.toolchain.test.policy.Tester"));
Comment 3 Jesse McConnell CLA 2011-07-29 08:16:07 EDT
Sorry, I should have mentioned it worked with both mechanisms in the test.

I am testing on ubuntu, what are you working with this on?
Comment 4 Phil Clay CLA 2011-07-29 10:14:25 EDT
(In reply to comment #3)
> Sorry, I should have mentioned it worked with both mechanisms in the test.
> 
> I am testing on ubuntu, what are you working with this on?


Ohhhhh, I see the problem in your testcase.  Your symlink points to a file.  My symlink points to a directory (containing the expanded contents of the jar, which happens to be the output path of the associated eclipse project).

For example...

$ ls -l WEB-INF/lib/
-rw-rw-r--  1 someuser somegroup   96221 Jul 26 16:15 commons-pool.jar
lrwxrwxrwx  1 someuser somegroup      44 Jul 26  2011 custom-module1.jar ->
../../../../../custom-module1/target/package


$ ls -l WEB-INF/lib/custom-module1.jar/
-rw-rw-r--  1 someuser somegroup   96221 Jul 26 16:15 com/


(I'm on Ubuntu as well.)
Comment 5 Jesse McConnell CLA 2011-07-29 10:46:35 EDT
I see what your doing.  Since your doing all this for eclipse purposes I would point out that there has been some nice success with a project called Webby coupled with jrebel for webapp development in eclipe...

but I'll take a look at this case as well now
Comment 6 Jesse McConnell CLA 2011-07-29 11:14:26 EDT
Hm, I don't like this...your basically trying to fool the webapp classloader by calling something a jar which it isn't.

How are you starting this up, is it an embedded use case?  Using addClasspath() works because it really is just a classpath entry, not in fact a jar file.
Comment 7 Jesse McConnell CLA 2011-07-29 11:16:00 EDT
That is to say that addClasspath on your symlinked jar file directory dealio does the right thing as would be expected.
Comment 8 Phil Clay CLA 2011-07-29 11:59:16 EDT
This is an embedded use case.  We have full control over how jetty deploys our wars.

However, I would really prefer to not call addClasspath() manually.  I would much prefer it to automatically happen.


Mainly because our "real" deployment will use a war file, as opposed to an expanded war.  And therefore, our "real" deployment will not be using symlinks.

I would prefer that both cases remain the same.  i.e. I want to avoid having to call addClasspath() in our dev case, but NOT call it in the "real" deployment scenario.


We use symlinks to speed up the development time.  Meaning, that all we have to do is build with an IDE (some people use eclipse, some use intellij, whatever), and we don't have to worry about creating a jar file for every little change we make.


> your basically trying to fool the webapp classloader by
> calling something a jar which it isn't.

I don't really see a downside of this.  I see it as comparable to being able
to deploy a war file OR an expanded war directory.  The underlying URLClassLoader logic doesn't care if the entry is a file or a directory, so I don't believe jetty should enforce an additional constraint (considering it works fine without the !isDirectory() check).
Comment 9 Jesse McConnell CLA 2011-07-29 12:25:15 EDT
ok, well I removed the check and we'll see if it passes muster