Hi Walter,

Not sure, but PackageAdmin.getBundle(Class clazz) (of the Package Admin
Service) comes to my mind ... I am not sure, though, how this would
react to your AOP stuff, you are also referring to.

Regards
Felix

Am Montag, den 21.01.2008, 12:29 +0100 schrieb Wouter de Vaal:
> Hi,
> 
> The following mail is about an issue we have and we have already
> discussed this with Peter Kriens on the JavaPolis
> in december and he suggested posted this on the osgi-dev, it is a bit
> belated, but here goes. 
> 
> To sum it all up, we did not find a way to get the bundle information
> for an arbitray class. The long version, see below.
> 
> We are using a project that uses wicket as a front end and we have our
> components separated using bundles. The 
> classes that comprise the wicket components are not know forehand, but
> are configured by user action and the
> classnames are stored in a database, together with their bundle
> symbolic name and version (there can be multiple 
> versions of the same bundle running in 1 environment, so versioning is
> crucial). Our problem is that wicket
> serializes these objects using ObjectOutputStream and then reloads
> them at a later date. The problem is that 
> these classes are not wired, but dynamically loaded using exact
> bundle/version matching, which ObjectInput/OutputStream
> has no notion of. What we could do is plug in our own subclasses of
> ObjectInput/OutputStream. What we are 
> doing is writing extra version/bundle information into the byte stream
> and reading it back in. For reading
> in the class we can just iterate through the bundles until we have a
> match, however finding the bundle
> and version on an arbitrary class, now that's a whole different
> story. 
> 
> There is a lot of information here, so I hope you bear with me.
> 
> The outputstream class just writes out a flag that we have a versioned
> class and after that the information.
> public class MultiVersionObjectOutputStream extends ObjectOutputStream
> { 
> 
>     private final IClassVersionStringProvider
> classVersionStringProvider;
> 
>     public MultiVersionObjectOutputStream(final OutputStream out,
> final IClassVersionStringProvider classVersionStringProvider) throws
> IOException { 
>         super(out);
>         this.classVersionStringProvider = classVersionStringProvider;
>     }
> 
>     @Override
>     protected void writeClassDescriptor(final ObjectStreamClass desc)
> throws IOException {
>         final String versionString =
> classVersionStringProvider.getClassVersionString(desc.forClass());
>         writeBoolean(versionString != null);
>         if (versionString != null) {
>             writeUTF(versionString); 
>         }
>         super.writeClassDescriptor(desc);
>     }
> 
> }
> 
> The information is delivered by this interface
> public interface IClassVersionStringProvider {
> 
>     String getClassVersionString(Class<?> clazz); 
>     
> }
> 
> Now here is the problem, how do we get the specific bundle for a
> class? First thoughts, just get skip
> through the bundles and do a reference check on the class object.
> 
> Our specific implementation looks like this 
>     public String getClassVersionString(Class<?> clazz) {
>         // locate correct bundle, reads our database 
>         Bundle foundBundle = Activator.getBundleForClass(clazz);
>         String symName = foundBundle.getSymbolicName();
>         String version = (String)
> foundBundle.getHeaders().get(Constants.BUNDLE_VERSION);
>         return symName + "%" + version;
>     }
> 
> in activator:
>     
>     public static Bundle getBundleForClass(Class clazz) { 
>         // find bundle
>         Bundle[] bundles = instance.context.getBundles();
>         // locate correct bundle
>         for (Bundle bundle : bundles) {
>             try {
>                 Class clazz2 = bundle.loadClass(clazz.getName());
>                 if (clazz2 == clazz){
>                     return bundle;
>                 }
>             } catch (ClassNotFoundException e) {
>                 continue;
>             } 
>         }
>         return null;
>     }
> 
> Problem is, this doesn't work for us. In our specific case, we get two
> hits, one of the real bundle and one
> on the spring-aop bundle, so other bundles that dynamically load
> classes and incidentally have loaded 
> our class, also match the criteria, but the information got from that
> bundle is no good, as when loading
> the class from that bundle, which it does so dynamically, will give
> the class from the bundle with the
> highest version number, but we need exact version matching. 
> 
> Up until know we did not find an good solution for this, however we
> managed to hack an equinox specific 
> solution to our problem:
>     @Override
>     public String getClassVersionString(Class<?> clazz) { 
>         // locate correct bundle
>         ClassLoader cl = clazz.getClassLoader();
>         if (cl != null &&
> cl.getClass().getName().equals("org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader"))
>  { 
>             Bundle foundBundle = null;
>             try {
>                 Method m =
> cl.getClass().getDeclaredMethod("getDelegate");
>                 Object classLoaderDelegate = m.invoke(cl);
>                 Method m2 =
> classLoaderDelegate.getClass().getDeclaredMethod("getBundle");
>                 m2.setAccessible(true);
>                 foundBundle = (Bundle) m2.invoke(classLoaderDelegate);
>             } catch (Exception e) { 
>                 throw new IllegalArgumentException(e);
>             }
>             String symName = foundBundle.getSymbolicName();
>             String version = (String)
> foundBundle.getHeaders().get(Constants.BUNDLE_VERSION );
>             return symName + "%" + version;
>         }
>         return null;
>     }
> 
> <sarcasm>nice eh?</sarcasm>. What we really would like to see and
> perhaps this is a good addition for the r5 specs, is 
> a generic way to get bundle information when you only have a Class
> object.
> 
> 
> For completeness, here follows the input part of the process.
> 
> public class MultiVersionObjectInputStream extends ObjectInputStream
> { 
> 
>     private final IMultiVersionClassResolver classResolver;
>     
>     private String versionString;
>     
>     public MultiVersionObjectInputStream(final InputStream in, final
> IMultiVersionClassResolver classResolver) throws IOException { 
>         super(in);
>         this.classResolver = classResolver;
>     }
> 
>     @Override
>     protected ObjectStreamClass readClassDescriptor() throws
> IOException, ClassNotFoundException {
>         final boolean hasVersionString = readBoolean(); 
>         versionString = hasVersionString ? readUTF() : null;
>         return super.readClassDescriptor();
>     }
> 
>     @Override
>     protected Class<?> resolveClass(final ObjectStreamClass desc)
> throws IOException, ClassNotFoundException { 
>         if (versionString != null) {
>             return classResolver.resolveClass(desc.getName(),
> versionString);
>         }
>         return super.resolveClass(desc);
>     }
> 
> }
> 
> public interface IMultiVersionClassResolver { 
> 
>     Class<?> resolveClass(String className, String versionString)
> throws ClassNotFoundException;
>     
> }
> 
> and our implementation
> public class LayoutBundleClassResolver implements
> IMultiVersionClassResolver{ 
> 
>     @Override
>     public Class<?> resolveClass(final String className, final String
> versionString) throws ClassNotFoundException {
>         int sepIndex = versionString.indexOf('%');
>         String bundleSymname = versionString.substring(0, sepIndex);
>         String bundleVersion = versionString.substring(sepIndex + 1);
>         Class<?> clazz = Activator.loadClass(className, bundleSymname,
> bundleVersion);
>         if (clazz == null) { 
>             throw new ClassNotFoundException("Could not find bundle
> for bundle symbolic name " + bundleSymname + " and version "
>                     + bundleVersion + " for class " + className); 
>         }
>         return clazz;
> 
>     }
> 
> }
> 
> and in activator (here we use a utility class from Spring DM):
>     public static Class<?> loadClass(String className, String
> bundleSymbolicName, String version) throws ClassNotFoundException{ 
>         // find bundle
>         Bundle[] bundles = instance.context.getBundles();
>         // locate correct bundle
>         for (Bundle bundle : bundles) {
>             String symName = foundBundle.getSymbolicName ();
>             String foundVersion = (String)
> bundle.getHeaders().get(Constants.BUNDLE_VERSION);
> 
>             if (symName.startsWith(bundleSymbolicName) &&
> version.equals(foundVersion)) {
>                 // got the bundle, load the class 
>                 BundleDelegatingClassLoader cl =
> BundleDelegatingClassLoader.createBundleClassLoaderFor(bundle);
>                 return cl.loadClass(className);
>             }
>         }
>         return null; 
>     }
> 
> Regards,
> Wouter de Vaal
> _______________________________________________
> OSGi Developer Mail List
> [email protected]
> http://www2.osgi.org/mailman/listinfo/osgi-dev

_______________________________________________
OSGi Developer Mail List
[email protected]
http://www2.osgi.org/mailman/listinfo/osgi-dev

Reply via email to