Some comments. You should not put the tracked service in the instance variable channel of the outer class. When you need the service in your Handler, get it from the tracker in a local var.
You should not be using the tracker object for your own wait/notify needs. ServiceTracker is not really designed for that. You should use your own objects for that. You probably should not block the handler's publish method indefinitely. Who knows what kind of deadlocks that will result in. Instead, you should buffer the info yourself until a tracked service reappears at which time you can flush your buffer into the tracked service. You should probably implement yourself as a queue where the handler publish implementation enqueues items. If there is a tracked channel, it can be immediately dequeued to the channel. Otherwise, then the tracker customizer later adds a channel, all queued items can be dequeued to the channel. -- BJ Hargrave Senior Technical Staff Member, IBM OSGi Fellow and CTO of the OSGi Alliance [email protected] office: +1 386 848 1781 mobile: +1 386 848 3788 From: Daniel McGreal <[email protected]> To: [email protected] Date: 2013/09/19 12:03 Subject: [osgi-dev] ServiceTrackers and blocking during refresh Sent by: [email protected] Hi OSGi-devs, I have a question about utilising ServiceTracker to manage a class's dependencies and blocking during the call, which I confess is probably well trodden ground but for which I can't apparently find the right Google terminology. Perhaps because I've been, until now, coddled by higher-level service dynamism, like Blueprint. I have a java.util.logging.Handler whose publish method needs a couple of OSGi services to operate. Any LogRecords that are published during a refresh of either of the dependencies should not be lost and should be reattempted when services resume (which in practice might be a long time, the device this application targets is incredibly primitive). Currently I have the Activator and Handler in the same implementation (which seems like it might be bad practice, if it is, I'd love to know why). Here's a trimmed down version, with just one dependency: public class RemoteLoggingHandler extends Handler implements BundleActivator { private volatile Channel channel; private ServiceTracker<IAmqpChannelProvider, IAmqpChannelProvider> channelProviderTracker; @Override public void start(BundleContext context) throws Exception { channelProviderTracker = new ServiceTracker<IAmqpChannelProvider, IAmqpChannelProvider>(context, IAmqpChannelProvider.class, null){ @Override public IAmqpChannelProvider addingService(ServiceReference<IAmqpChannelProvider> reference) { IAmqpChannelProvider channelProvider = super.addingService(reference); try { channel = channelProvider.createChannel(); //Some initialisation, can happen once or multiple times, no problem. channel.exchangeDeclare(LoggingConstants.LOGGING_EXG_NAME, "topic", true); notifyAll(); } catch (IOException e) { e.printStackTrace(); } return channelProvider; } @Override public synchronized void modifiedService(ServiceReference<IAmqpChannelProvider> reference, IAmqpChannelProvider service) { removedService(reference, service); addingService(reference); } @Override public void removedService(ServiceReference<IAmqpChannelProvider> reference, IAmqpChannelProvider service) { super.removedService(reference, service); channel = null; } }; channelProviderTracker.open(); LogManager.getLogManager().getLogger("").addHandler(this); } @Override public void stop(BundleContext context) throws Exception { LogManager.getLogManager().getLogger("").removeHandler(this); } @Override public void publish(LogRecord record) { try { synchronized (channelProviderTracker) { while(channel == null) channelProviderTracker.wait(); channel.basicPublish(LoggingConstants.LOGGING_EXG_NAME, record.getLoggerName(), null, record.getMessage().getBytes()); } } catch (Exception e) { reportError(null, e, ErrorManager.WRITE_FAILURE); return; } } //Some boilerplate.... @Override public void flush() {} //Unnecessary. @Override public void close() throws SecurityException { flush(); try { channel.close(); } catch (IOException e) { reportError("Amqp channel could not be closed", e, ErrorManager. CLOSE_FAILURE); } } } I don't feel that I've been successful. The channel member field can still go to null while it's being used in publish, and probably many other problems I've missed! Can anyone point out to me either the standard procedure for this, or any comments on my above attempt? Many thanks, Dan. _______________________________________________ OSGi Developer Mail List [email protected] https://mail.osgi.org/mailman/listinfo/osgi-dev
_______________________________________________ OSGi Developer Mail List [email protected] https://mail.osgi.org/mailman/listinfo/osgi-dev
