You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@buildr.apache.org by Ed Smiley <es...@ebrary.com> on 2010/10/07 23:51:04 UTC

Odd stuff in Java classpath

I am seeing, depending on the context of where I am in the build state, different behavior of the Java object's class path.

Why this matters to me: 
My build not only complies and packages Java source, but generates a large number of xml configuration files with classnames for dependency injection.
I want to have the developer have the option of turning on a test that all referenced classes are instantiatable.  

Now when I access a class in any of my compilation dependencies I can get to the Java.classpath object and insert the values into it.
Then any class in any of those jars can be invoked.  Basically something like this works, when invoked at the end of each subproject:

Java.classpath << all_libs # a little more complicated, but like this, and this works
Java.java.lang.Class.forName(dep.class).asSubclass(Java.java.lang.Class.forName(dep.subclass)) # subclass is Object if not specified, this too works

So my dependency injection, which is distributed with the components can be validated against any of the classes it is compiled against.  However I want to also distribute custom classes with the artifacts.
However, I cannot make each subproject have a circular dependency on :package, which is what happens.  To get the packaged components validated against the configuration files, I need to arrange the dependencies such that the check is done in a custom task that depends on :package, so that I can add in checks with the created artifacts.  There's no problem in setting the Java classpath, but it is no longer honored, and classes that used to work, now don't.

If I do the following
      puts "CLASSPATH"
      p Java.classpath 
      Java.java.lang.System.out.println(Java.java.lang.System.getProperty("java.class.path"))


I get
CLASSPATH
[["org.apache.ant:ant:jar:1.8.0", "org.apache.ant:ant-launcher:jar:1.8.0", "org.apache.ant:ant-trax:jar:1.8.0"], ["/Library/Ruby/Gems/1.8/gems/buildr-1.4.2/lib/buildr/java"], ["isis-toolkit/client-server.jar", "isis-toolkit/common.jar", "isis-toolkit/document-aserver.jar", "isis-toolkit/document-client.jar", "isis-toolkit/document-processor.jar", "isis-toolkit/netstore.jar", "isis-toolkit/search-server.jar", "isis-toolkit/server.jar", "isis-toolkit/software-server.jar", "isis-toolkit/transport-server.jar", "isis-toolkit/user-server.jar", "isis-toolkit/util.jar"]]

/Users/esmiley/.m2/repository/org/apache/ant/ant/1.8.0/ant-1.8.0.jar:/Users/esmiley/.m2/repository/org/apache/ant/ant-launcher/1.8.0/ant-launcher-1.8.0.jar:/Users/esmiley/.m2/repository/org/apache/ant/ant-trax/1.8.0/ant-trax-1.8.0.jar:/Library/Ruby/Gems/1.8/gems/buildr-1.4.2/lib/buildr/java

In this new context, where it appears to need to be, it appears that I can set the classpath until the cows come home in the Java object, but it is not being honored.  The classes that passed the test before now throw NoClassDefFoundErrors. 


--Ed

Re: Odd stuff in Java classpath

Posted by Alex Boisvert <al...@gmail.com>.
Hi Ed,

The Java.classpath is a little finicky because it can't be changed once RJB
has been loaded, and that loading usually happens after the buildfile has
been read but before buildr starts running tasks.

For you purpose, I would suggest running an external Java process where you
can more easily control the classpath.

Here's an example,

define :foo do

  compile.with ...
  test.with ...

  task :validate do
    Java::Commands.java ["org.example.CheckInjection", "-file context.xml"],
      :classpath => [ compile.dependencies, compile.target, all_libs, ... ]
  end
end

Project.local_task :validate

After which you can run:

buildr foo:validate

to validate if you injection context is valid.

alex


On Thu, Oct 7, 2010 at 2:51 PM, Ed Smiley <es...@ebrary.com> wrote:

