Hi Gus:

Sorry for the delayed reply - I’m on vacation and my internet access is a 
little spotty.

PreferredClassLoader is important in the long run, but you probably won’t 
notice the lack of it until you get to some more complicated service scenarios. 
 In the short term, you could probably do without, however It might be a little 
complicated to get rid of.

Unfortunately, to explain further, we need some more background info.  Bear 
with me for a few minutes, we’ll come back to the problem, I promise….

Jini is a “mobile-code” style of network architecture, although that may not be 
entirely obvious.  When you pass arguments to a service call, or receive return 
values, what’s actually being passed is a serialized, or “marshalled” version 
of the object as it exists inside the virtual machine (VM).  This includes 
“looking up a service” - what you’re receiving is a marshalled version of the 
proxy to the that service.

Serialization is typically performed by ‘net.jini.io.MarshallInputStream’ and 
‘net.jini.io.MarshallOutputStream’.   These classes extend ObjectInputStream 
and ObjectOutputStream, respectively.  The marshalled form includes a copy of 
all the data inside the instance, plus the name of the class, and a “codebase 
annotation” that provides a url that points to where the class can be 
downloaded from (hence mobile code).

When a marshalled object is loaded into a local virtual machine, the 
MarshallInputStream needs to create an instance of the object’s class, and then 
put the instance data into it.  It will create a new class loader that loads 
bytecode from the “codebase annotation url”, and then use that class loader to 
create the local instance of the marshalled object.

Keep in mind that “Foo” loaded by class loader CL-B can implement an interface 
“Bar”, or extend a class “Bas” that is loaded by class loader CL-A.  So it’s 
entirely possible that the user of “Foo” has no knowledge of, or access to the 
actual implementation class (e.g. by declaring a reference to “Bar” or “Bas” 
and then storing a reference to “Foo” in it).  Also, this applies to fields of 
an instance as well.

But what happens if the same class name exists in the local VM’s class path?  
In most cases, we’d rather use the locally-defined byte code.  Partly for 
efficiency reasons (avoid downloading the jar file) but also because if we want 
to “use” the class locally, it needs to come from the same class loader (in 
other words, “Foo” loaded by class loader CL-A is actually a different class 
than “Foo” loaded by class loader CL-B, even if the actual byte code is the 
same).  So the default behaviour is to use a locally-available class if there 
is one, and only use a different class loader if the required class is not 
available locally.

If we load a marshalled object, and we already have the same class available 
locally, what happens to the codebase annotation that was on the marshalled 
object?  It’s gone, since the codebase annotation goes with the codebase, which 
clearly is associated with the class loader.  Usually that’s OK, but what if 
the service really wants to use the code that it’s providing remotely?  That’s 
where PreferredClassLoader comes in.

PreferredClassLoader takes a look at a file in the first “jar” file in the 
codebase annotation (META-INF/Prefered.list), and treats it as a list of 
“Preferred” classes.  These are classes where the downloaded code is 
“Preferred” to the locally-available class.  That way the codebase annotation 
sticks, not to mention that the service provides the byte code that it’s 
expecting.

That’s not a really common use case, so as I say, you might not notice the lack 
of PreferredClassLoader.  It’s plugged in using an SPI provider  
(META-INF.services/java.rmi.server.RMIClassLoaderSpi) file that’s in 
jsk-resources.jar.  So to remove PreferredClassLoader properly, you need to put 
a different version of jsk-resources.jar into your class path.

There’s another possible problem, although I can’t prove it - just a bad 
feeling that I get.  Having read through all the above, you might notice that 
there’s a lot of class loader hocus-pocus going on.  Remote code, and by 
extension, Jini, is pretty fussy about class loading, and class loader 
hierarchy.  In addition to on-demand loading (mobile code), the library uses 
things like the Preferred list, and Spi configuration files, that are also 
loaded through the class loaders.  Although there’s nothing illegal, it’s an 
advanced use of the class loading mechanism, so it wouldn’t shock me if the 
class loader used in your “one-jar” implementation isn’t complete enough to 
support it.  I’ve come across other class loader implementations (e.g. Apache 
Virtual File System) that didn’t work properly, and you mentioned that your 
‘one-jar’ loader has a known bug around loading classes by name (which, if you 
think about it, is what needs to happen for just about all the remote class 
loading).  Also, you need to be able to set the codebase annotation on a given 
class loader.

A “one-jar” library is really a custom class loader that gets it’s byte code 
from files that are inside the jar file.  So, it’s plausible that any given 
“one-jar” approach could cause problems.

