[
Date Prev][
Date Next][
Thread Prev][
Thread Next][
Date Index][
Thread Index]
[
List Home]
Re: [ecf-dev] Race conditions with remote provider
|
Hi Alex,
On 6/6/2011 1:04 PM, Alex Blewitt wrote:
<stuff deleted>
Well, I'm writing a discovery container. So I'm using r-osgi and the example hello consumer/producer examples, just with my container instead of ZeroConf. In turn, I'm instantiating this and connecting to it (with connect(id,connectContext)) in the bundle activator's start method.
I see...that's interesting (using r-osgi to write a discovery API
provider).
<stuff deleted>
So I had something like (excuse the exact method calls, this is from memory):
class Activator {
void start() {
id = IDFactory.getDefault().createId(namespace, new String[] { "a","b","c" });
myContainer = new MyContainer();
myContainer.connect(id,null);
context.registerService(myContainer, {IDiscoveryListener, IDiscoveryAdvertiser});
}
}
The problem was that the ID Factory was using the namespace (either from the plugin.xml where I have some links, or from a service I may have registered prior to that call ... think the plugin.xml is how it's finding it) and using bundle.getClass() to load the namespace's specific class provider.
Since the start() method hasn't completed at that point, an external bundle doing bundle.loadClass(some.class.name) fails with some OSGi 'bundle not started' type exception.
I see. Markus Kuppe is the lead committer on the discovery API, so I'll
defer to him...but one approach you might take is to create a
ServiceFactory (rather than registering the locator/advertiser service
directly in the start method). This will defer the container creation
and the call to IDFactory.getDefault() (the IDFactory...and the
container factory for that matter...defer the processing of the
appropriate extensions until the *first* time that
IDFactory.getDefault() is called).
This (ServiceFactory) is the approach that the jmdns and jslp discovery
provider use...as the discovery providers 'want' to make themselves
available as soon as the ECF core itself is started...and this can (and
does) get mixed up with timing issues (e.g. sometimes discovery
container creation can initiate blocking I/O calls...as I expect your
connect call probably does...and naturally one won't want to put that
into the classload/activator start).
So, for example, here's some code that the jmdns provider uses on start():
final Properties props = new Properties();
props.put(IDiscoveryService.CONTAINER_NAME, NAME);
props.put(Constants.SERVICE_RANKING, new Integer(750));
String[] clazzes = new String[]
{IDiscoveryService.class.getName(), IDiscoveryLocator.class.getName(),
IDiscoveryAdvertiser.class.getName()};
// this is the usage of ServiceFactory
serviceRegistration = context.registerService(clazzes, new
ServiceFactory() {
private volatile JMDNSDiscoveryContainer jdc;
/* (non-Javadoc)
* @see
org.osgi.framework.ServiceFactory#getService(org.osgi.framework.Bundle,
org.osgi.framework.ServiceRegistration)
*/
public Object getService(final Bundle bundle, final
ServiceRegistration registration) {
if (jdc == null) {
try {
jdc = new JMDNSDiscoveryContainer(); // <--
the jmdns id is created in this constructor
jdc.connect(null, null);
// this catch is for the case when the JMDNSDiscoveryContainer ID
creation fails
} catch (final IDCreateException e) {
Trace.catching(JMDNSPlugin.PLUGIN_ID,
JMDNSDebugOptions.EXCEPTIONS_CATCHING, this.getClass(),
"getService(Bundle, ServiceRegistration)", e); //$NON-NLS-1$ //$NON-NLS-2$
} catch (final ContainerConnectException e) {
Trace.catching(JMDNSPlugin.PLUGIN_ID,
JMDNSDebugOptions.EXCEPTIONS_CATCHING, this.getClass(),
"getService(Bundle, ServiceRegistration)", e); //$NON-NLS-1$ //$NON-NLS-2$
jdc = null;
}
}
return jdc;
}
/* (non-Javadoc)
* @see
org.osgi.framework.ServiceFactory#ungetService(org.osgi.framework.Bundle, org.osgi.framework.ServiceRegistration,
java.lang.Object)
*/
public void ungetService(final Bundle bundle, final
ServiceRegistration registration, final Object service) {
//TODO-mkuppe we later might want to dispose jSLP when
the last!!! consumer ungets the service
//Though don't forget about the (ECF) Container which
might still be in use
}
}, props);
Hopefully this will help with your case. Discovery providers are
naturally kind of tricky...since they typically 'want' to be started
very early (upon start of ECF itself), while still having lazy
processing of the ECF extensions (id factories and container
factories). The ServiceFactory allows/supports this without creating
timing/classload/bundle state exception with another thread.
I'm hoping/expecting that Markus will smack me down quick if this is all
wrong though :).
Thanks,
Scott