> I am seeing, depending on the context of where I am in the build state,
> different behavior of the Java object's class path.
>
> Why this matters to me:
> My build not only complies and packages Java source, but generates a large
> number of xml configuration files with classnames for dependency injection.
> I want to have the developer have the option of turning on a test that all
> referenced classes are instantiatable.
>
> Now when I access a class in any of my compilation dependencies I can get
> to the Java.classpath object and insert the values into it.
> Then any class in any of those jars can be invoked.  Basically something
> like this works, when invoked at the end of each subproject:
>
> Java.classpath << all_libs # a little more complicated, but like this, and
> this works
> Java.java.lang.Class.forName(dep.class).asSubclass(Java.java.lang.Class.forName(dep.subclass))
> # subclass is Object if not specified, this too works
>
> So my dependency injection, which is distributed with the components can be
> validated against any of the classes it is compiled against.  However I want
> to also distribute custom classes with the artifacts.
> However, I cannot make each subproject have a circular dependency on
> :package, which is what happens.  To get the packaged components validated
> against the configuration files, I need to arrange the dependencies such
> that the check is done in a custom task that depends on :package, so that I
> can add in checks with the created artifacts.  There's no problem in setting
> the Java classpath, but it is no longer honored, and classes that used to
> work, now don't.
>
> If I do the following
>      puts "CLASSPATH"
>      p Java.classpath
>
>  Java.java.lang.System.out.println(Java.java.lang.System.getProperty("java.class.path"))
>
>
> I get
> CLASSPATH
> [["org.apache.ant:ant:jar:1.8.0", "org.apache.ant:ant-launcher:jar:1.8.0",
> "org.apache.ant:ant-trax:jar:1.8.0"],
> ["/Library/Ruby/Gems/1.8/gems/buildr-1.4.2/lib/buildr/java"],
> ["isis-toolkit/client-server.jar", "isis-toolkit/common.jar",
> "isis-toolkit/document-aserver.jar", "isis-toolkit/document-client.jar",
> "isis-toolkit/document-processor.jar", "isis-toolkit/netstore.jar",
> "isis-toolkit/search-server.jar", "isis-toolkit/server.jar",
> "isis-toolkit/software-server.jar", "isis-toolkit/transport-server.jar",
> "isis-toolkit/user-server.jar", "isis-toolkit/util.jar"]]
>
>
> /Users/esmiley/.m2/repository/org/apache/ant/ant/1.8.0/ant-1.8.0.jar:/Users/esmiley/.m2/repository/org/apache/ant/ant-launcher/1.8.0/ant-launcher-1.8.0.jar:/Users/esmiley/.m2/repository/org/apache/ant/ant-trax/1.8.0/ant-trax-1.8.0.jar:/Library/Ruby/Gems/1.8/gems/buildr-1.4.2/lib/buildr/java
>
> In this new context, where it appears to need to be, it appears that I can
> set the classpath until the cows come home in the Java object, but it is not
> being honored.  The classes that passed the test before now throw
> NoClassDefFoundErrors.
>
>
> --Ed

Re: Good stuff in Java classpath

Posted by Alex Boisvert <al...@gmail.com>.
On Fri, Oct 8, 2010 at 9:29 AM, Ed Smiley <es...@ebrary.com> wrote:

> Well, it's fine when you understand it, as you can usually get around it.
> I do find that allowing Java.classpath to act as if it were writable is
> terribly misleading, since any debugging code you add will at as if it had,
> indeed been modified.
> Fortunately I was skeptical enough to suspect this.  Took a while.....
>  :) Might help to have better documentation of the Java object.
>

Agreed.  I've had this on my documentation TODO list for some time.  I've
also been meaning to issue a warning when Java.classpath is modified after
Java.load has happened.

Added an issue to track this:
https://issues.apache.org/jira/browse/BUILDR-523

alex

Re: Good stuff in Java classpath

Posted by Ed Smiley <es...@ebrary.com>.
Well, it's fine when you understand it, as you can usually get around it.
I do find that allowing Java.classpath to act as if it were writable is terribly misleading, since any debugging code you add will at as if it had, indeed been modified.
Fortunately I was skeptical enough to suspect this.  Took a while.....    :) Might help to have better documentation of the Java object.

--Ed
On Oct 8, 2010, at 7:16 AM, Alex Boisvert wrote:

> On Thu, Oct 7, 2010 at 7:01 PM, Ed Smiley <es...@ebrary.com> wrote:
> 
>> I found a workaround. :)
>> 
> 
> Good to hear!
> 
> 
>> You can stuff the jars that will be packaged into Java.classpath EVEN if
>> they don't exist (yet).
>> 
>> When they DO exist, and you are checking a release or running tests, the
>> classpath becomes correct and it works.
>> It doesn't seem to have any other ill effects.  Not sure why Java.classpath
>> needs to be set so early in the game.  Is that a design flaw?
>> 
> 
> It's a design limitation, yes.  First, it's due to how the JVM works with a
> static classpath set on startup.  RJB does no magic here, it simply starts
> an embedded JVM and passes it the defined classpath before a Java class is
> invoke from Ruby.
> 
> The semi-good-news:  The latest version of RJB (1.2.9) now creates a new
> URLClassLoader right on top of the system classloader and allows new
> classpath elements to be added at runtime.   This helps although it
> introduces some fragility with an additional classloader (some libraries
> assume/want to be in the system classloader).
> 
> However, I had some issues with RJB 1.2.9 when I tried it out so I backed
> out the change and pushed the upgrade to later.  We'll need to do some
> debugging before upgrading.
> 
> alex


Re: Good stuff in Java classpath

Posted by Alex Boisvert <al...@gmail.com>.
On Thu, Oct 7, 2010 at 7:01 PM, Ed Smiley <es...@ebrary.com> wrote:

> I found a workaround. :)
>

Good to hear!


