0

I have an OSGi application. Working with EJB context.lookup I had to set Thread context class loader as bundle class loader in order to be able to cast. Like this:

Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
Entity entity=bean.getOne();
System.out.println(entity.getClass().getClassLoader());

output is

org.apache.felix.framework.BundleWiringImpl@7468776f

This code works. The problem that I can't cast if I have ArrayList

Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
ArrayList<Entity> entities=bean.getMany();

This code returns ClassCastException.

Checking

ArrayList<Entity> temp=new ArrayList<Entity>();
System.out.println(temp.getClass().getClassLoader());

returns NULL - it means bootstrap class. How can it be fixed?

EDIT:

The most interesting, that ArrayList with String works, classic array works, but ArrayList and ArrayList with Entiry don't work.

Class Bean {
....
  @Override //THIS DOESN'T WORK
    public ArrayList<Entity> readMany() {
        Entity dir1=new Entity();
        dir1.setContent("1 test");
        Entity dir2=new Entity();
        dir2.setContent("2 test");
        ArrayList<Entity> result=new ArrayList<>();
        result.add(dir1);result.add(dir2);
        return result;
    }

    @Override //THIS WORKS
    public ArrayList<String> readMany2() {
        String str1=new String("1 test");
        String str2=new String("2 test");
        ArrayList<String> result=new ArrayList<>();
        result.add(str1);
        result.add(str2);
        return result; 
    }

@Override //THIS WORKS
public Entity[] readArray() {
    ArrayList<Entity> al=readMany();
    Entity[] ar=new Entity[al.size()];
    for (int i = 0; i < al.size(); i++) {
        ar[i]=al.get(i);
    }
    return ar;
}

@Override //THIS DOESN'T WORK
public ArrayList readSimpleArrayList() {
    ArrayList<Entity> gal=readMany();
    ArrayList al= new ArrayList();
    for (Entity obj : gal) {
        al.add(obj);
    }
    return al;
}
...
}

Here is the log

java.lang.ClassCastException: com.test.cmn.shd.base.dir.language.LanguageDirEntity cannot be cast to com.test.cmn.shd.base.dir.language.LanguageDirEntity at com.test.cmn.dt.base.Activator.start(Activator.java:83) at org.apache.felix.framework.util.SecureAction.startActivator(SecureAction.java:645) at org.apache.felix.framework.Felix.activateBundle(Felix.java:1977) at org.apache.felix.framework.Felix.startBundle(Felix.java:1895) at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:944) at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:931) at com.test.cmn.dt.loader.LoaderModel.startCoreModule(LoaderModel.java:149) at com.test.cmn.dt.loader.LoaderModel.access$100(LoaderModel.java:39) at com.test.cmn.dt.loader.LoaderModel$InstallAndStartModuleWorker.doInBackground(LoaderModel.java:79) at com.test.cmn.dt.loader.LoaderModel$InstallAndStartModuleWorker.doInBackground(LoaderModel.java:73) at javax.swing.SwingWorker$1.call(SwingWorker.java:296) at java.util.concurrent.FutureTask.run(FutureTask.java:262) at javax.swing.SwingWorker.run(SwingWorker.java:335) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:744)

EDIT 2 - FULL CODE. This code is executed on client computer. JavaEE (GF4) is running on server. There are three osgi bundles:for server,for client and shared. The copy of shared is both on server and on clients and contains LanguageDirBeanRemote and LanguageDirEntity.

ClassLoader thatLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
try {
       Properties jndiProps = new Properties();
       jndiProps.put("java.naming.factory.initial", "com.sun.enterprise.naming.impl.SerialInitContextFactory");
       jndiProps.put("java.naming.factory.url.pkgs", "com.sun.enterprise.naming");
       jndiProps.put("java.naming.factory.state", "com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");
       jndiProps.setProperty("org.omg.CORBA.ORBInitialHost", "x.x.x.x");
       jndiProps.setProperty("org.omg.CORBA.ORBInitialPort", "3700");
       InitialContext ctx = new InitialContext(jndiProps);
       LanguageDirBeanRemote bean=(LanguageDirBeanRemote)ctx.lookup("java:global/...");
       ArrayList<LanguageDirEntity> elements=bean.readDirectory();
      System.out.println("HERE I GET THE ERROR:"+elements.get(0).getContent());
} finally {
Thread.currentThread().setContextClassLoader(thatLoader);
}

EDIT 3 I opened a bug

