river-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Peter <j...@zeus.net.au>
Subject Re: OSGi
Date Fri, 27 Jan 2017 12:54:53 GMT
Thanks Gregg,

Thoughts inline below.

Cheers,

Peter.


On 27/01/2017 12:35 AM, Gregg Wonderly wrote:
> Is there any thought here about how a single client might use both an OSGi deployed service
and a conventionally deployed service?

Not yet, I'm currently considering how to support OSGi by implementing 
an RMIClassLoaderSPI, similar to how Dennis has for Maven in Rio.

I think once a good understanding of OSGi has developed, we can consider 
how an implementation could support that, possibly by exploiting 
something like Pax URL built into PreferredClassProvider.


>   The ContextClassLoader is a good abstraction mechanism for finding “the” approriate
class loader.  It allows applications to deploy a composite class loader in some form that
would be able to resolve classes from many sources and even provide things like preferred
classes.

Yes, it works well for conventional frameworks and is utilised by 
PreferredClassProvider, but it's use in OSGi is discouraged, I'd like to 
consider how it's use can be avoided in an OSGi env.

>
>
> In a Java desktop application, would a transition from a background thread, interacting
with a service to get an object from a service which is not completely resolved to applicable
loaders still resolve correctly in an EventDispatch Thread?  That event dispatch thread can
have the context class loader set on it by the thread which got the object, to be the class
loader of the service object, to make sure that the resolution of classes happens with the
correct class loader such that there will not be a problem with the service object having
one set of definitions and another service or the application classpath having a conflicting
class definition by the same name.
>
> I’ve had to spend quite a bit of time to make sure that these scenarios work correctly
in my Swing applications.

Have you got more information?  I'm guessing this relates to delayed 
unmarshalling into the EventDispatch thread.

It's early days yet, I'm still working it out what information is 
required to resolve the correct ClassLoaders & bundles, but this is an 
important question, Bharath mentioned Entry's can be utilised for 
versioning and this seems like a good idea.

What follows are thoughts and observations.

A bundle can be created from a URL, provided the codebase the URL refers 
to has an OSGi bundle manifest, so this could allow any of the existing 
URL formats to deliver a proxy codebase for an OSGi framework.  When 
OSGi loads the bundle, the package dependencies will be wired up by the 
local env.  If the URL doesn't reference a bundle, then we could use 
Bharath's approach and subclass the client's ClassLoader, this does make 
all the clients classes visible to the proxy however, but that would 
happen anyway in a standard Jini / River environment.

OSGi gives preference to already loaded bundles when resolving package 
dependencies, so the client should be careful not to unmarshall any 
proxy's that might require a higher version than the bundle already 
installed when the client bundle resolved its dependencies.

One of the proxy bundle dependencies will be the service api bundle.  
The proxy bundle can limit the service api package / packages to a 
specific version or version range, which it could advertise in an 
Entry.  Boolean logic comparison of Entry's would have to be performed 
locally, after matching on the service type (this requires delayed 
unmarshalling to prevent the resolution of unwanted versions).

So, the OSGi bundles installed in remote JVM's communicating with each 
other, may not be exactly the same version, but should be compatible.  
Multiple versions of a package or bundle may be available in a JVM, so 
we need to ensure we've selected one imported by the proxy.

We could define a simple rule, that the first URL in an annotation, must 
be the proxy bundle, with all dependencies to be provisioned by the OSGi 
Framework, which would allow the same codebase annotation to be utilised 
in a non OSGi environment, but this will probably mean the loss of any 
trailing URL annotations if the proxy's remarshalled.

Once the proxy bundle has been loaded, its ClassLoader needs to be the 
default for the remaining stream classes (smart proxy fields), since it 
knows the most about what dependencies are required, these will be 
visible to the proxy's ClassLoader.

If a class doesn't have an existing ClassLoader referenced by 
annotatation and cannot be resolved from the proxy's Bundle, it probably 
requres a new bundle to be resolved.

What's next?  Determine what annotations to retrieve from OSGi Bundles.

How PreferredClassProvider currently obtains codebase annotations 
(Trivia: the application/system ClassLoader is a URLClassLoader up to 
Java 8, but is no longer an instance of URLClassLoader in Java 9):

Summary, If ClassLoader:

   1. Is local loader? return java.rmi.server.codebase property or null
      if not defined.
   2. Is instance of ClassAnnoation, get annotation and return.
   3. Is instance of URLClassLoader, get URL's as string and return.
   4. else return java.rmi.server.codebase property or null if not defined.


/**
      * Returns the annotation string for the specified class loader
      * (possibly null).  If check is true and the annotation would be
      * determined from an invocation of URLClassLoader.getURLs() on
      * the loader, only return the true annotation if the current
      * security context has permission to connect to all of the URLs.
      **/
     private String getLoaderAnnotation(ClassLoader loader, boolean check) {

     if (isLocalLoader(loader)) {
         return getClassAnnotation(loader);
     }

         /*
      * Get the codebase URL path for the class loader, if it supports
      * such a notion (i.e., if it is a URLClassLoader or subclass).
      */
     String annotation = null;
     if (loader instanceof ClassAnnotation) {
         /*
          * If the class loader is one of our RMI class loaders, we have
          * already computed the class annotation string, and no
          * permissions are required to know the URLs.
          */
         annotation = ((ClassAnnotation) loader).getClassAnnotation();

     } else if (loader instanceof java.net.URLClassLoader) {
         try {
         URL[] urls = ((java.net.URLClassLoader) loader).getURLs();
         if (urls != null) {
             if (check) {
             SecurityManager sm = System.getSecurityManager();
             if (sm != null) {
                 Permissions perms = new Permissions();
                 for (int i = 0; i < urls.length; i++) {
                 Permission p =
                     urls[i].openConnection().getPermission();
                 if (p != null) {
                     if (!perms.implies(p)) {
                     sm.checkPermission(p);
                     perms.add(p);
                     }
                 }
                 }
             }
             }
             annotation = PreferredClassLoader.urlsToPath(urls);
         }
         } catch (SecurityException e) {
         /*
          * If access was denied to the knowledge of the class
          * loader's URLs, fall back to the default behavior.
          */
         } catch (IOException e) {
         /*
          * This shouldn't happen, although it is declared to be
          * thrown by openConnection() and getPermission().  If it
          * does happen, forget about this class loader's URLs and
          * fall back to the default behavior.
          */
         }
     }

     if (annotation != null) {
         return annotation;
     } else {
         return getClassAnnotation(loader);
     }
     }

