Many thanks, 
I shall reimplement tomorrow and potentially seek further specific advice if 
I've still got any questions.
Best, Dan.

> On 19 Sep 2013, at 18:57, BJ Hargrave <[email protected]> wrote:
> 
> 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
_______________________________________________
OSGi Developer Mail List
[email protected]
https://mail.osgi.org/mailman/listinfo/osgi-dev

Reply via email to