/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.camel.component.zookeeper;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.component.zookeeper.operations.AnyOfOperations;
import org.apache.camel.component.zookeeper.operations.ChildrenChangedOperation;
import org.apache.camel.component.zookeeper.operations.DataChangedOperation;
import org.apache.camel.component.zookeeper.operations.ExistenceChangedOperation;
import org.apache.camel.component.zookeeper.operations.ExistsOperation;
import org.apache.camel.component.zookeeper.operations.GetChildrenOperation;
import org.apache.camel.component.zookeeper.operations.GetDataOperation;
import org.apache.camel.component.zookeeper.operations.OperationResult;
import org.apache.camel.component.zookeeper.operations.ZooKeeperOperation;
import org.apache.camel.impl.DefaultConsumer;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.ZooKeeper;
/**
* <code>ZooKeeperConsumer</code> uses various {@link ZooKeeperOperation} to
* interact and consume data from a ZooKeeper cluster.
*/
@SuppressWarnings("rawtypes")
public class ZooKeeperConsumer extends DefaultConsumer {
private final ZooKeeperConnectionManager zkm;
private ZooKeeper connection;
private ZooKeeperConfiguration configuration;
private LinkedBlockingQueue<ZooKeeperOperation> operations = new LinkedBlockingQueue<ZooKeeperOperation>();
private ExecutorService executor;
private volatile boolean shuttingDown;
public ZooKeeperConsumer(ZooKeeperEndpoint endpoint, Processor processor) {
super(endpoint, processor);
this.zkm = endpoint.getConnectionManager();
this.configuration = endpoint.getConfiguration();
}
@Override
protected void doStart() throws Exception {
super.doStart();
connection = zkm.getConnection();
if (log.isDebugEnabled()) {
log.debug(String.format("Connected to Zookeeper cluster %s", configuration.getConnectString()));
}
initializeConsumer();
executor = getEndpoint().getCamelContext().getExecutorServiceManager().newFixedThreadPool(this, "Camel-Zookeeper OperationsExecutor", 1);
OperationsExecutor opsService = new OperationsExecutor();
executor.submit(opsService);
}
@Override
protected void doStop() throws Exception {
super.doStop();
shuttingDown = true;
if (log.isTraceEnabled()) {
log.trace(String.format("Shutting down zookeeper consumer of '%s'", configuration.getPath()));
}
getEndpoint().getCamelContext().getExecutorServiceManager().shutdown(executor);
zkm.shutdown();
}
private void initializeConsumer() {
String node = configuration.getPath();
if (configuration.isListChildren()) {
initializeChildListingConsumer(node);
} else {
initializeDataConsumer(node);
}
}
private void initializeDataConsumer(String node) {
if (!shuttingDown) {
if (log.isDebugEnabled()) {
log.debug(String.format("Initializing consumption of data on node '%s'", node));
}
addBasicDataConsumeSequence(node);
}
}
private void initializeChildListingConsumer(String node) {
if (!shuttingDown) {
if (log.isDebugEnabled()) {
log.debug(String.format("Initializing child listing of node '%s'", node));
}
addBasicChildListingSequence(node);
}
}
private Exchange createExchange(String path, OperationResult result, WatchedEvent watchedEvent) {
Exchange e = getEndpoint().createExchange();
ZooKeeperMessage in = new ZooKeeperMessage(path, result.getStatistics(), watchedEvent);
e.setIn(in);
if (result.isOk()) {
in.setBody(result.getResult());
} else {
e.setException(result.getException());
}
return e;
}
private class OperationsExecutor implements Runnable {
private ZooKeeperOperation current;
private WatchedEvent watchedEvent;
public void run() {
while (isRunAllowed()) {
try {
current = operations.take();
if (log.isTraceEnabled()) {
log.trace(String.format("Processing '%s' operation", current.getClass().getSimpleName()));
}
} catch (InterruptedException e) {
continue;
}
String node = current.getNode();
try {
OperationResult result = current.get();
if (ZooKeeperUtils.hasWatchedEvent(current)) {
watchedEvent = ZooKeeperUtils.getWatchedEvent(current);
}
if (result != null && current.shouldProduceExchange()) {
getProcessor().process(createExchange(node, result, watchedEvent));
watchedEvent = null;
}
} catch (Exception e) {
handleException(e);
backoffAndThenRestart();
} finally {
if (configuration.isRepeat()) {
try {
operations.offer(current.createCopy());
} catch (Exception e) {
backoffAndThenRestart();
}
}
}
}
}
private void backoffAndThenRestart() {
try {
if (isRunAllowed()) {
Thread.sleep(configuration.getBackoff());
initializeConsumer();
}
} catch (Exception e) {
// ignore
}
}
}
private void addBasicDataConsumeSequence(String node) {
operations.clear();
operations.add(new AnyOfOperations(node, new ExistsOperation(connection, node), new ExistenceChangedOperation(connection, node)));
operations.add(new GetDataOperation(connection, node));
operations.add(new DataChangedOperation(connection, node, false, configuration.isSendEmptyMessageOnDelete()));
}
private void addBasicChildListingSequence(String node) {
operations.clear();
operations.add(new AnyOfOperations(node, new ExistsOperation(connection, node), new ExistenceChangedOperation(connection, node)));
operations.add(new GetChildrenOperation(connection, node));
operations.add(new ChildrenChangedOperation(connection, node, false));
}
}