Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » Rich Client Platform (RCP) » Loading a plugin programmatically
Loading a plugin programmatically [message #903884] Mon, 27 August 2012 02:31 Go to next message
Chun Tat Chu is currently offline Chun Tat Chu
Messages: 11
Registered: August 2012
Junior Member
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 #903944 is a reply to message #903884] Mon, 27 August 2012 07:38 Go to previous messageGo to next message
Thorsten Schlathölter is currently offline Thorsten Schlathölter
Messages: 208
Registered: February 2012
Location: Düsseldorf
Senior Member
Hi Chun,
I have not used BuddyPolicies yet and you do not provide the required information to solve the issue from a configuration point of view (you would have to post the manifests). But a solution to your problem would be to load the Class via the plugin1 which you already have at hand:

Bundle b = context.installBundle(...);
b.loadClass("dc_buddypolicy_plugin1.Logger")


That should work.

Regards,
Thorsten
Re: Loading a plugin programmatically [message #903964 is a reply to message #903944] Mon, 27 August 2012 08:59 Go to previous messageGo to next message
Chun Tat Chu is currently offline Chun Tat Chu
Messages: 11
Registered: August 2012
Junior Member
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 09:00]

Report message to a moderator

Re: Loading a plugin programmatically [message #903973 is a reply to message #903964] Mon, 27 August 2012 09:19 Go to previous messageGo to next message
Thorsten Schlathölter is currently offline Thorsten Schlathölter
Messages: 208
Registered: February 2012
Location: Düsseldorf
Senior Member
None of the manifests specifies a Bundle-ClassPath. If you run from within eclipse this is usually no problem. But if you load a plugin from external, the plugin itself does not no where to look for the classes.

I don't know how dc_buddypolicy_plugin1_1.0.0.jar is organized but I would recommend to add the following line to your manifest:

Bundle-ClassPath: ., pathToJarThatContainsTheClass/jarThatContainsTheClass.jar


Where "." specifies the root of your jar and jarThatContainsTheClass.jar
optionally specifies a jar where your class is located.

Regards,
Thorsten
Re: Loading a plugin programmatically [message #903981 is a reply to message #903973] Mon, 27 August 2012 09:44 Go to previous messageGo to next message
Chun Tat Chu is currently offline Chun Tat Chu
Messages: 11
Registered: August 2012
Junior Member
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 09:45]

Report message to a moderator

Re: Loading a plugin programmatically [message #903984 is a reply to message #903981] Mon, 27 August 2012 09:57 Go to previous messageGo to next message
Chun Tat Chu is currently offline Chun Tat Chu
Messages: 11
Registered: August 2012
Junior Member
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 10:17]

Report message to a moderator

Re: Loading a plugin programmatically [message #904117 is a reply to message #903984] Mon, 27 August 2012 15:58 Go to previous messageGo to next message
Thorsten Schlathölter is currently offline Thorsten Schlathölter
Messages: 208
Registered: February 2012
Location: Düsseldorf
Senior Member
Sorry 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?

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?

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
Re: Loading a plugin programmatically [message #904233 is a reply to message #904117] Mon, 27 August 2012 23:43 Go to previous messageGo to next message
Chun Tat Chu is currently offline Chun Tat Chu
Messages: 11
Registered: August 2012
Junior Member
Thorsten Schlathölter wrote on Mon, 27 August 2012 15:58
Sorry 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. Mad

Thorsten, thanks for your help!
Re: Loading a plugin programmatically [message #905143 is a reply to message #904233] Wed, 29 August 2012 15:40 Go to previous messageGo to next message
Paul Webster is currently offline Paul Webster
Messages: 6859
Registered: July 2009
Location: Ottawa
Senior Member

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


Re: Loading a plugin programmatically [message #905780 is a reply to message #905143] Fri, 31 August 2012 00:17 Go to previous message
Chun Tat Chu is currently offline Chun Tat Chu
Messages: 11
Registered: August 2012
Junior Member
Paul Webster wrote on Wed, 29 August 2012 15: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


Hi Paul, thank you for your tip. I will give it a try!
Previous Topic:RCP - Debugging which plugin is missing from target platform
Next Topic:Export wizard failing
Goto Forum:
  


Current Time: Fri Aug 29 20:26:37 EDT 2014

Powered by FUDForum. Page generated in 0.02093 seconds