org.fusesource.fabric.activemq.OsgiDiscoveryAgent Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fabric-activemq Show documentation
Show all versions of fabric-activemq Show documentation
FuseSource Fabric :: ActiveMQ
The newest version!
/**
* Copyright (C) 2010-2011, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* The software in this package is published under the terms of the
* CDDL license a copy of which has been included with this distribution
* in the license.txt file.
*/
package org.fusesource.fabric.activemq;
import org.apache.activemq.command.DiscoveryEvent;
import org.apache.activemq.transport.discovery.DiscoveryAgent;
import org.apache.activemq.transport.discovery.DiscoveryListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
public class OsgiDiscoveryAgent implements DiscoveryAgent {
private static final Log LOG = LogFactory.getLog(OsgiDiscoveryAgent.class);
private AtomicBoolean running=new AtomicBoolean();
private final AtomicReference discoveryListener = new AtomicReference();
// private final HashSet registeredServices = new HashSet();
private final HashMap discoveredServices = new HashMap();
private final AtomicInteger startCounter = new AtomicInteger(0);
private Thread thread;
private long updateInterval = 1000*10;
private long initialReconnectDelay = 1000;
private long maxReconnectDelay = 1000 * 30;
private long backOffMultiplier = 2;
private boolean useExponentialBackOff=true;
private int maxReconnectAttempts = 0;
private final Object sleepMutex = new Object();
private long minConnectTime = 5000;
private String propertyName;
public void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
class SimpleDiscoveryEvent extends DiscoveryEvent {
private int connectFailures;
private long reconnectDelay = initialReconnectDelay;
private long connectTime = System.currentTimeMillis();
private AtomicBoolean failed = new AtomicBoolean(false);
private AtomicBoolean removed = new AtomicBoolean(false);
public SimpleDiscoveryEvent(String service) {
super(service);
}
}
public void registerService(String service) throws IOException {
throw new UnsupportedOperationException();
}
synchronized private Set doLookup(long freshness) {
Set rc = new HashSet();
BundleContext context = Activator.BUNDLE_CONTEXT.get();
if( context!=null ) {
ServiceReference reference = context.getServiceReference(ConfigurationAdmin.class.getName());
if (reference != null) {
ConfigurationAdmin ca = (ConfigurationAdmin)context.getService(reference);
try {
Configuration config = ca.getConfiguration(propertyName);
Dictionary properties = config.getProperties();
if (properties != null) {
Enumeration keys = properties.keys();
while (keys.hasMoreElements()) {
String key = (String) keys.nextElement();
if( key.startsWith("broker.url.") ) {
rc.add(((String)properties.get(key)).trim());
}
}
}
} catch (IOException e) {
}
}
}
return rc;
}
public void serviceFailed(DiscoveryEvent devent) throws IOException {
final SimpleDiscoveryEvent event = (SimpleDiscoveryEvent)devent;
if (event.failed.compareAndSet(false, true)) {
discoveryListener.get().onServiceRemove(event);
if(!event.removed.get()) {
// Setup a thread to re-raise the event...
Thread thread = new Thread() {
public void run() {
// We detect a failed connection attempt because the service
// fails right away.
if (event.connectTime + minConnectTime > System.currentTimeMillis()) {
LOG.debug("Failure occured soon after the discovery event was generated. It will be clasified as a connection failure: "+event);
event.connectFailures++;
if (maxReconnectAttempts > 0 && event.connectFailures >= maxReconnectAttempts) {
LOG.debug("Reconnect attempts exceeded "+maxReconnectAttempts+" tries. Reconnecting has been disabled.");
return;
}
synchronized (sleepMutex) {
try {
if (!running.get() || event.removed.get()) {
return;
}
LOG.debug("Waiting "+event.reconnectDelay+" ms before attepting to reconnect.");
sleepMutex.wait(event.reconnectDelay);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
return;
}
}
if (!useExponentialBackOff) {
event.reconnectDelay = initialReconnectDelay;
} else {
// Exponential increment of reconnect delay.
event.reconnectDelay *= backOffMultiplier;
if (event.reconnectDelay > maxReconnectDelay) {
event.reconnectDelay = maxReconnectDelay;
}
}
} else {
event.connectFailures = 0;
event.reconnectDelay = initialReconnectDelay;
}
if (!running.get() || event.removed.get()) {
return;
}
event.connectTime = System.currentTimeMillis();
event.failed.set(false);
discoveryListener.get().onServiceAdd(event);
}
};
thread.setDaemon(true);
thread.start();
}
}
}
public void setDiscoveryListener(DiscoveryListener discoveryListener) {
this.discoveryListener.set(discoveryListener);
}
public void start() throws Exception {
if( startCounter.addAndGet(1)==1 ) {
running.set(true);
thread = new Thread("HTTPDiscovery Agent") {
@Override
public void run() {
while(running.get()) {
try {
update();
Thread.sleep(updateInterval);
} catch (InterruptedException e) {
return;
}
}
}
};
thread.setDaemon(true);
thread.start();
}
}
private void update() {
// synchronized(registeredServices) {
// for (String service : registeredServices) {
// doRegister(service);
// }
// }
// Find new registered services...
DiscoveryListener discoveryListener = this.discoveryListener.get();
if(discoveryListener!=null) {
Set activeServices = doLookup(updateInterval*3);
// If there is error talking the the central server, then activeServices == null
if( activeServices !=null ) {
synchronized(discoveredServices) {
HashSet removedServices = new HashSet(discoveredServices.keySet());
removedServices.removeAll(activeServices);
HashSet addedServices = new HashSet(activeServices);
addedServices.removeAll(discoveredServices.keySet());
addedServices.removeAll(removedServices);
for (String service : addedServices) {
SimpleDiscoveryEvent e = new SimpleDiscoveryEvent(service);
discoveredServices.put(service, e);
discoveryListener.onServiceAdd(e);
}
for (String service : removedServices) {
SimpleDiscoveryEvent e = discoveredServices.remove(service);
if( e !=null ) {
e.removed.set(true);
}
discoveryListener.onServiceRemove(e);
}
}
}
}
}
public void stop() throws Exception {
if( startCounter.decrementAndGet()==0 ) {
running.set(false);
if( thread!=null ) {
thread.join(updateInterval*3);
thread=null;
}
}
}
public long getUpdateInterval() {
return updateInterval;
}
public void setUpdateInterval(long updateInterval) {
this.updateInterval = updateInterval;
}
}