Saturday, October 04, 2008

New version of Spring Extension Factory available

When retrieving an OSGi service its good practice to use a timeout. Otherwise the operation could be blocked forever (in the case the service never appears). In past versions the Spring Extension Factory used a hard-coded timeout of 5 seconds. I chose this value more or less randomly, guessing that no bundle would hopefully need for than 5 seconds to create its application context. But I was proven wrong... :-)

Therefore I made the timeout setting configurable. Now you can define the timeout value (in milliseconds) via a system property called "org.eclipse.springextensionfactory.timeout". To set the timeout to 20sec, for example, start the JVM with this option:

-Dorg.eclipse.springextensionfactory.timeout=20000

I also added some more readable exceptions if no application context can be found or if the name of the bean to be used cannot be determined.

The new version can be downloaded from here:
Thanks for reporting the problem, Matthias!

54 comments:

jawher said...

Kudos (again) Martin ! Keep this cool stuff coming :)

Cheers,
Jawher

Martin Lippert said...

Thanks for the nice feedback!!! :-)

Cheers,
-Martin

Unknown said...

Thanks for this new release ! (and thanks also from my teamate Jimmy, who unfortunately can't tell you this right now)

Martin Lippert said...

Thanks for the feedback, Mr. Magne... :-)

Unknown said...

btw, I was thinking of the possibility to use the getBean() method that takes constructor-args.
Here is a draft...

in getBeanName():
if (data != null && data.toString().length() > 0) {
return (new StringTokenizer(data.toString(), DELIMITER).nextToken());
}


in setInitializationdata():
if (data != null && data.toString().length() > 0)
{
StringTokenizer tokenizer = new StringTokenizer(data.toString(), DELIMITER);
List objects = new ArrayList(tokenizer.countTokens() - 1);
//skip bean name
if(tokenizer.hasMoreTokens())
tokenizer.nextToken();
//retrieve parameters
while (tokenizer.hasMoreTokens())
objects.add(tokenizer.nextToken());
this.bean = appContext.getBean(beanName, objects.toArray());
}
else
{
this.bean = appContext.getBean(beanName);
}


be careful, it's not tested ! What do you think about this kind of feature ?

Martin Lippert said...

Sounds like a nice enhancement. If I understand this idea correctly you would like to define beans in your app context that take string-typed constructor args. And you would like to define the values for those args from the extension definition instead of defining them in the app context, correct?

Unknown said...

Exactly, sorry if my explanation wasn't clear ;)
In my example I add some parameters to the extension definition (separated by a DELIMITER), and pass them to the getBean() method.

Ralf Ebert said...

Hi Martin,

are the sources for the Spring Extension Factory available somewhere?

Ralf

Martin Lippert said...

The source code is included as zip inside the bundles jar.

Ralf Ebert said...

Thanks for the help/providing this really helpful class!

btw, have you seen a good way to make sure the spring dm extender bundle is actually started? I'm a bit averse to using config.ini for RCP applications, because it tends to complicate setup of run configurations and deployments (yet another thing that can break...). So I usually end up starting it programmatically from some bundle Activator. Any better ideas for this?

Martin Lippert said...

Hm, there is other option coming to my mind. I use config.ini in most of my projects and it works fine, but starting it programmatically from an activator is also possible. But then how do you make sure this activators bundle is started? ;-)

Cheers,
-Martin

Martin Lippert said...

Oh, I meant there is NO other option coming to my mind... Sorry...

Ralf Ebert said...

Usually by using the bundle that contains the Application class. It's started automatically because of Bundle-ActivationPolicy... :)

config.ini seems to be the right way, but imo it's messy to use from Eclipse Run Configurations. Did you get that working smoothly or do you start the bundles manually when developing?

Martin Lippert said...