Community
  • 1
  • 1
  • The actual class cast exception stack trace would be helpful. – BJ Hargrave Apr 19 '14 at 20:30
  • @BJ Hargrave Hello, BJ Hargrave. Thank you for your professional attention. I edited the question. –  Apr 19 '14 at 20:41
  • @BJ Hargrave I added some more information. –  Apr 19 '14 at 21:35
  • Can you check if your bundle containing the client code you posted above embeds the package com.test.cmn.shd.base.dir.language? You should also check the other bundles if they contain or export this package. Make sure you have no two bundles with that package. – Christian Schneider Apr 23 '14 at 06:59
  • @Christian Schneider It contains all package and there is no duplicate because LanguageDirEntity[] works without problem but ArrayList doesn't. –  Apr 23 '14 at 07:38

3 Answers3

3

"X cannot be cast to X", in a classloader/OSGi context, generally means that you have two classloaders present in your system, both of which have loaded the class X, and that you're trying to pass an instance created using one classloader's copy of the class to code expecting the other classloader's copy.

Fixes are either to make sure that only one instance of that class exists in any classloader and is shared by all code (simplest), or to make sure that instances never cross the classloader context boundaries, or (more fragile) to make sure that everyone is finding EXACTLY the same implementation of that class (same exact bytecodes for the class and everything it inherits).

keshlam
  • 7,681
  • 1
  • 15
  • 31
  • Thank you for you time. Look, there two classes. Entity (mine) and ArrayList(standart java). When I use only one Entity everything is ok (this class is loaded by osgi loader). When I use Entity and ArratList(AL is loaded by bootstrap) it doesn't work. –  Apr 20 '14 at 04:08
1

As the other answer has said, the root of your problem is almost certainly that you have multiple copies of LanguageDirEntity loaded by different classloaders. The most likely cause of this is that you've inadvertently packaged the physical class into different bundles. So the first solution is to inspect your bundles for the class. If they're all in the same location, running

grep -r LanguageDirEntity *

is a quick and dirty way of finding them. (Are you using the Maven bundle plugin for your build? It's easy to inadvertently embed dependencies into bundles unless you get your poms right.)

The reason things work when you use a String in an ArrayList is that String will be provided by the system, and there will never be multiple copies of it loaded by different class loaders. The reason you have problems using ArrayList but don't have problems using the Entity classes directly is that the interaction of generics and collections introduces some extra casts which aren't needed in the direct-usage case.

Unpacking exactly what's going on is tricky without knowing what classes are packaged where. I'd guess, from the problems you're seeing, that Bean is not in the same bundle as the Activator. However, the basic idea is that every time the ArrayList is used by code (either to add elements in or read elements out), a cast will be done to cast the contents to 'Entity'. The more casts you do, in the more different bundles, the greater the chance of hitting classes which have been incompatibly loaded from different bundles. In this case, it looks like your Activator saw a different copy of the class than your Bean, so when the Activator attempts the (implicit, added by the compiler) cast, it's incompatible with the contents of the ArrayList.

Community
  • 1
  • 1
Holly Cummins
  • 9,508
  • 3
  • 19
  • 24
  • Thank you for your time. I've read your post with great attention. I checked all bundles for the class. It's ok. The problem is with classloaders, I totally agree. However bean is EJB. And I can't control what it's going on inside it (at least I don't know). I added to my question EDIT 2 FULL CODE. Can you point my mistake? I don't understand something. Somewhere something is wrong. But where? I checked this code hundred times. –  Apr 20 '14 at 18:45
  • Ahh, I see, I suspect in this case the two copies are the client copy and the server copy. In general, java EE technologies and OSGi don't work very well, because of things like Java EE's use of the context classloader. This is exactly why the Enterprise OSGi spec was written (with Apache Aries as an implementation). Aries does have an OpenEJB integration, but I'm not sure if it would help in your case. I think you're in tricky water with what you're trying to do, essentially. – Holly Cummins Apr 21 '14 at 07:06
  • Thank you again. I am not so experienced developer, but the problem I see mustn't seem to be very new, as it's the most common technologies and client-server architecture. Maybe I must use some library or some another approach with ejb to make my application work. Can you advise something? If this variant can't work let's forget about it. My problem is to connect OSGi client and EJB. –  Apr 21 '14 at 07:17
0

Thank you everybody. I've finally solved it. I'm writing how as I understand it. There are two ways to use gf-client with osgi client:

  1. Not official way - take glassfish bundles and manually install them via osgi api.
  2. Official way - copy glassfish/lib/gf-client.jar and glassfish/modules to client and in classpath add gf-clinet.jar.

I don't know how, I did by the first way. I can't call it wrong, because glassfish is osgi itself. When I did it by the second way this problem disappeared. So the problem was in different classloaders when you load from java classpath and from osgi bundle.