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