Its not so complicated to use a config.ini for your launch configurations within the IDE. Just list those bundles you would like to start (don't forget the update.configurator or the simpleconfigurator) and select all your bundles you would like to deploy to your runtime via the standard launch configuration. Thats it, no real magic.

Mark Nuttall said...

What are we supposed to be putting in the config.ini?

Martin Lippert said...

The config.ini that I used in one of my examples consists just of a single line:

osgi.bundles=org.eclipse.equinox.common@2:start,org.eclipse.update.configurator@3:start,org.springframework.bundle.osgi.extender@4:start, org.eclipse.core.runtime@start

Could be that your bundle symbolic names are slightly different (I am using an older version of spring dm here), but in general that should do it. You can set this as template in your launch config, the rest will be added by PDE when you start the app.

Please note that this config.ini is not a complete config.ini that could be used for a standalone deployment of the application. For a standalow deployment you should add the other stuff that typically is part of the config.ini

HTH,
-Martin

Eric Jain said...

I've got Spring Extension Factory working great for Views and Editors, but it occasionally fails on startup when using it for Commands/Handlers:

Plug-in "xyz" was unable to execute setInitializationData on an instance of "org.eclipse.springframework.util.SpringExtensionFactory".

java.lang.RuntimeException: application context for bundle xyz not found
at org.eclipse.springframework.util.SpringExtensionFactory.setInitializationData(SpringExtensionFactory.java:68)

Martin Lippert said...

Seems like the application context for the bundle that contains the commands/handlers didn't get initialized in time. Can you check whether Spring is able to create the application context? Or does it wait for a dependent bundle to complete the context creation?

Or is the context creation just not fast enough? In that case you can set the timeout for the spring extension factory to a higher value...

HTH,
Martin

Eric Jain said...

I'll try setting a higher timeout. Strange thing is that I have several almost identical handlers, and just one of them (the one that's listed first in the plugin.xml) fails (once in a while) with the described error...

Eric Jain said...

Just observed that setting org.eclipse.springextensionfactory.timeout to 1 causes all handlers to fail, so increasing the value above the default of 5000 should indeed fix the problem. Thanks!

Martin Lippert said...

Great to hear that you got it working for your setting!!!

-Martin

Eric Jain said...

Here's another strange issue: Regardless of whether I "Require-Bundle" or "Import-Package" org.eclipse.springframework.util, I get the following warning (even though everything now appears to be working just fine): "Referenced class 'org.eclipse.springframework.util.SpringExtensionFactory' in attribute 'class' is not on the plug-in classpath".

Martin Lippert said...

This is indeed strange... Sounds like a PDE issue... What if you press "Update classpath..." (PDE Tools)?

Eric Jain said...

The warnings go away if I remove "Bundle-RequiredExecutionEnvironment: J2SE-1.5" from the Spring Extension Factory manifest (or append ", JavaSE-1.6"). I'm using Java 6, and it seems that "J2SE-1.5" is not interpreted as "at least Java 5", but as "Java 5".

Eric Jain said...

Correction: Looks like this doesn't get rid of the warnings after all; it's just that sometimes after doing a full rebuild that the compiler warnings disappear for a while... Will raise this issue at eclipse.org.

Ralf Ebert said...

Just wrote a little tutorial about Eclipse RCP and Spring OSGi also mentioning your SpringExtensionFactory:
Tutorial: Spring OSGi + Eclipse RCP

Thanks again for the class, using it in almost all my RCP projects

Martin Lippert said...

Hi Ralf!

Thanks for the feedback. Great to hear that the extension factory helps you in your projects and thanks a lot for mentioning it in your nice tutorial! :-)

Cheers,
-Martin

Ralf Ebert said...

btw, for me PDE complained about the classes not available because of the extra jar in the bundle jar. It worked at runtime though. I rebundled it to be a regular binary + separate source bundle. It's part of the spring_osgi "dropin" now.

Eric Jain said...

@Ralf: Thanks! Using the separate binary and source bundles, I no longer see the warnings, either.

Eric Jain said...

Any suggestions how to terminate the application (after showing an error message) if the SpringExtensionFactory fails?

Martin Lippert said...

Hi Eric!

I answered your question to the mailing list.

HTH,
-Martin

Setya said...

Hi,

Is it possible to use SpringExtensionFactory to inject EditorPart instead of ViewPart ?

Cheers,
Setya

Martin Lippert said...

Hi Setya,

you should be able to use the SpringExtensionFactory for nearly any extension definition, it is not specifically built for view parts (was just my example). EditorParts should work as well.

HTH,
Martin

Stefan Hartung said...

Hey,

back to the command-problem. Today we hat this problem at work,too and it seems to me, that the solution with setting up the timeout intervall is not enough.

As i looked through the code of your project my attention goes to the scenario how the factory searches the plugin.xml for possible application contexts. It seems to me, that only extensions with the attribute "id" are captured. Commands have an id-attribute, too but there it is not only "id". Instead, they have a "commandId".

