Loading a plugin programmatically [message #903884] |
Mon, 27 August 2012 06:31 |
|
I have been searching the Internet and this forum on this. I read a bunch of articles and nothing appears to be helping. Please don't mind me if this question has already been asked.
What I am trying to do is to programmatically load a plugin within my RCP.
My scenario includes the following
- dc_buddypolicy_plugin1 is a headless plugin that provides a static class for logging to standard out
- dc_buddypolicy_plugin3 is my RCP and it tries to load plugin1 programmatically.
Below is a code snippet in dc_buddypolicy_plugin3::Activator::start() method.
public void start(BundleContext context) throws Exception {
super.start(context);
plugin = this;
System.out.println(PLUGIN_ID + ":Activator::start invoked");
try {
System.out.println(PLUGIN_ID + ":Activator::start installing bundle");
Bundle b = context.installBundle("file:///home/beyonddc/workspace/deployed_plugin/plugins/dc_buddypolicy_plugin1_1.0.0.jar");
System.out.println(PLUGIN_ID + ":Activator::start installed bundle");
b.start();
System.out.println("Invoking Class.forName()");
Class lc = Class.forName("dc_buddypolicy_plugin1.Logger");
} catch (Throwable th) {
th.printStackTrace();
}
}
My dc_buddypolicy_plugin1 appears to be loaded just fine according to the log in standard out except that I couldn't load my class via Class.forName().
dc_buddypolicy_plugin3:Activator::start invoked
dc_buddypolicy_plugin3:Activator::start installing bundle
dc_buddypolicy_plugin3:Activator::start installed bundle
dc_buddypolicy_plugin3:Activator::start dc_buddypolicy_plugin1 INSTALLED
dc_buddypolicy_plugin1:Activator - start - invoked [08272012]
dc_buddypolicy_plugin1::logger - dc_buddypolicy_plugin1:Activator::start invoked [08272012]
Invoking Class.forName()
java.lang.ClassNotFoundException: dc_buddypolicy_plugin1.Logger
I also setup buddy policy
dc_buddypolicy_plugin3 has Eclipse-BuddyPolicy: registered
dc_buddypolicy_plugin1 has Eclipse-RegisterBuddy: dc_buddypolicy_plugin3
Can someone please kindly explain what I did wrong? Thanks!
|
|
|
|
Re: Loading a plugin programmatically [message #903964 is a reply to message #903944] |
Mon, 27 August 2012 12:59 |
|
Hi Thorsten,
Here's my manifest
<!-- dc_buddypolicy_plugin1 -->
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Buddy Policy Plugin 1
Bundle-SymbolicName: dc_buddypolicy_plugin1; singleton:=true
Bundle-Version: 1.0.0
Bundle-Activator: dc_buddypolicy_plugin1.Activator
Bundle-Vendor: David Chu
Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-ActivationPolicy: lazy
Export-Package: dc_buddypolicy_plugin1
Eclipse-RegisterBuddy: dc_buddypolicy_plugin3
<!-- dc_buddypolicy_plugin3 -->
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Buddy Policy Plugin3
Bundle-SymbolicName: dc_buddypolicy_plugin3; singleton:=true
Bundle-Version: 1.0.0
Bundle-Activator: dc_buddypolicy_plugin3.Activator
Bundle-Vendor: David Chu
Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-ActivationPolicy: lazy
Export-Package: dc_buddypolicy_plugin3
Eclipse-BuddyPolicy: registered
I added the following code as you suggested in my dc_buddypolicy_plugin3::Activator::start() method but I am still getting a ClassNotFoundException...
Classname is dc_buddypolicy_plugin1.Logger
java.lang.NoClassDefFoundError: dc_buddypolicy_plugin1/Logger
at dc_buddypolicy_plugin3.Activator.start(Activator.java:70)
b.start();
Class<dc_buddypolicy_plugin1.Logger> logclass = b.loadClass("dc_buddypolicy_plugin1.Logger");
System.out.println("Classname is " + logclass.getName());
dc_buddypolicy_plugin1.Logger.logMsg(PLUGIN_ID + ":Activator", "start", "invoked")
I am going to look into more. Any recommendation would be appreciated. Thanks!
[Updated on: Mon, 27 August 2012 13:00] Report message to a moderator
|
|
|
|
Re: Loading a plugin programmatically [message #903981 is a reply to message #903973] |
Mon, 27 August 2012 13:44 |
|
I added "Bundle-ClassPath: .,/home/beyonddc/workspace/deployed_plugin/plugins/dc_buddypolicy_plugin1_1.0.0.jar" but I am still getting the NoClassDefFoundError exception.
The thing is before adding "Bundle-ClassPath" into dc_buddypolicy_plugin3, I was already able to load the Logger class by following your recommendation. I also verify that the class did loaded by invoking the getName() method. See code below...
Class<dc_buddypolicy_plugin1.Logger> logclass = b.loadClass("dc_buddypolicy_plugin1.Logger");
"System.out.println("Classname is " + logclass.getName());"
I thought that piece of code will load the class into the ClassLoader. If the class did get loaded then why I couldn't use it and still getting a NoClassDefFoundError exception?
Class<dc_buddypolicy_plugin1.Logger> logclass = b.loadClass("dc_buddypolicy_plugin1.Logger");
System.out.println("Classname is " + logclass.getName());
dc_buddypolicy_plugin1.Logger.logMsg(PLUGIN_ID + ":Activator", "start", "invoked");
[Updated on: Mon, 27 August 2012 13:45] Report message to a moderator
|
|
|
Re: Loading a plugin programmatically [message #903984 is a reply to message #903981] |
Mon, 27 August 2012 13:57 |
|
I changed the code to use reflection instead and then now I can invoke on the logger successfully. Why's that?
Class<dc_buddypolicy_plugin1.Logger> logclass = b.loadClass("dc_buddypolicy_plugin1.Logger");
System.out.println("Classname is " + logclass.getName());
Class[] argTypes = new Class[] { String.class, String.class, String.class };
java.lang.reflect.Method logMsg = logclass.getDeclaredMethod("logMsg", argTypes);
String[] msgArgs = new String[] { "test1", "test2", "test3" };
logMsg.invoke(null, msgArgs);
Just an additional note, I do need to include the following in my dc_buddypolicy_plugin3 manifest if I need to get it to run with reflection after I exported my Eclipse RCP application.
Bundle-ClassPath: .,/home/beyonddc/workspace/deployed_plugin/plugins/dc_buddypolicy_plugin1_1.0.0.jar
[Updated on: Mon, 27 August 2012 14:17] Report message to a moderator
|
|
|
|
Re: Loading a plugin programmatically [message #904233 is a reply to message #904117] |
Tue, 28 August 2012 03:43 |
|
Thorsten Schlathölter wrote on Mon, 27 August 2012 15:58Sorry but there is a little missunderstanding. You should certainly not add /home/beyonddc/workspace/deployed_plugin/plugins/dc_buddypolicy_plugin1_1.0.0.jarto the classpath of plugin3. First of all it is an absolute path which has nothing to do in a manifest. What if you decide to give the plugin to someone else? And then if you add it to plugin3 what is the need of plugin1 anyway?
That makes sense. I was just trying different combination b/c I couldn't get it working.
Thorsten Schlathölter wrote on Mon, 27 August 2012 15:58
You need to add the Bundle-ClassPath to the manifest if the classes are located in a jar. So for example if your logger is packed in a jar logger.jar and you place the jar into a lib folder located in your plugin, then you should add logger.jar to the classpath. In that case the classpath would be:
Bundle-ClassPath: ., lib/logger.jar
Where lib is a folder located in your plugin1.
If on the other hand your class is located in a folder dc_buddypolicy_plugin1/Logger.class and the folder itself is located in the root of dc_buddypolicy_plugin1_1.0.0.jar, then
Bundle-ClassPath: .
Is sufficient. I am not sure whether Bundle-ClassPath can be neglected in that case. At least it does not harm.
You definetly have to remove /home/beyonddc/workspace/deployed_plugin/plugins/dc_buddypolicy_plugin1_1.0.0.jar from the classpath of plugin3!!!
May I ask why you want to use the buddy policy to resolve the class from plugin1? As far as I know the buddy policy was introduced to overcome class loading issues when bundles cannot make use of the normal OSGI delegation model. Since you have a simple OSGI bundle with one class, why don't you just import the package or the bundle into plugin3? And what is the use of loading the plugin programmatically? Why don't you just add it to your configuration? Is this just a proof of concept?
Yup! It is just a proof of concept. I have an assignment that I need to generate some code from a template and then package it into an Eclipse plugin. Because the name of the package is unknown until the Eclipse plugin is generated so I want to provide a button in my RCP that will take a file location as an import and then load the plugin via the installBundle() method. After the bundle gets loaded then I would like to use it.
I thought this should be achievable because Eclipse IDE allows you to download and install a plugin without restarting the Eclipse IDE itself.
Thorsten Schlathölter wrote on Mon, 27 August 2012 15:58
Regarding your reflection question: Why would you load the class other than to use it for reflection? If you were already able to load the class before modifying the Bundle-ClassPath then go back to that point and use the class with reflection.
Regards,
Thorsten
I don't really want to use reflection. I was hoping that after installBundle() is executed then I can just use my Logger class directly and log message.
The following code is really what I want to achieve but I couldn't get it working.
public void start(BundleContext context) throws Exception {
super.start(context);
plugin = this;
System.out.println(PLUGIN_ID + ":Activator::start invoked");
try {
System.out.println(PLUGIN_ID + ":Activator::start installing bundle");
Bundle b = context.installBundle("reference:file:///home/beyonddc/workspace/exported_plugins/plugins/dc_buddypolicy_plugin1_1.0.0.jar");
System.out.println(PLUGIN_ID + ":Activator::start installed bundle");
b.start();
dc_buddypolicy_plugin1.Logger.logMsg(PLUGIN_ID + ":Activator", "start", "invoked");
} catch (Throwable th) {
th.printStackTrace();
}
In short, I just want to load a plugin via the installBundle() method and then use the Logger class from dc_buddypolicy_plugin1 to log a message. It's just that no matter how I try this I kept getting error.
Thorsten, thanks for your help!
|
|
|
Re: Loading a plugin programmatically [message #905143 is a reply to message #904233] |
Wed, 29 August 2012 19:40 |
|
recap: You want to install bundle1 at runtime from bundle3 and then use it from bundle3.
bundle3 can't see your bundle1 classes by default. You'd either have to require the bundle bundle1 or import the packages from bundle1 into bundle3. But then bundle1 would have to be there at compile time for sure, and (even if optional) you would not be able to load a class that references anything from bundle3 (until after it had been installed and PackageAdmin or Wiring had been updated).
The OSGi pattern to do this would be to provide a service interface (something that will allow you to start using the bundle1 functionality). Then you have bundle1 provide the service (preferably through Declarative Services) and bundle3 would consume the service (use a ServiceTracker, for example).
Then when bundle1 is installed the service it provides (a logger?) would become available to bundle3.
PW
Paul Webster
http://wiki.eclipse.org/Platform_Command_Framework
http://wiki.eclipse.org/Command_Core_Expressions
http://wiki.eclipse.org/Menu_Contributions
|
|
|
Re: Loading a plugin programmatically [message #905780 is a reply to message #905143] |
Fri, 31 August 2012 04:17 |
|
Paul Webster wrote on Wed, 29 August 2012 15:40recap: You want to install bundle1 at runtime from bundle3 and then use it from bundle3.
bundle3 can't see your bundle1 classes by default. You'd either have to require the bundle bundle1 or import the packages from bundle1 into bundle3. But then bundle1 would have to be there at compile time for sure, and (even if optional) you would not be able to load a class that references anything from bundle3 (until after it had been installed and PackageAdmin or Wiring had been updated).
The OSGi pattern to do this would be to provide a service interface (something that will allow you to start using the bundle1 functionality). Then you have bundle1 provide the service (preferably through Declarative Services) and bundle3 would consume the service (use a ServiceTracker, for example).
Then when bundle1 is installed the service it provides (a logger?) would become available to bundle3.
PW
Hi Paul, thank you for your tip. I will give it a try!
|
|
|
Powered by
FUDForum. Page generated in 0.03365 seconds