/**
      * Returns the annotation string for the specified class loader.
      *
      * <p>This method is invoked in order to determine the annotation
      * string for the system class loader, an ancestor of the system
      * class loader, any class loader that is not an instance of
      * {@link ClassAnnotation} or {@link URLClassLoader}, or (for an
      * invocation of {@link #getClassAnnotation(Class)
      * getClassAnnotation(Class)}) a <code>URLClassLoader</code> for
      * which the current security context does not have the
      * permissions necessary to connect to all of its URLs.
      *
      * <p><code>PreferredClassProvider</code> implements this method
      * as follows:
      *
      * <p>This method returns the value of the system property
      * <code>"java.rmi.server.codebase"</code> (or possibly an earlier
      * cached value).
      *
      * @param loader the class loader to obtain the annotation string
      * for
      *
      * @return the annotation string for the class loader, or
      * <code>null</code>
      **/
     protected String getClassAnnotation(ClassLoader loader) {
     checkInitialized();
     return codebaseProperty;
     }


> Gregg
>
>> On Jan 20, 2017, at 6:08 PM, Peter<jini@zeus.net.au>  wrote:
>>
>> Looking at your modifications to ServiceDiscoveryManager, I noticed you've made changes
to set the context ClassLoader prior to calling the lookup service.
>>
>> This is eventually utilised by PreferredClassProvider to lookup the necessary loader
to utilise for deserialization of the lookup results.
>>
>> I think if we develop a RMIClassLoader provider for OSGi, we can avoid utilising
the context ClassLoader.
>>
>> Since all OSGi ClassLoader's are instances of BundleReference, it's easy to utilise
OSGi bundle url anotations (I think this needs to incorporate bundle versions).  I'd also
like to utilise Java 9 jrt style URL's.
>>
>> Cheers,
>>
>> Peter.
>>
>> On 20/01/2017 11:09 PM, Bharath Kumar wrote:
>>> Thanks Peter for the review.
>>>
>>> While creating this POC, I tried to make RIO framework as set of OSGI.
>>> bundles. Rio project extends LookupDiscoveryManager class in one of the
>>> class .org.rioproject.impl.client.DiscoveryManagementPool.SharedDiscoveryManager.
>>> That's why I removed the final modifier.
>>>
>>>
>>> Regarding groovy files,
>>> I have made the org.apache.river as system fragment bundle. So we can't
>>> import any packages from other bundles. But we can use system bundle's
>>> packages,. That's why i removed groovy files. If we use these groovy files,
>>> we need to import packages from groovy bundle which is not possible here. I
>>> will check JGDMS to see how it is used.
>>>
>>>
>>> Thanks&   Regards,
>>> Bharath
>>>
>>>
>>> On Fri, Jan 20, 2017 at 6:09 PM, Peter<jini@zeus.net.au>   wrote:
>>>
>>>> Hi Bharath,
>>>>
>>>> Re your changes (I've found so far):
>>>>
>>>> LookupDiscoveryManager is non final, I'm interested why?
>>>>
>>>> BasicInvocationDispatcher, you've set the context class loader around a
>>>> block of code, to use the ClassLoader passed in during construction.  I'm
>>>> currently investigating addong methods where ClassLoader can be passed in
>>>> for OSGi.
>>>>
>>>> Regarding bundle structure, I've restructured the layout here (so you
>>>> don't need to delete Groovy config):
>>>>
>>>> https://github.com/pfirmstone/JGDMS/tree/Maven_build/modularize/JGDMS
>>>>
>>>> The full commit history has been retained, so u can see all changes.
>>>>
>>>> Cheers,
>>>>
>>>> Peter.
>>>>
>>>> Sent from my Samsung device.
>>>>
>>>>   Include original message
>>>> ---- Original message ----
>>>> From: Bharath Kumar<bharathkumar.a@gmail.com>
>>>> Sent: 20/01/2017 09:42:38 pm
>>>> To: dev@river.apache.org
>>>> Subject: Re: OSGi
>>>>
>>>> Hello all,
>>>>
>>>> I have also added a package in org.apache.river bundle to create the river
>>>> service in osgi environment ( Here RIver
>>>> uses NonActivatableServiceDescriptor).
>>>>
>>>> package name is  org.apache.river.start.ext
>>>>
>>>>
>>>> As river bundle is system fragment, i have to remove
>>>> the groovy dependency.
>>>> So i removed groovy files.
>>>>
>>>> net.jini.config.Component.groovy
>>>>
>>>> net.jini.config.GroovyConfig.groovy
>>>>
>>>>
>>>>
>>>> Thanks&   Regards,
>>>>
>>>> Bharath
>>>>
>>>>
>>>>


Mime
View raw message