package org.distributeme.registry.metaregistry; import net.anotheria.util.IdCodeGenerator; import net.anotheria.util.StringUtils; import net.anotheria.util.queue.IQueueWorker; import net.anotheria.util.queue.QueuedProcessor; import net.anotheria.util.queue.UnrecoverableQueueOverflowException; import org.configureme.ConfigurationManager; import org.distributeme.core.RegistryUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; /** * This class supports operations that are needed for registry clustering. * @author lrosenberg */ public enum Cluster { /** * Singleton instance. */ INSTANCE; /** * The id of the node is unique and generated on startup. */ private String id = IdCodeGenerator.generateCode(20); /** * Entires of this cluster. */ private List<ClusterEntry> entries = new ArrayList<ClusterEntry>(); /** * QueuedProcessor for asynchronous process of commands. */ private QueuedProcessor<ClusterSyncCommand> syncCommandProcessor; /** * Logger. */ private static Logger log = LoggerFactory.getLogger(Cluster.class); /** * Configuration. */ private ClusterConfiguration configuration; /** * Singleton. */ private Cluster(){ } public void init(){ configuration = new ClusterConfiguration(); try{ ConfigurationManager.INSTANCE.configure(configuration); }catch(IllegalArgumentException e){ //ignore } MetaRegistryImpl.getInstance().addListener(new ClusterRegistryListener()); syncCommandProcessor = new QueuedProcessor<ClusterSyncCommand>("ClusterSyncCommandProcessor", new ClusterWorker(), 5000, 50, log); syncCommandProcessor.start(); } void reconfigure(String registryCluster){ log.info("Configuring registry cluster as "+registryCluster); if (registryCluster==null || registryCluster.length()==0) return; String tokens[] = StringUtils.tokenize(registryCluster, ','); for (String t : tokens){ String[] tokens2 = StringUtils.tokenize(t, ':'); ClusterEntry e = new ClusterEntry(tokens2[0], Integer.parseInt(tokens2[1])); //System.out.println("Parsed cluster entry "+e); entries.add(e); } log.info("Configured registry cluster with "+entries.size()+" entries: "+entries); } public List<ClusterEntry> entries(){ return entries; } public boolean isClusterActive(){ return entries!=null && entries.size()>0; } public void addSyncCommand(ClusterSyncCommand command){ try{ syncCommandProcessor.addToQueue(command); }catch(UnrecoverableQueueOverflowException e){ log.error("Couldn't schedule command due to queue overflow, cluster is probably NOT de-synched."); } } /** * The worker processes cluster sync commands in the update queue. * @author lrosenberg * */ class ClusterWorker implements IQueueWorker<ClusterSyncCommand>{ @Override public void doWork(ClusterSyncCommand command) throws Exception { if (log.isDebugEnabled()) log.debug("new sync command "+command); for (ClusterEntry entry : entries){ if (entry.isMe()) continue; boolean result; switch(command.getOperation()){ case UNBIND: result = RegistryUtil.notifyUnbind(entry, command.getDescriptor()); break; case BIND: result = RegistryUtil.notifyBind(entry, command.getDescriptor()); break; default: throw new IllegalArgumentException("Unknown operation of the sync command "+command); } if (log.isDebugEnabled()){ log.debug("Transmitted "+command+" on "+entry+" with success: "+result); } if (!result) log.warn("Couldn't transmit "+command+" to "+entry); } } } /** * Returns the id of this node of the cluster. * @return */ public String getId(){ return id; } public ClusterConfiguration getConfiguration(){ return configuration; } }