package com.linkedin.databus.client;
/*
*
* Copyright 2013 LinkedIn Corp. All rights reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jboss.netty.channel.Channel;
import com.linkedin.databus.client.pub.ServerInfo;
import com.linkedin.databus.core.BootstrapCheckpointHandler;
import com.linkedin.databus.core.Checkpoint;
import com.linkedin.databus.core.DbusEventBuffer;
import com.linkedin.databus.core.DbusEventFactory;
import com.linkedin.databus.core.DbusEventInternalReadable;
import com.linkedin.databus.core.InternalDatabusEventsListener;
import com.linkedin.databus.core.data_model.DatabusSubscription;
import com.linkedin.databus.core.util.IdNamePair;
import com.linkedin.databus2.core.DatabusException;
import com.linkedin.databus2.core.container.request.RegisterResponseEntry;
import com.linkedin.databus2.core.container.request.RegisterResponseMetadataEntry;
public class ConnectionState implements DatabusRelayConnectionStateMessage,
DatabusBootstrapConnectionStateMessage
{
public enum StateId
{
INITIAL,
PICK_SERVER,
REQUEST_SOURCES,
SOURCES_REQUEST_SENT,
SOURCES_RESPONSE_SUCCESS,
SOURCES_REQUEST_ERROR,
SOURCES_RESPONSE_ERROR,
REQUEST_REGISTER,
REGISTER_REQUEST_SENT,
REGISTER_RESPONSE_SUCCESS,
REGISTER_REQUEST_ERROR,
REGISTER_RESPONSE_ERROR,
REQUEST_STREAM,
STREAM_REQUEST_SENT,
STREAM_REQUEST_SUCCESS,
STREAM_REQUEST_ERROR,
STREAM_RESPONSE_ERROR,
STREAM_RESPONSE_DONE,
CLOSED,
BOOTSTRAP_REQUESTED,
BOOTSTRAP,
REQUEST_START_SCN,
START_SCN_REQUEST_SENT,
START_SCN_RESPONSE_SUCCESS,
START_SCN_REQUEST_ERROR,
START_SCN_RESPONSE_ERROR,
REQUEST_TARGET_SCN,
TARGET_SCN_REQUEST_SENT,
TARGET_SCN_RESPONSE_SUCCESS,
TARGET_SCN_REQUEST_ERROR,
TARGET_SCN_RESPONSE_ERROR,
START_SNAPSHOT_PHASE,
START_CATCHUP_PHASE,
BOOTSTRAP_DONE
}
private StateId _stateId;
//INIITIAL
private final DbusEventBuffer _dataEventsBuffer;
private DatabusHttpClientImpl.StaticConfig _clientConfig;
protected final List<DatabusSubscription> _subscriptions;
private final List<String> _sourcesNames;
protected BootstrapCheckpointHandler _bstCheckpointHandler = null;
private final String _sourcesNameList;
//PICK_SERVER extends INITIAL
private boolean _isRelayFellOff = false;
//REQUEST_SOURCES extends PICK_SERVER
private InetSocketAddress _serverInetAddress;
private ServerInfo _currentServerInfo;
private DatabusRelayConnection _relayConnection;
//REQUEST_START_SCN extends PICK_SERVER
private DatabusBootstrapConnection _bootstrapConnection;
//SOURCES_RESPONSE_SUCCESS extends REQUEST_SOURCES
private List<IdNamePair> _sources;
protected Map<Long, IdNamePair> _sourcesIdMap = new HashMap<Long, IdNamePair>(20);
protected Map<String, IdNamePair> _sourcesNameMap = new HashMap<String, IdNamePair>(20);
//private DbusKeyCompositeFilter _filter = new DbusKeyCompositeFilter();
//REQUEST_SOURCES_SCHEMAS extends SOURCES_READY
private String _sourcesIdListString;
private String _subsListString;
//SOURCES_SCHEMAS_READY extends READ_SOURCES_SCHEMAS
private Map<Long, List<RegisterResponseEntry>> _sourcesSchemas; // mandatory, else can't make callbacks to consumer
private Map<Long, List<RegisterResponseEntry>> _keysSchemas;
private List<RegisterResponseMetadataEntry> _metadataSchemas;
//REQUEST_DATA_EVENTS extends SOURCES_SCHEMAS_READY
private Checkpoint _checkpoint;
private boolean _scnRegress = false;
private boolean _flexibleCheckpointRequest = false;
private final List<InternalDatabusEventsListener> _readEventListeners;
//READ_DATA_EVENTS extends REQUEST_DATA_EVENTS
private ChunkedBodyReadableByteChannel _readChannel;
//START_SNAPSHOT_PHASE
private Channel _startScnChannel;
private ServerInfo _currentBSServerInfo;
private String _hostName, _svcName;
//DATA_EVENTS_DONE extends READ_DATE_EVENTS
protected ConnectionState(DbusEventBuffer dataEventsBuffer, List<String> sourcesNames, List<DatabusSubscription> subs)
{
_stateId = StateId.INITIAL;
_dataEventsBuffer = dataEventsBuffer;
_readEventListeners = new ArrayList<InternalDatabusEventsListener>();
if(subs != null)
{
_subscriptions = new ArrayList<DatabusSubscription>(subs);
_sourcesNames = DatabusSubscription.getStrList(_subscriptions);
}
else if(sourcesNames != null)
{
_subscriptions = DatabusSubscription.createSubscriptionList(sourcesNames);
_sourcesNames = new ArrayList<String>(sourcesNames);
}
else
{
throw new IllegalArgumentException("both sources and subscriptions are null");
}
StringBuilder sb = new StringBuilder();
boolean firstSource = true;
for (String sourceName: _sourcesNames)
{
if (! firstSource) sb.append(',');
sb.append(sourceName);
firstSource = false;
}
_sourcesNameList = sb.toString();
_hostName = "";
_svcName = "";
// The bootstrap checkpoint handler will be constructed when we get the final list of sources from the relay.
}
// This method is overridden for V3
public void createBootstrapCheckpointHandler()
{
assert _bstCheckpointHandler == null : "BootstrapCheckpointHandler already initialized " + _bstCheckpointHandler.toString();
_bstCheckpointHandler = new BootstrapCheckpointHandler(_sourcesNames);
}
public void expandSubscriptions()
{
// For V3, the table names are known after the relay responds with the source list,
// so this method is overridden to replace _subscriptions and _sourcesNames accordingly.
}
public StateId getStateId()
{
return _stateId;
}
public ConnectionState switchToPickServer()
{
_stateId = StateId.PICK_SERVER;
return this;
}
public ConnectionState switchToRequestSources(ServerInfo relayInfo,
InetSocketAddress relayInetAddr,
DatabusRelayConnection relayConnection)
{
//TODO HIGH : add checks for valid input states
_stateId = StateId.REQUEST_SOURCES;
_serverInetAddress = relayInetAddr;
_currentServerInfo = relayInfo;
_relayConnection = relayConnection;
return this;
}
@Override
public void switchToSourcesRequestError()
{
_stateId = StateId.SOURCES_REQUEST_ERROR;
}
@Override
public void switchToSourcesResponseError()
{
_stateId = StateId.SOURCES_RESPONSE_ERROR;
}
@Override
public void switchToSourcesSuccess(List<IdNamePair> sources, String hostName, String svcName)
{
_stateId = StateId.SOURCES_RESPONSE_SUCCESS;
setSourcesIds(sources);
setTrackingInfo(hostName, svcName);
}
public void setSourcesIds(List<IdNamePair> sources)
{
_sources = sources;
_sourcesIdMap.clear();
_sourcesNameMap.clear();
for (IdNamePair source: _sources)
{
_sourcesIdMap.put(source.getId(), source);
_sourcesNameMap.put(source.getName(), source);
}
expandSubscriptions();
}
public void setTrackingInfo(String hostName, String svcName)
{
_hostName = hostName;
_svcName = svcName;
}
public String getHostName()
{
return _hostName;
}
public String getSvcName()
{
return _svcName;
}
// This method is overridden for V3
public DbusEventInternalReadable createEopEvent(Checkpoint cp, DbusEventFactory eventFactory)
throws DatabusException
{
return eventFactory.createLongKeyEOPEvent(cp.getWindowScn(), (short) 0);
}
public ConnectionState switchToRequestSourcesSchemas(String sourcesIdListString,
String subsListString)
{
_stateId = StateId.REQUEST_REGISTER;
setSourcesIdListString(sourcesIdListString) ;
setSubsListString(subsListString);
return this;
}
public void setSourcesIdListString(String sourcesIdListString)
{
_sourcesIdListString = sourcesIdListString;
}
public void setSubsListString(String subsListString)
{
_subsListString = subsListString;
}
@Override
public void switchToRegisterSuccess(Map<Long, List<RegisterResponseEntry>> sourcesSchemas,
Map<Long, List<RegisterResponseEntry>> keysSchemas,
List<RegisterResponseMetadataEntry> metadataSchemas)
{
_stateId = StateId.REGISTER_RESPONSE_SUCCESS;
setSourcesSchemas(sourcesSchemas);
setKeysSchemas(keysSchemas); // OK if null
setMetadataSchemas(metadataSchemas); // OK if null
}
public void setSourcesSchemas(Map<Long, List<RegisterResponseEntry>> sourcesSchemas)
{
_sourcesSchemas = sourcesSchemas;
}
public void setKeysSchemas(Map<Long, List<RegisterResponseEntry>> keysSchemas)
{
_keysSchemas = keysSchemas;
}
public void setMetadataSchemas(List<RegisterResponseMetadataEntry> metadataSchemas)
{
_metadataSchemas = metadataSchemas;
}
public ConnectionState switchToRequestStream(Checkpoint checkpoint)
{
_stateId = StateId.REQUEST_STREAM;
if (_checkpoint != checkpoint || _readEventListeners.isEmpty())
{
_checkpoint = checkpoint;
_readEventListeners.clear();
_readEventListeners.add(_checkpoint);
}
return this;
}
@Override
public void switchToStreamSuccess(ChunkedBodyReadableByteChannel readChannel)
{
_stateId = StateId.STREAM_REQUEST_SUCCESS;
_readChannel = readChannel;
}
public void switchToStreamResponseDone()
{
_stateId = StateId.STREAM_RESPONSE_DONE;
}
public ConnectionState switchToClosed()
{
_stateId = StateId.CLOSED;
return this;
}
@Override
public void switchToRegisterRequestError()
{
_stateId = StateId.REGISTER_REQUEST_ERROR;
}
@Override
public void switchToRegisterResponseError()
{
_stateId = StateId.REGISTER_RESPONSE_ERROR;
}
public DatabusRelayConnection getRelayConnection()
{
return _relayConnection;
}
@Override
public void switchToStreamRequestError()
{
_stateId = StateId.STREAM_REQUEST_ERROR;
}
@Override
public void switchToStreamResponseError()
{
_stateId = StateId.STREAM_RESPONSE_ERROR;
}
public ConnectionState switchToRequestTargetScn(Checkpoint ckpt)
{
_stateId = StateId.REQUEST_TARGET_SCN;
setCheckpoint(ckpt);
return this;
}
@Override
public void switchToTargetScnRequestError()
{
_stateId = StateId.TARGET_SCN_REQUEST_ERROR;
}
@Override
public void switchToTargetScnResponseError()
{
_stateId = StateId.TARGET_SCN_RESPONSE_ERROR;
}
@Override
public void switchToTargetScnSuccess()
{
_stateId = StateId.TARGET_SCN_RESPONSE_SUCCESS;
}
public ConnectionState bootstrapServerSelected(InetSocketAddress serviceInetAddr,
DatabusBootstrapConnection bootstrapConnection,
ServerInfo currentBSServerInfo)
{
_serverInetAddress = serviceInetAddr;
_bootstrapConnection = bootstrapConnection;
_currentBSServerInfo = currentBSServerInfo;
return this;
}
public ConnectionState switchToRequestStartScn(Checkpoint ckpt)
{
_stateId = StateId.REQUEST_START_SCN;
setCheckpoint(ckpt);
return this;
}
@Override
public void switchToStartScnRequestError()
{
_stateId = StateId.START_SCN_REQUEST_ERROR;
}
@Override
public void switchToStartScnResponseError()
{
_stateId = StateId.START_SCN_RESPONSE_ERROR;
}
@Override
public void switchToStartScnSuccess(Checkpoint ckpt, DatabusBootstrapConnection bootstrapConnection, ServerInfo bsServerInfo)
{
_stateId = StateId.START_SCN_RESPONSE_SUCCESS;
_currentBSServerInfo = bsServerInfo;
if ( ckpt != _checkpoint)
{
_checkpoint = ckpt;
_readEventListeners.clear();
_readEventListeners.add(_checkpoint);
}
if ( null != bootstrapConnection)
{
_bootstrapConnection = bootstrapConnection;
}
}
public void switchToBootstrap(Checkpoint cp)
{
_stateId = StateId.BOOTSTRAP;
_checkpoint = cp;
}
public Channel getStartScnChannel()
{
return _startScnChannel;
}
public List<String> getSourcesNames()
{
return _sourcesNames;
}
public List<DatabusSubscription> getSubscriptions()
{
return _subscriptions;
}
public String getSourcesNameList()
{
return _sourcesNameList;
}
public InetSocketAddress getServerInetAddress()
{
return _serverInetAddress;
}
public List<IdNamePair> getSources()
{
return _sources;
}
public String getSourcesIdListString()
{
return _sourcesIdListString;
}
public String getSubsListString()
{
return _subsListString;
}
public Map<Long, IdNamePair> getSourceIdMap()
{
return _sourcesIdMap;
}
public Map<String, IdNamePair> getSourcesNameMap()
{
return _sourcesNameMap;
}
public Map<Long, List<RegisterResponseEntry>> getSourcesSchemas()
{
return _sourcesSchemas;
}
public Map<Long, List<RegisterResponseEntry>> getKeysSchemas()
{
return _keysSchemas;
}
public List<RegisterResponseMetadataEntry> getMetadataSchemas()
{
return _metadataSchemas;
}
public DbusEventBuffer getDataEventsBuffer()
{
return _dataEventsBuffer;
}
public ChunkedBodyReadableByteChannel getReadChannel()
{
return _readChannel;
}
public void setCheckpoint(Checkpoint cp)
{
//intentional object comparison!
if (cp == _checkpoint) return;
if (null != _checkpoint)
{
_readEventListeners.remove(_checkpoint);
}
_checkpoint = cp;
_readEventListeners.add(_checkpoint);
}
public Checkpoint getCheckpoint()
{
return _checkpoint;
}
public List<InternalDatabusEventsListener> getListeners()
{
return _readEventListeners;
}
public DatabusHttpClientImpl.StaticConfig getClientConfig()
{
return _clientConfig;
}
@Override
public String toString()
{
return _stateId.toString();
}
public ServerInfo getCurrentServerInfo()
{
return _currentServerInfo;
}
public DatabusBootstrapConnection getBootstrapConnection()
{
return _bootstrapConnection;
}
public void clearBootstrapState()
{
_readEventListeners.clear();
}
public void setRelayConnection(DatabusRelayConnection conn)
{
_relayConnection = conn;
}
public void setBootstrapConnection(DatabusBootstrapConnection conn)
{
_bootstrapConnection = conn;
}
@Override
public void switchToSourcesRequestSent() {
_stateId = StateId.SOURCES_REQUEST_SENT;
}
@Override
public void swichToRegisterRequestSent() {
_stateId = StateId.REGISTER_REQUEST_SENT;
}
@Override
public void switchToStreamRequestSent() {
_stateId = StateId.STREAM_REQUEST_SENT;
}
@Override
public void switchToBootstrapRequested() {
_stateId = StateId.BOOTSTRAP_REQUESTED;
}
@Override
public void switchToBootstrapDone()
{
_stateId = StateId.BOOTSTRAP_DONE;
}
@Override
public void switchToStartScnRequestSent()
{
_stateId = StateId.START_SCN_REQUEST_SENT;
}
@Override
public void switchToTargetScnRequestSent()
{
_stateId = StateId.TARGET_SCN_REQUEST_SENT;
}
public ServerInfo getCurrentBSServerInfo()
{
return _currentBSServerInfo;
}
public void setCurrentBSServerInfo(ServerInfo serverInfo)
{
_currentBSServerInfo = serverInfo;
}
public boolean isRelayFellOff()
{
return _isRelayFellOff;
}
public void setRelayFellOff(boolean retryOnFellOff)
{
_isRelayFellOff = retryOnFellOff;
}
public boolean isSCNRegress()
{
return _scnRegress;
}
public void setSCNRegress(boolean regress)
{
_scnRegress = regress;
}
public boolean isFlexibleCheckpointRequest()
{
return _flexibleCheckpointRequest;
}
public void setFlexibleCheckpointRequest(boolean flexibleCheckpointRequest)
{
_flexibleCheckpointRequest = flexibleCheckpointRequest;
}
/**
* @return the bstCheckpointHandler
*/
protected BootstrapCheckpointHandler getBstCheckpointHandler()
{
return _bstCheckpointHandler;
}
}