/** * Copyright (c) 2002-2012 "Neo Technology," * Network Engine for Objects in Lund AB [http://neotechnology.com] * * This file is part of Neo4j. * * Neo4j is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.neo4j.kernel.ha; import static org.neo4j.kernel.ha.cluster.ClusterMemberModeSwitcher.getServerId; import java.net.URI; import java.util.HashMap; import java.util.Map; import org.neo4j.cluster.protocol.cluster.ClusterListener; import org.neo4j.com.ComSettings; import org.neo4j.kernel.configuration.Config; import org.neo4j.kernel.ha.cluster.ClusterMemberChangeEvent; import org.neo4j.kernel.ha.cluster.ClusterMemberListener; import org.neo4j.kernel.ha.cluster.ClusterMemberState; import org.neo4j.kernel.ha.cluster.ClusterMemberStateMachine; import org.neo4j.kernel.impl.nioneo.store.StoreId; import org.neo4j.kernel.impl.nioneo.xa.NeoStoreXaDataSource; import org.neo4j.kernel.impl.transaction.DataSourceRegistrationListener; import org.neo4j.kernel.impl.transaction.XaDataSourceManager; import org.neo4j.kernel.impl.transaction.xaframework.XaDataSource; import org.neo4j.kernel.impl.util.StringLogger; import org.neo4j.kernel.lifecycle.LifeSupport; import org.neo4j.kernel.lifecycle.Lifecycle; public class ClusterSlaves implements Slaves, Lifecycle { private volatile Map<URI /*Member cluster URI, not HA URI*/, Slave> slaves; protected final StringLogger msgLog; private StoreId storeId; private final Config config; private final LifeSupport life; private final ClusterListener clusterListener; public ClusterSlaves( ClusterMemberStateMachine clusterEvents, StringLogger msgLog, Config config, XaDataSourceManager xaDsm ) { this.msgLog = msgLog; this.config = config; this.life = new LifeSupport(); this.slaves = new HashMap<URI, Slave>(); clusterEvents.addClusterMemberListener( new SlavesClusterEventsListener() ); xaDsm.addDataSourceRegistrationListener( new StoreIdSettingListener() ); clusterListener = new SlavesClusterListener(); } // TODO Creepy getter due to initialization problems between this and ClusterJoin ClusterListener getClusterListener() { return clusterListener; } @Override public void init() throws Throwable { life.init(); } @Override public void start() throws Throwable { life.start(); } @Override public void stop() throws Throwable { life.stop(); } @Override public void shutdown() throws Throwable { life.shutdown(); } @Override public Iterable<Slave> getSlaves() { return slaves.values(); } private class SlavesClusterEventsListener implements ClusterMemberListener { private volatile boolean instantiateFullSlaveClients; private volatile URI previouslyElectedMaster; @Override public void masterIsElected( ClusterMemberChangeEvent event ) { instantiateFullSlaveClients = event.getNewState() == ClusterMemberState.TO_MASTER || event.getNewState () == ClusterMemberState.MASTER; URI masterUri = event.getServerClusterUri(); if ( previouslyElectedMaster == null || !previouslyElectedMaster.equals( masterUri ) ) { life.clear(); slaves = new HashMap<URI, Slave>(); previouslyElectedMaster = masterUri; } } @Override public void slaveIsAvailable( ClusterMemberChangeEvent event ) { Map<URI, Slave> newSlaves = new HashMap<URI, Slave>( slaves ); URI slaveHaUri = event.getServerHaUri(); Slave slave = instantiateFullSlaveClients ? life.add( new SlaveClient( getServerId( slaveHaUri ), slaveHaUri.getHost(), slaveHaUri.getPort(), msgLog, storeId, config.get( HaSettings.max_concurrent_channels_per_slave ), config.get( ComSettings.com_chunk_size ) ) ) : new SlaveInformation( slaveHaUri, getServerId( slaveHaUri ) ); newSlaves.put( event.getServerClusterUri(), slave ); slaves = newSlaves; } @Override public void instanceStops( ClusterMemberChangeEvent event ) { slaveLeft( event.getServerHaUri() ); } @Override public void masterIsAvailable( ClusterMemberChangeEvent event ) { } } private class StoreIdSettingListener implements DataSourceRegistrationListener { @Override public void registeredDataSource( XaDataSource ds ) { if ( ds.getName().equals( Config.DEFAULT_DATA_SOURCE_NAME ) ) { NeoStoreXaDataSource neoXaDs = (NeoStoreXaDataSource) ds; storeId = neoXaDs.getStoreId(); } } @Override public void unregisteredDataSource( XaDataSource ds ) { } } private class SlavesClusterListener extends ClusterListener.Adapter { @Override public void leftCluster( URI member ) { slaveLeft( member ); } } protected Slave instantiateSlaveClient( URI haUri ) { return life.add( new SlaveClient( getServerId( haUri ), haUri.getHost(), haUri.getPort(), msgLog, storeId, config.get( HaSettings.max_concurrent_channels_per_slave ), config.get( ComSettings.com_chunk_size ) ) ); } private void slaveLeft( URI slaveUri ) { Map<URI, Slave> newSlaves = new HashMap<URI, Slave>( slaves ); Slave slave = newSlaves.remove( slaveUri ); life.remove( slave ); slaves = newSlaves; } }