/* * Copyright 2012-2015, the original author or authors. * * 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. */ package com.flipkart.aesop.runtime.relay; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import org.trpr.platform.core.impl.logging.LogFactory; import org.trpr.platform.core.spi.logging.Logger; import com.flipkart.aesop.runtime.config.ProducerRegistration; import com.flipkart.aesop.runtime.metrics.MetricsCollector; import com.flipkart.aesop.runtime.producer.AbstractEventProducer; import com.flipkart.aesop.runtime.relay.netty.HttpRelayPipelineFactory; import com.linkedin.databus.container.netty.HttpRelay; import com.linkedin.databus.core.util.InvalidConfigException; import com.linkedin.databus2.core.DatabusException; import com.linkedin.databus2.core.seq.MultiServerSequenceNumberHandler; import com.linkedin.databus2.producers.EventProducer; import com.linkedin.databus2.relay.DatabusRelayMain; import com.linkedin.databus2.relay.config.PhysicalSourceStaticConfig; import com.linkedin.databus2.schemas.SchemaRegistryService; import com.linkedin.databus2.schemas.SourceIdNameRegistry; /** * The <code>DefaultRelay</code> class defines behavior of a default Databus Relay. Provides methods to register * one or more Databus Change Event producers. Also propagates all lifecycle commands on this Relay to all registered * event producers. This implementation is based on the Databus {@link DatabusRelayMain} source but differs in the following * aspects: * <pre><ul> * <li>Does not interpret producer implementations from URI pattern such as Oracle and GoldenGate</li> * <li>Does not start or otherwise interpret flags for the initializing the DB Puller</li> * </ul><pre> * * @author Regunath B * @version 1.0, 08 Jan 2014 */ public class DefaultRelay extends HttpRelay { /** Logger for this class*/ protected static final Logger LOGGER = LogFactory.getLogger(DefaultRelay.class); /** The SCN reader-writer*/ protected MultiServerSequenceNumberHandler maxScnReaderWriters; /** The ProducerRegistration list for the Relay*/ protected List<ProducerRegistration> producerRegistrationList = new ArrayList<ProducerRegistration>(); /** metrics collector */ private MetricsCollector metricsCollector; /** List of disconnected peers. We'll use a copy on write list to deal with concurrency. writes are low on this list*/ private List<String> disconnectedPeers = new CopyOnWriteArrayList<String>(); /** * Constructor for this class. Invokes constructor of the super-type with the passed-in arguments */ public DefaultRelay(StaticConfig config, PhysicalSourceStaticConfig[] pConfigs, SourceIdNameRegistry sourcesIdNameRegistry, SchemaRegistryService schemaRegistry) throws IOException, InvalidConfigException, DatabusException { super(config, pConfigs, sourcesIdNameRegistry, schemaRegistry); metricsCollector = new MetricsCollector(this); } /** * Overriden superclass method. Calls pause on the registered Producers after calling super.pause() * @see com.linkedin.databus.container.netty.HttpRelay#pause() */ public void pause() { super.pause(); for (ProducerRegistration producerRegistration : this.producerRegistrationList) { producerRegistration.getEventProducer().pause(); } } /** * Overriden superclass method. Calls resume on the registered Producers after calling super.resume() * @see com.linkedin.databus.container.netty.HttpRelay#resume() */ public void resume() { super.resume(); for (ProducerRegistration producerRegistration : this.producerRegistrationList) { producerRegistration.getEventProducer().unpause(); } } /** * Overriden superclass method. Calls shutdown on the registered Producers after calling super.suspendOnError() * @see com.linkedin.databus.container.netty.HttpRelay#suspendOnError(java.lang.Throwable) */ public void suspendOnError(Throwable cause) { super.suspendOnError(cause); for (ProducerRegistration producerRegistration : this.producerRegistrationList) { producerRegistration.getEventProducer().shutdown(); } } /** * Gets a list of all known connected peers * @return List of peer names */ public List<String> getPeers() { List<String> allPeers = getHttpStatisticsCollector().getPeers(); for (String disconnectedPeer : this.disconnectedPeers) { allPeers.remove(disconnectedPeer); } return allPeers; } /** * Informs this Relay of a peer connection * @param peer the connected peer */ public void firePeerConnect(String peer) { this.disconnectedPeers.remove(peer); LOGGER.debug("Disconnected peers in 'firePeerConnect'" + this.disconnectedPeers); } /** * Informs this Relay of a disconnected peer * @param peer the disconnected peer */ public void firePeerDisconnect(String peer) { this.disconnectedPeers.add(peer); LOGGER.debug("Disconnected peers in 'firePeerDisconnect'" + this.disconnectedPeers); } /** * Overriden superclass method. Creates and uses the Aesop {@link HttpRelayPipelineFactory} instead of the Databus * {@link com.linkedin.databus.container.netty.HttpRelayPipelineFactory} * @see com.linkedin.databus.container.netty.HttpRelay#initializeRelayNetworking() */ protected void initializeRelayNetworking() throws IOException, DatabusException { _httpBootstrap.setPipelineFactory(new HttpRelayPipelineFactory(this, _httpBootstrap.getPipelineFactory())); } /** * Overriden superclass method. Starts up the registered Producers after calling super.doStart() * @see com.linkedin.databus.container.netty.HttpRelay#doStart() */ protected void doStart() { super.doStart(); for (ProducerRegistration producerRegistration : this.producerRegistrationList) { EventProducer producer = producerRegistration.getEventProducer(); long startScn = -1; if (AbstractEventProducer.class.isAssignableFrom(producer.getClass())) { try { startScn = ((AbstractEventProducer)producer).getMaxScnReaderWriter().getMaxScn(); } catch(Exception e) { LOGGER.error("Error starting producer : '" + ((AbstractEventProducer)producer).getName() + "'. Producer not started.", e); continue; } } producer.start(startScn); } this.registerShutdownHook(); } /** * Overriden superclass method. Stops the registered Producers after calling super.doShutdown() * @see com.linkedin.databus.container.netty.HttpRelay#doShutdown() */ protected void doShutdown(){ LOGGER.info("Shutting down Relay"); for (ProducerRegistration producerRegistration : this.producerRegistrationList){ producerRegistration.getEventProducer().shutdown(); } LOGGER.info("All producers shutdown completed"); super.doShutdown(); LOGGER.info("Relay shutdown completed"); } /** Getter/Setter methods to override default implementations of various components used by this Relay*/ public MultiServerSequenceNumberHandler getMaxScnReaderWriters() { return this.maxScnReaderWriters; } public void setMaxScnReaderWriters( MultiServerSequenceNumberHandler maxScnReaderWriters) { this.maxScnReaderWriters = maxScnReaderWriters; } public void setProducerRegistrationList(List<ProducerRegistration> producerRegistrationList) { this.producerRegistrationList = producerRegistrationList; } public List<ProducerRegistration> getProducerRegistrationList() { return this.producerRegistrationList; } public MetricsCollector getMetricsCollector() { return metricsCollector; } }