To test this thing i added manually an id-attribute (the plugin.xml didn't like it, but there was no actual error). I started the application again and it worked.

The same problem exists e.g. with the Initializer fpr PreferencePages. There you have to manually add the id-attribute, too and then it works.

I don't know wether it is the right way or not, but it works and that is good ;-).

Stefan Hartung said...

Btw, i forgot to mention that the factory helped us a lot in our project. Thanks for your work :-).

Cheers,

Stefan

Martin Lippert said...

Hi Stefan!

Thanks for the nice feedback. Great to hear that this piece of code helps you.

With regards to your problem: Keep in mind that you can explicitly define the id of the bean to use by the extension factory:

http://martinlippert.blogspot.com/2008/05/dependency-injection-for-extensions.html

Take a look at the ":myview" at the end of the class attribute where you type in the extension factory. This allows you to define exactly which bean to use for this extension, independently from any id attribute in the extension.

HTH,
Martin

Stefan Hartung said...

Hi Martin,

thanks for the advice. I didn't know it yet.

Greetings,

Stefan

Henno Vermeulen said...

Thank you for this tool! I found it through Ralf's blog. For me a time out of 5s is not enough for handlers. I also have the problem that the workbench only shows once the handlers are initiated.

A very cool and useful feature would be if the SpringExtensionFactory is extended (pun intended) so that it does not block, but calls a setter method on the bean (e.g. the handler) once the service this bean needs becomes available.

Then the bean/handler would not need to know how to look up services but can still set for example set its enabled property depending on whether or not the service is already available. This way the workbench can already startup while the context is still being created.

I also have an issue that the progress bar of my splash screen only shows up AFTER all handlers are created, see my post here http://www.eclipse.org/forums/index.php?t=msg&goto=517161&#msg_517161 if you feel like it.

Martin Lippert said...

Hi Henno!

Do I understand this correctly: You define an extension (a handler) via the SpringExtensionFactory, define the real handler as a bean in your spring context and use Spring to inject some dependencies into this bean, right? And what you are asking for some kind of lazy injection, eh?

The SpringExtensionFactory typically does not work around the dependency injection mechanism of Spring. Therefore to me your questions sounds more like something you would/should like to do within the spring context. Isn't this possible in Spring? If you are injecting OSGi services via Spring-DM, this should already be possible by defining the cardinality of your service bean (0..1). Doing it that way you would get your service bean injected into your bean no matter if the real service is available or not. Would that help in your situation?

-Martin

Henno Vermeulen said...

Thank you for your quick reply. Yes, you are right, this is some kind of lazy initialization; setting the service reference some time after the handler is constructed. I did not know of the "service cardinality" (just starting out with Eclips RCP, OSGi and Spring) I will check it out!

After some thought, I also realized it would be nice if we could simply annotate a field with @Resource (or @Autowired) and have Spring-DM set this field when a service comes or goes. I am not sure if this is already possible or not so I created a forum post on Spring-DM.

http://forum.springsource.org/showthread.php?p=286308#post286308

Henno Vermeulen said...

Wow thank you Martin! Using cardinality="0..1" in my <osgi:reference.. tag worked like a charm! I still use SpringExtensionFactory to create the handler from a Spring context, but the injected service now seems to be a lazy-loading proxy and it is not blocked anymore!

Ralf Ebert said...

Another solution that worked quite will for me and has exactly the behaviour that you mentioned ("set a field when a service comes or goes") is to use the Wire mechanism from Riena Core, see http://www.ralfebert.de/blog/eclipsercp/spring_osgi/#184

Martin Lippert said...

Glad to hear that the cardinality thing works for you!!! :-)

Cheers,
-Martin

Unknown said...

Hi Martin,

we are using the spring extension factory in a server side equinox context in which we are not allowed to set system properties due to restrictions by our customer.

Our idea is to use BundleContext.getProperty instead of System.getProperty in SpringExtensionFactory.getApplicationContext. In order to have a quick workaround we will create a patched version based on your source code.

What do you think about using this approach for an offical 1.0.4-version?

Cheers, Jens

Martin Lippert said...

Hi Jens!

This sounds like a good idea for a next version of the spring extension factory. I will take that into account, but don't know at the moment, when its time for a new version of it. Nevertheless if you have a patch for the code at hand, please feel free to send it to me. I would appreciate that a lot!

Cheers,
-Martin

Unknown said...

Hi Martin,

our trivial change is restricted to the following lines (excerpt from SpringExtensionFactory.getApplicationContext()):