That’s why a container approach is usually better.  There’s a rudimentary 
container provided with River, ‘com.sun.jini.start.ServiceStarter' which is 
unfortunately not used in the “getting started” doc (not sure who wrote it, but 
I’m afraid that I personally don’t have time to re-write it just now).  You can 
see an example of its use in the service registrar startup that’s mentioned in 
that doc.  More advanced containers are available in the River-Container 
project (https://github.com/trasukg/river-container) or the Rio project 
(http://rio-project.org).

Essentially, Jini needs class loaders that are designed for Jini.  (Really, 
it’s not Jini - mobile code needs mobile-code-aware class loaders).  My 
experience has been that it’s possible to put simple service consumers into 
non-Jini-aware class loaders (e.g. web application containers like Tomcat, or 
command-line class paths) with a few limitations.  Hosting service 
implementations without ServiceStarter, River-Container, Rio, or some other 
Jini-aware environment will be a complete exercise in frustration.

So, have a look at the way the JavaSpaces implementation gets started up (it’s 
the closes to a plain service implementation) and copy that.  Or have a look at 
River-Container.

And I suppose we ought to renovate the “getting started” page.  You might also 
want to have a look at the “hello” example, as mentioned in 
http://river.apache.org/doc/info-index.html#example.

Cheers,

Greg.


On Jul 7, 2014, at 8:19 PM, Gus Heck <[email protected]> wrote:

> Sorry, sometimes I have a habit of over explaining and obscuring the
> important question... How critical is PreferredClassProvider? Is it really
> needed? Can I do without it?
> 
> 
> On Mon, Jul 7, 2014 at 10:23 AM, Gus Heck <[email protected]> wrote:
> 
>> Yes the build tool is gradle. It pulls resources from maven central just
>> like maven. It's worth checking out if you haven't. It uses the cool dep
>> resolution stuff from maven but ditches the pom straight-jacket.
>> 
>> I found the class, in the jar, overlooked it the first time. I searched on
>> loader when I opened the jar with emacs and didn't notice that the package
>> I found was com/sun/jini not net/jini (oops!). So that's resolved, but this
>> only makes things muddier. The jar is being packed with one-jar (which is a
>> tool that allows you to produce a jar containing both your classes and
>> their dependencies in the same jar). Clearly many other classes are loading
>> and so I turned on one-jar's logging and got this:
>> 
>> guss-mbp:ingest gus$ j7a -Done-jar.info=true -jar
>> build/libs/ingest-node.jar foo bar 2>&1 | grep Preferred
>> [JarClassLoader] WARN:
>> com/sun/jini/loader/pref/internal/PreferredResources.class in
>> lib/jsk-platform-2.2.2.jar is hidden by lib/jini-ext-2.1.jar (with
>> different bytecode)
>> [JarClassLoader] WARN:  net/jini/loader/pref/PreferredClassLoader$1.class
>> in lib/jsk-platform-2.2.2.jar is hidden by lib/jini-ext-2.1.jar (with
>> different bytecode)
>> [JarClassLoader] WARN:  net/jini/loader/pref/PreferredClassLoader$2.class
>> in lib/jsk-platform-2.2.2.jar is hidden by lib/jini-ext-2.1.jar (with
>> different bytecode)
>> [JarClassLoader] WARN:  net/jini/loader/pref/PreferredClassLoader$3.class
>> in lib/jsk-platform-2.2.2.jar is hidden by lib/jini-ext-2.1.jar (with
>> different bytecode)
>> [JarClassLoader] WARN:  net/jini/loader/pref/PreferredClassLoader$4.class
>> in lib/jsk-platform-2.2.2.jar is hidden by lib/jini-ext-2.1.jar (with
>> different bytecode)
>> [JarClassLoader] WARN:  net/jini/loader/pref/PreferredClassLoader.class in
>> lib/jsk-platform-2.2.2.jar is hidden by lib/jini-ext-2.1.jar (with
>> different bytecode)
>> [JarClassLoader] WARN:
>> net/jini/loader/pref/PreferredClassProvider$1.class in
>> lib/jsk-platform-2.2.2.jar is hidden by lib/jini-ext-2.1.jar (with
>> different bytecode)
>> [JarClassLoader] WARN:
>> net/jini/loader/pref/PreferredClassProvider$2.class in
>> lib/jsk-platform-2.2.2.jar is hidden by lib/jini-ext-2.1.jar (with
>> different bytecode)
>> [JarClassLoader] WARN:
>> net/jini/loader/pref/PreferredClassProvider$3.class in
>> lib/jsk-platform-2.2.2.jar is hidden by lib/jini-ext-2.1.jar (with
>> different bytecode)
>> [JarClassLoader] WARN:
>> net/jini/loader/pref/PreferredClassProvider$4.class in
>> lib/jsk-platform-2.2.2.jar is hidden by lib/jini-ext-2.1.jar (with
>> different bytecode)
>> [JarClassLoader] WARN:
>> net/jini/loader/pref/PreferredClassProvider$5.class in
>> lib/jsk-platform-2.2.2.jar is hidden by lib/jini-ext-2.1.jar (with
>> different bytecode)
>> [JarClassLoader] WARN:
>> net/jini/loader/pref/PreferredClassProvider$LoaderEntry.class in
>> lib/jsk-platform-2.2.2.jar is hidden by lib/jini-ext-2.1.jar (with
>> different bytecode)
>> [JarClassLoader] WARN:
>> net/jini/loader/pref/PreferredClassProvider$LoaderKey.class in
>> lib/jsk-platform-2.2.2.jar is hidden by lib/jini-ext-2.1.jar (with
>> different bytecode)
>> [JarClassLoader] WARN:  net/jini/loader/pref/PreferredClassProvider.class
>> in lib/jsk-platform-2.2.2.jar is hidden by lib/jini-ext-2.1.jar (with
>> different bytecode)
>> [JarClassLoader] WARN:
>> net/jini/loader/pref/PreferredFactoryClassLoader.class in
>> lib/jsk-platform-2.2.2.jar is hidden by lib/jini-ext-2.1.jar (with
>> different bytecode)
>> java.lang.NoClassDefFoundError: net.jini.loader.pref.PreferredClassProvider
>> 
>> So I took the jini-ext jar out, and all I get is
>> uss-mbp:ingest gus$ j7a -Done-jar.info=true -jar
>> build/libs/ingest-node.jar foo bar 2>&1 | grep Preferred
>> java.lang.NoClassDefFoundError: net.jini.loader.pref.PreferredClassProvider
>> 
>> So it's there, it's found but something odd is going on after that that
>> results in NoClassDefFoundError. This may relate to the JarClassLoader that
>> gets installed, but clearly that works most of the time...
>> 
>> Are you attempting to manually load this class by looking up the .class as
>> file? (one-jar has a known bug along these lines. It loads classes just
>> fine from the included jars, and files from your project just fine. I think
>> it loads files from dep jars just fine too (otherwise lots of stuff would
>> have issues) but it seems to confused if you try to load classes as if they
>> are files: https://sourceforge.net/p/one-jar/bugs/73/)
>> 
>> How critical is PreferredClassProvider to river's operation?
>> 
>> -Gus
>> 
>> 
>> On Mon, Jul 7, 2014 at 9:39 AM, Greg Trasuk <[email protected]>
>> wrote:
>> 
>>> 
>>> PreferredClassProvider should be in jsk-platform.jar (which I think you
>>> confirmed with your ‘grep -r’).  It is there in the 2.2.2 build that I
>>> have.  I’m not familiar with your build tool (Gradle?) but you might want
>>> to check the class path entry in the jar manifest that’s generated, to see
>>> if jsk-platform is actually in the runtime class path.
>>> 
>>> Are you starting up the system using the ServiceStarter approach or
>>> winging it yourself?  If ServiceStarter, could you post the starter
>>> configuration file?
>>> 
>>> Cheers,
>>> 
>>> Greg Trasuk.
>>> On Jul 6, 2014, at 8:27 PM, Gus Heck <[email protected]> wrote:
>>> 
>>>> I've now spent several hours hunting for this class. What jar contains
>>> it?
>>>> 
>>>> Presently I'm building with the following (shotgun, messy non minimized)
>>>> dependencies:
>>>> dependencies {
>>>> compile 'com.google.guava:guava:15.0'
>>>> compile 'com.google.code.findbugs:jsr305:2.0.3'
>>>> compile 'jini:jini-core:2.1'
>>>> compile 'org.apache.river:reggie:2.2.2'
>>>> compile 'net.jini:jini-ext:2.1'
>>>> compile 'net.jini:jsk-lib:2.2.2'
>>>> compile 'net.jini:jsk-dl:2.2.2'
>>>> compile 'net.jini:jsk-platform:2.2.2'
>>>> compile 'net.jini:jsk-resources:2.2.2'
>>>> compile 'net.jini:jini-core:2.1'
>>>> compile 'net.jini:jini-ext:2.1'
>>>> compile 'net.jini:jsk-policy:2.2.2'
>>>> //    compile 'net.jini:jini-starterkit:2.1-beta2' // 404 in maven
>>> central
>>>> compile 'org.apache.logging.log4j:log4j-core:2.0-rc2'
>>>> testCompile 'junit:junit:4.11'
>>>> }
>>>> 
>>>> I basically got frustrated and just threw everything I could find at it
>>> and
>>>> it still does this:
>>>> 
>>>> guss-mbp:ingest gus$ alias j7a
>>>> alias
>>>> 
>>> j7a='/Library/Java/JavaVirtualMachines/jdk1.7.0_60.jdk/Contents/Home/jre/bin/java
>>>> -Djava.security.policy=/Users/gus/tools/jpolicy/all.policy
>>>> 
>>> -Djava.rmi.server.RMIClassLoaderSpi=net.jini.loader.pref.PreferredClassProvider'
>>>> guss-mbp:ingest gus$ j7a -Done-jar.silent=true -jar
>>>> build/libs/ingest-node.jar foo bar
>>>> Starting injester node...
>>>> Jul 06, 2014 7:57:31 PM
>>>> net.jini.discovery.LookupDiscovery$UnicastDiscoveryTask run
>>>> INFO: exception occurred during unicast discovery to guss-mbp.lan:54290
>>>> with constraints InvocationConstraints[reqs: {}, prefs: {}]
>>>> java.lang.NoClassDefFoundError:
>>> net.jini.loader.pref.PreferredClassProvider
>>>> at
>>>> 
>>> java.rmi.server.RMIClassLoader.initializeProvider(RMIClassLoader.java:687)
>>>> at java.rmi.server.RMIClassLoader.access$000(RMIClassLoader.java:110)
>>>> at java.rmi.server.RMIClassLoader$1.run(RMIClassLoader.java:120)
>>>> at java.rmi.server.RMIClassLoader$1.run(RMIClassLoader.java:119)
>>>> at java.security.AccessController.doPrivileged(Native Method)
>>>> at java.rmi.server.RMIClassLoader.<clinit>(RMIClassLoader.java:117)
>>>> at net.jini.loader.ClassLoading.loadClass(ClassLoading.java:138)
>>>> at
>>>> net.jini.io.MarshalInputStream.resolveClass(MarshalInputStream.java:296)
>>>> at
>>>> java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1612)
>>>> at
>>> java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1517)
>>>> at
>>>> 
>>> java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1771)
>>>> at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)
>>>> at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
>>>> at net.jini.io.MarshalledInstance.get(MarshalledInstance.java:358)
>>>> at
>>>> 
>>> com.sun.jini.discovery.DiscoveryV1.doUnicastDiscovery(DiscoveryV1.java:397)
>>>> at
>>> net.jini.discovery.LookupDiscovery$13.run(LookupDiscovery.java:3327)
>>>> at java.security.AccessController.doPrivileged(Native Method)
>>>> at
>>>> 
>>> net.jini.discovery.LookupDiscovery.doUnicastDiscovery(LookupDiscovery.java:3324)
>>>> at
>>>> 
>>> net.jini.discovery.LookupDiscovery.doUnicastDiscovery(LookupDiscovery.java:3355)
>>>> at
>>>> net.jini.discovery.LookupDiscovery.access$3900(LookupDiscovery.java:696)
>>>> at
>>>> 
>>> net.jini.discovery.LookupDiscovery$UnicastDiscoveryTask.run(LookupDiscovery.java:1744)
>>>> at
>>> com.sun.jini.thread.TaskManager$TaskThread.run(TaskManager.java:331)
>>>> 
>>>> No Service Registries found
>>>> 
>>>> I'm passing in the option for this class on the advice of your getting
>>>> started page <
>>> http://river.apache.org/user-guide-basic-river-services.html>.
>>>> It would be really nice if that page listed what dependency I needed for
>>>> this.
>>>> 
>>>> I tried grepping the entire distribution and only found it in two places
>>>> 
>>>> guss-mbp:apache-river-2.2.2 gus$ grep -r PreferredClassProvider *
>>>> <*snip* javadoc matches>
>>>> Binary file lib/jini-ext.jar matches
>>>> Binary file lib/jsk-platform.jar matches
>>>> 
>>>> in both jars I see only:
>>>> drwxr-xr-x         0  10-Nov-2013  22:49:20  com/sun/jini/loader/
>>>> drwxr-xr-x         0  10-Nov-2013  22:49:20  com/sun/jini/loader/pref/
>>>> drwxr-xr-x         0  10-Nov-2013  22:49:20
>>>> com/sun/jini/loader/pref/internal/
>>>> -rw-r--r--      7218  10-Nov-2013  22:49:20
>>>> com/sun/jini/loader/pref/internal/PreferredResources.class
>>>> 
>>>> 
>>>> But in any case as you can see both of these libs are already in my
>>>> dependency list.
>>>> 
>>>> This puzzles me since it's clearly in your repository...
>>>> http://svn.apache.org/viewvc/river/jtsk/trunk/src/net/jini/loader/pref/
>>>> 
>>>> shouldn't it be in at least one of the distributed jars, if not one of
>>> the
>>>> packages on mavencentral?
>>>> 
>>>> -Gus
>>>> 
>>>> --
>>>> http://www.the111shift.com
>>> 
>>> 
>> 
>> 
>> --
>> http://www.the111shift.com
>> 
> 
> 
> 
> -- 
> http://www.the111shift.com

Reply via email to