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

Reply via email to