int timeout = 5000;
try {
String timeoutSetting = System.getProperty(SpringExtensionFactory.TIMEOUT_PROPERTY_NAME);
if (timeoutSetting == null) {
// try to get this value from the BundleContext instead
// you can set values for the BundleContext in launch.ini or config.ini
timeoutSetting = Activator.getDefault().getBundleContext() .getProperty(SpringExtensionFactory.TIMEOUT_PROPERTY_NAME);
}
timeout = Integer.parseInt(timeoutSetting);
}
catch (Exception e) {
}

As you can see system properties are read from first. Reading the properties from the bundle context is just used as a fallback. Thus, the behaviour for client using system properties remains unchanged. Of course you could choose to always only use the properties from the bundle context. In this case framework properties would override system properties.

Cheers, Jens

Henno Vermeulen said...

Martin, I have a problem not directly related to your tool but I think you may be able to help me out, I would surely appreciate it!

I have a use case where I need to manually request a Spring service bean by name, so I wish to bypass your SpringExtensionFactory but use the same lookup method to get this service.

Do you know if I can reuse your code easily to do this?

Below are some details of my problem:

I want to make a generic Eclipse command + handler that can open an editor for Customer, Product, User, etc... by having it inspect the type of selected object in a view.
This way its code remains the same even when adding objects of different types. For example when the class name is "Customer" it needs to find the bean "customerDao", for "Product" it needs "productDao", etc...

At the moment I request these services by using the OSGi BundleContext.getService method.

The implementation of this does an RMI call and gives a ClassNotFoundException when the spring-context bundle tries to unmarshal the result of this call. I don't get this exception if I inject the service bean using your SpringExtensionFactory.
(My own bundle executing this code has access to all the classes I need)

For example when I call

dao.findById(1);

and this dao call returns an object of type Product, the Product class cannot be found by Spring even though my bundle has imported it.

I don't understand why this problem does not occur with the way SpringExtensionFactory gets the dao bean.

Martin Lippert said...

Hi Henno!

I think the best and easiest solution would be to let spring inject the application context object into some bean. This can be done by letting the bean implement the ApplicationContextAware interface. This injects the app context for your bundle and you can call this app context any time the bundle is alive for its beans (for productDao, customerDao, etc.).

The question remains in which object spring should inject the application context. In your case it sounds much like the generic command handler is the right one. So could you define the generic command handler within your spring context and refer to this from the extension using the spring extension factory?

HTH,
Martin

Unknown said...

Hi Martin,
Thanx for a really great product. I was wondering if you could explain if it is possible to apply the open session in view pattern. I keep getting a LazyInitialiazationException in my views when trying to display lazy loaded fields.

Martin Lippert said...

Hi Michael,

I am not sure what you are referring to. Is your question related to the Spring Extension Factory somehow? I think you need to explain this in more detail to me... :-)

Also make sure you pick up the Spring Extension Factory from the github location:

http://github.com/martinlippert/spring-extension-factory

Cheers,
-Martin

Rino said...
This comment has been removed by the author.
Rino said...

Hi Martin,

Can you confirm that your Spring Extension Factory can also be used in (RAP JFace) Dialogs, such as a TitleAreaDialog, when configured in the plugin.xml as org.eclipse.ui.views (sounds wrong to me ;-)

Because when i tried to use it as this:
@Autowired
private LoginDialog loginDialog;
the loginDialog stays null.

Maybe you know another way to fetch this bean from the spring context inside a (RAP) Application.java class.

Any help is appreciated
Regards
Rino

Martin Lippert said...

Hey Rino!

I haven't tried this with the @Autowired annotation, but the general injection mechanism should also work in a RAP app. I would try this using the old-fashioned injection mechanism first to see if the mechanism works. If you have that working, I would play with the @Autowired annotation.

HTH,
Martin

Rino said...

Hi Martin,

When running my rap app in Virgo, with your excellent Spring Extension Factory plugin (1.0.4), i got an almost simular exception as mentioned by @Eric Jain.

Bundle org.eclipse.core.runtime_3.6.0.v20100505, Unable to create view ID xyzView. Plug-in abc was unable to execute setInitializationData on an instance of "org.eclipse.springframework.util.SpringExtensionFactory". java.lang.NullPointerException: null
at org.eclipse.springframework.util. SpringExtensionFactory. getApplicationContext(SpringExtensionFactory.java:127)


Any ideas?
btw setting the timeout to a much higher value (in JAVA_OPTS in my case) doesn't do the trick

Thanks in advance
Rino