/*
* $Id: DatabusRelayMain.java 261967 2011-04-19 02:54:35Z cbotev $
*/
package com.linkedin.databus2.relay;
/*
*
* 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.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.log4j.Logger;
import com.linkedin.databus.container.netty.HttpRelay;
import com.linkedin.databus.container.request.ControlSourceEventsRequestProcessor;
import com.linkedin.databus.core.DatabusRuntimeException;
import com.linkedin.databus.core.DbusEventBufferAppendable;
import com.linkedin.databus.core.UnsupportedKeyException;
import com.linkedin.databus.core.data_model.PhysicalPartition;
import com.linkedin.databus.core.util.InvalidConfigException;
import com.linkedin.databus2.core.DatabusException;
import com.linkedin.databus2.core.container.request.ProcessorRegistrationConflictException;
import com.linkedin.databus2.core.container.request.RequestProcessorRegistry;
import com.linkedin.databus2.core.seq.MaxSCNReaderWriter;
import com.linkedin.databus2.core.seq.MultiServerSequenceNumberHandler;
import com.linkedin.databus2.core.seq.SequenceNumberHandlerFactory;
import com.linkedin.databus2.producers.EventCreationException;
import com.linkedin.databus2.producers.EventProducer;
import com.linkedin.databus2.producers.EventProducerServiceProvider;
import com.linkedin.databus2.producers.RelayEventProducer;
import com.linkedin.databus2.producers.RelayEventProducersRegistry;
import com.linkedin.databus2.producers.db.OracleEventProducer;
import com.linkedin.databus2.relay.config.PhysicalSourceStaticConfig;
import com.linkedin.databus2.relay.config.ReplicationBitSetterStaticConfig.SourceType;
import com.linkedin.databus2.schemas.SchemaRegistryService;
/**
* Main class for a Databus Relay.
*
* @author Jemiah Westerman<jwesterman@linkedin.com>
* @version $Revision: 261967 $
*/
public class DatabusRelayMain extends HttpRelay {
public static final Logger LOG = Logger.getLogger(DatabusRelayMain.class
.getName());
public static final String DB_RELAY_CONFIG_FILE_OPT_NAME = "db_relay_config";
private final RelayEventProducersRegistry _producersRegistry = RelayEventProducersRegistry.getInstance();
MultiServerSequenceNumberHandler _maxScnReaderWriters;
protected Map<PhysicalPartition, EventProducer> _producers;
Map<PhysicalPartition, MonitoringEventProducer> _monitoringProducers;
ControlSourceEventsRequestProcessor _csEventRequestProcessor;
private boolean _dbPullerStart = false;
public DatabusRelayMain() throws IOException, InvalidConfigException,
DatabusException {
this(new HttpRelay.Config(), null);
}
public DatabusRelayMain(HttpRelay.Config config,
PhysicalSourceStaticConfig[] pConfigs) throws IOException,
InvalidConfigException, DatabusException {
this(config.build(), pConfigs);
}
public DatabusRelayMain(HttpRelay.StaticConfig config,
PhysicalSourceStaticConfig[] pConfigs) throws IOException,
InvalidConfigException, DatabusException {
super(config, pConfigs);
SequenceNumberHandlerFactory handlerFactory = _relayStaticConfig
.getDataSources().getSequenceNumbersHandler().createFactory();
_maxScnReaderWriters = new MultiServerSequenceNumberHandler(
handlerFactory);
_producers = new HashMap<PhysicalPartition, EventProducer>(
_pConfigs.size());
_monitoringProducers = new HashMap<PhysicalPartition, MonitoringEventProducer>(_pConfigs.size());
_dbPullerStart = false;
}
public void setDbPullerStart(boolean s) {
_dbPullerStart = s;
}
public boolean getDbPullerStart() {
return _dbPullerStart;
}
/** overrides HTTP relay method */
@Override
public void removeOneProducer(PhysicalSourceStaticConfig pConfig) {
PhysicalPartition pPartition = pConfig.getPhysicalPartition();
List<EventProducer> plist = new ArrayList<EventProducer>();
if (_producers != null && _producers.containsKey(pPartition))
plist.add(_producers.remove(pPartition));
// if(_maxScnReaderWriters != null &&
// _maxScnReaderWriters.getHandler(pPartition)
// _maxScnReaderWriters.remove();
if (_monitoringProducers != null
&& _monitoringProducers.containsKey(pPartition))
plist.add(_monitoringProducers.remove(pPartition));
if (plist.size() > 0 && _csEventRequestProcessor != null)
_csEventRequestProcessor.removeEventProducers(plist);
}
/** overrides HTTP relay method */
@Override
public void addOneProducer(PhysicalSourceStaticConfig pConfig)
throws DatabusException, EventCreationException,
UnsupportedKeyException, SQLException, InvalidConfigException {
// Register a command to allow start/stop/status of the relay
List<EventProducer> plist = new ArrayList<EventProducer>();
PhysicalPartition pPartition = pConfig.getPhysicalPartition();
MaxSCNReaderWriter maxScnReaderWriters = _maxScnReaderWriters
.getOrCreateHandler(pPartition);
LOG.info("Starting server container with maxScnReaderWriter:"
+ maxScnReaderWriters);
// Get the event buffer
DbusEventBufferAppendable dbusEventBuffer = getEventBuffer()
.getDbusEventBufferAppendable(pPartition);
// Get the schema registry service
SchemaRegistryService schemaRegistryService = getSchemaRegistryService();
// Get a stats collector per physical source
addPhysicalPartitionCollectors(pPartition);
String statsCollectorName = pPartition.toSimpleString();
/*
* _inBoundStatsCollectors.addStatsCollector(statsCollectorName, new
* DbusEventsStatisticsCollector(getContainerStaticConfig().getId(),
* statsCollectorName+".inbound", true, false, getMbeanServer()));
*
* _outBoundStatsCollectors.addStatsCollector(statsCollectorName, new
* DbusEventsStatisticsCollector(getContainerStaticConfig().getId(),
* statsCollectorName+".outbound", true, false, getMbeanServer()));
*/
// Create the event producer
String uri = pConfig.getUri();
if(uri == null)
throw new DatabusException("Uri is required to start the relay");
uri = uri.trim();
EventProducer producer = null;
if (uri.startsWith("jdbc:")) {
SourceType sourceType = pConfig.getReplBitSetter().getSourceType();
if (SourceType.TOKEN.equals(sourceType))
throw new DatabusException("Token Source-type for Replication bit setter config cannot be set for trigger-based Databus relay !!");
// if a buffer for this partiton exists - we are overwri
producer = new OracleEventProducerFactory().buildEventProducer(
pConfig, schemaRegistryService, dbusEventBuffer,
getMbeanServer(), _inBoundStatsCollectors
.getStatsCollector(statsCollectorName),
maxScnReaderWriters);
} else if (uri.startsWith("mock")) {
// Get all relevant pConfig attributes
//TODO add real instantiation
EventProducerServiceProvider mockProvider = _producersRegistry.getEventProducerServiceProvider("mock");
if (null == mockProvider)
{
throw new DatabusRuntimeException("relay event producer not available: " + "mock");
}
producer = mockProvider.createProducer(pConfig, schemaRegistryService,
dbusEventBuffer,
_inBoundStatsCollectors
.getStatsCollector(statsCollectorName),
maxScnReaderWriters);
} else if (uri.startsWith("gg:")){
producer = new GoldenGateEventProducer(pConfig,
schemaRegistryService,
dbusEventBuffer,
_inBoundStatsCollectors
.getStatsCollector(statsCollectorName),
maxScnReaderWriters);
} else if (uri.startsWith("mysql:")){
LOG.info("Adding OpenReplicatorEventProducer for uri :" + uri);
final String serviceName = "or";
EventProducerServiceProvider orProvider = _producersRegistry.getEventProducerServiceProvider(serviceName);
if (null == orProvider)
{
throw new DatabusRuntimeException("relay event producer not available: " + serviceName);
}
producer = orProvider.createProducer(pConfig, schemaRegistryService,
dbusEventBuffer,
_inBoundStatsCollectors.getStatsCollector(statsCollectorName),
maxScnReaderWriters);
} else
{
// Get all relevant pConfig attributes and initialize the nettyThreadPool objects
RelayEventProducer.DatabusClientNettyThreadPools nettyThreadPools =
new RelayEventProducer.DatabusClientNettyThreadPools(0,getNetworkTimeoutTimer(),getBossExecutorService(),
getIoExecutorService(), getHttpChannelGroup());
producer = new RelayEventProducer(pConfig, dbusEventBuffer,
_inBoundStatsCollectors
.getStatsCollector(statsCollectorName),
maxScnReaderWriters,nettyThreadPools);
}
// if a buffer for this partiton exists - we are overwriting it.
_producers.put(pPartition, producer);
plist.add(producer);
// append 'monitoring event producer'
if (producer instanceof OracleEventProducer) {
MonitoringEventProducer monitoringProducer = new MonitoringEventProducer(
"dbMonitor." + pPartition.toSimpleString(),
pConfig.getName(), pConfig.getUri(),
((OracleEventProducer) producer).getMonitoredSourceInfos(),
getMbeanServer());
_monitoringProducers.put(pPartition, monitoringProducer);
plist.add(monitoringProducer);
}
if (_csEventRequestProcessor == null)
_csEventRequestProcessor = new ControlSourceEventsRequestProcessor(
null, this, plist);
else
_csEventRequestProcessor.addEventProducers(plist);
RequestProcessorRegistry processorRegistry = getProcessorRegistry();
processorRegistry.reregister(
ControlSourceEventsRequestProcessor.COMMAND_NAME,
_csEventRequestProcessor);
}
public void initProducers() throws InvalidConfigException,
DatabusException, EventCreationException, UnsupportedKeyException,
SQLException, ProcessorRegistrationConflictException {
LOG.info("initializing producers");
for (PhysicalSourceStaticConfig pConfig : _pConfigs) {
addOneProducer(pConfig);
}
this.setDbPullerStart(_relayStaticConfig.getStartDbPuller());
LOG.info("done initializing producers");
}
/** get maxScnReaderWriters given a physical source **/
public MaxSCNReaderWriter getMaxSCNReaderWriter(PhysicalSourceStaticConfig pConfig)
{
try
{
MaxSCNReaderWriter maxScnReaderWriters = _maxScnReaderWriters
.getOrCreateHandler(pConfig.getPhysicalPartition());
return maxScnReaderWriters;
}
catch (DatabusException e)
{
LOG.warn("Cannot get maxScnReaderWriter for " + pConfig.getPhysicalPartition() + " error=" + e);
}
return null;
}
public static void main(String[] args) throws Exception
{
Cli cli = new Cli();
cli.processCommandLineArgs(args);
cli.parseRelayConfig();
// Process the startup properties and load configuration
PhysicalSourceStaticConfig[] pStaticConfigs = cli.getPhysicalSourceStaticConfigs();
HttpRelay.StaticConfig staticConfig = cli.getRelayConfigBuilder().build();
// Create and initialize the server instance
DatabusRelayMain serverContainer = new DatabusRelayMain(staticConfig, pStaticConfigs);
serverContainer.initProducers();
serverContainer.registerShutdownHook();
serverContainer.startAndBlock();
}
@Override
protected void doStart() {
super.doStart();
LOG.info("Starting. Producers are :" + _producers);
for (Entry<PhysicalPartition, EventProducer> entry : _producers
.entrySet()) {
EventProducer producer = entry.getValue();
// now start the default DB puller thread; depending on
// configuration setting / cmd line
if (this.getDbPullerStart()) {
if (producer != null) {
LOG.info("starting db puller: " + producer.getName());
producer.start(-1L);
LOG.info("db puller started: " + producer.getName());
}
}
}
}
@Override
public void pause() {
for (Entry<PhysicalPartition, EventProducer> entry : _producers
.entrySet()) {
EventProducer producer = entry.getValue();
if (null != producer) {
if (producer.isRunning()) {
producer.pause();
LOG.info("EventProducer :" + producer.getName()
+ " pause sent");
} else if (producer.isPaused()) {
LOG.info("EventProducer :" + producer.getName()
+ " already paused");
}
}
}
}
@Override
public void resume() {
for (Entry<PhysicalPartition, EventProducer> entry : _producers
.entrySet()) {
EventProducer producer = entry.getValue();
if (null != producer) {
if (producer.isPaused()) {
producer.unpause();
LOG.info("EventProducer :" + producer.getName()
+ " resume sent");
} else if (producer.isRunning()) {
LOG.info("EventProducer :" + producer.getName()
+ " already running");
}
}
}
}
@Override
protected void doShutdown() {
LOG.warn("Shutting down Relay!");
for (Entry<PhysicalPartition, EventProducer> entry : _producers
.entrySet()) {
PhysicalPartition pPartition = entry.getKey();
EventProducer producer = entry.getValue();
if (null != producer
&& (producer.isRunning() || producer.isPaused())) {
producer.shutdown();
try {
producer.waitForShutdown();
} catch (InterruptedException ie) {
}
LOG.info("EventProducer is shutdown!");
}
MonitoringEventProducer monitoringProducer = _monitoringProducers
.get(pPartition);
if (monitoringProducer != null) {
if (monitoringProducer.isRunning()) {
monitoringProducer.shutdown();
}
while (monitoringProducer.isRunning()
|| monitoringProducer.isPaused()) {
try {
monitoringProducer.waitForShutdown();
} catch (InterruptedException ie) {
}
}
monitoringProducer.unregisterMBeans();
}
}
super.doShutdown();
}
public EventProducer[] getProducers() {
EventProducer[] result = new EventProducer[_producers.size()];
_producers.values().toArray(result);
return result;
}
public MonitoringEventProducer[] getMonitoringProducers() {
MonitoringEventProducer[] result = new MonitoringEventProducer[_monitoringProducers
.size()];
_monitoringProducers.values().toArray(result);
return result;
}
@Override
public void awaitShutdown() {
super.awaitShutdown();
}
}