> You can stuff the jars that will be packaged into Java.classpath EVEN if
> they don't exist (yet).
>
> When they DO exist, and you are checking a release or running tests, the
> classpath becomes correct and it works.
> It doesn't seem to have any other ill effects.  Not sure why Java.classpath
> needs to be set so early in the game.  Is that a design flaw?
>

It's a design limitation, yes.  First, it's due to how the JVM works with a
static classpath set on startup.  RJB does no magic here, it simply starts
an embedded JVM and passes it the defined classpath before a Java class is
invoke from Ruby.

The semi-good-news:  The latest version of RJB (1.2.9) now creates a new
URLClassLoader right on top of the system classloader and allows new
classpath elements to be added at runtime.   This helps although it
introduces some fragility with an additional classloader (some libraries
assume/want to be in the system classloader).

However, I had some issues with RJB 1.2.9 when I tried it out so I backed
out the change and pushed the upgrade to later.  We'll need to do some
debugging before upgrading.

alex

Good stuff in Java classpath

Posted by Ed Smiley <es...@ebrary.com>.
I found a workaround. :)

You can stuff the jars that will be packaged into Java.classpath EVEN if they don't exist (yet).

When they DO exist, and you are checking a release or running tests, the classpath becomes correct and it works.
It doesn't seem to have any other ill effects.  Not sure why Java.classpath needs to be set so early in the game.  Is that a design flaw?  


pseudocode
.....

 define "something" do
  ...
    compile stuff....
    package stuff...
    set up the classpath including the NONEXISTENT packaged archives ...<====
 end

    task "somelatertask" => :package do             # the packaged archives exist now
.....
         deps.each do |dep| 
         Java.java.lang.Class.forName(dep.class).asSubclass(Java.java.lang.Class.forName(dep.subclass)) # <== succeeds if class exists
      end



--Ed
PS Certainly better than trying to build my own classloader in Java invoked in Ruby....




On Oct 7, 2010, at 2:51 PM, Ed Smiley wrote:

> I am seeing, depending on the context of where I am in the build state, different behavior of the Java object's class path.
> 
> Why this matters to me: 
> My build not only complies and packages Java source, but generates a large number of xml configuration files with classnames for dependency injection.
> I want to have the developer have the option of turning on a test that all referenced classes are instantiatable.  
> 
> Now when I access a class in any of my compilation dependencies I can get to the Java.classpath object and insert the values into it.
> Then any class in any of those jars can be invoked.  Basically something like this works, when invoked at the end of each subproject:
> 
> Java.classpath << all_libs # a little more complicated, but like this, and this works
> Java.java.lang.Class.forName(dep.class).asSubclass(Java.java.lang.Class.forName(dep.subclass)) # subclass is Object if not specified, this too works
> 
> So my dependency injection, which is distributed with the components can be validated against any of the classes it is compiled against.  However I want to also distribute custom classes with the artifacts.
> However, I cannot make each subproject have a circular dependency on :package, which is what happens.  To get the packaged components validated against the configuration files, I need to arrange the dependencies such that the check is done in a custom task that depends on :package, so that I can add in checks with the created artifacts.  There's no problem in setting the Java classpath, but it is no longer honored, and classes that used to work, now don't.
> 
> If I do the following
>       puts "CLASSPATH"
>       p Java.classpath 
>       Java.java.lang.System.out.println(Java.java.lang.System.getProperty("java.class.path"))
> 
> 
> I get
> CLASSPATH
> [["org.apache.ant:ant:jar:1.8.0", "org.apache.ant:ant-launcher:jar:1.8.0", "org.apache.ant:ant-trax:jar:1.8.0"], ["/Library/Ruby/Gems/1.8/gems/buildr-1.4.2/lib/buildr/java"], ["isis-toolkit/client-server.jar", "isis-toolkit/common.jar", "isis-toolkit/document-aserver.jar", "isis-toolkit/document-client.jar", "isis-toolkit/document-processor.jar", "isis-toolkit/netstore.jar", "isis-toolkit/search-server.jar", "isis-toolkit/server.jar", "isis-toolkit/software-server.jar", "isis-toolkit/transport-server.jar", "isis-toolkit/user-server.jar", "isis-toolkit/util.jar"]]
> 
> /Users/esmiley/.m2/repository/org/apache/ant/ant/1.8.0/ant-1.8.0.jar:/Users/esmiley/.m2/repository/org/apache/ant/ant-launcher/1.8.0/ant-launcher-1.8.0.jar:/Users/esmiley/.m2/repository/org/apache/ant/ant-trax/1.8.0/ant-trax-1.8.0.jar:/Library/Ruby/Gems/1.8/gems/buildr-1.4.2/lib/buildr/java
> 
> In this new context, where it appears to need to be, it appears that I can set the classpath until the cows come home in the Java object, but it is not being honored.  The classes that passed the test before now throw NoClassDefFoundErrors. 
> 
> 
> --Ed