/*
* 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.nifi.amqp.processors;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.AMQP.Basic.RecoverOk;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.AMQP.Exchange.BindOk;
import com.rabbitmq.client.AMQP.Exchange.DeclareOk;
import com.rabbitmq.client.AMQP.Exchange.DeleteOk;
import com.rabbitmq.client.AMQP.Exchange.UnbindOk;
import com.rabbitmq.client.AMQP.Queue.PurgeOk;
import com.rabbitmq.client.AMQP.Tx.CommitOk;
import com.rabbitmq.client.AMQP.Tx.RollbackOk;
import com.rabbitmq.client.AMQP.Tx.SelectOk;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Command;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.FlowListener;
import com.rabbitmq.client.GetResponse;
import com.rabbitmq.client.Method;
import com.rabbitmq.client.ReturnListener;
import com.rabbitmq.client.ShutdownListener;
import com.rabbitmq.client.ShutdownSignalException;
/**
* Implementation of {@link Channel} to be used during testing
*/
class TestChannel implements Channel {
private final ExecutorService executorService;
private final Map<String, BlockingQueue<GetResponse>> enqueuedMessages;
private final Map<String, List<String>> routingKeyToQueueMappings;
private final Map<String, String> exchangeToRoutingKeyMappings;
private final List<ReturnListener> returnListeners;
private boolean open;
private boolean corrupted;
private Connection connection;
public TestChannel(Map<String, String> exchangeToRoutingKeyMappings,
Map<String, List<String>> routingKeyToQueueMappings) {
this.enqueuedMessages = new HashMap<>();
this.routingKeyToQueueMappings = routingKeyToQueueMappings;
if (this.routingKeyToQueueMappings != null) {
for (List<String> queues : routingKeyToQueueMappings.values()) {
for (String queue : queues) {
this.enqueuedMessages.put(queue, new ArrayBlockingQueue<GetResponse>(100));
}
}
}
this.exchangeToRoutingKeyMappings = exchangeToRoutingKeyMappings;
this.executorService = Executors.newCachedThreadPool();
this.returnListeners = new ArrayList<>();
this.open = true;
}
void corruptChannel() {
this.corrupted = true;
}
void setConnection(Connection connection) {
this.connection = connection;
}
@Override
public void addShutdownListener(ShutdownListener listener) {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void removeShutdownListener(ShutdownListener listener) {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public ShutdownSignalException getCloseReason() {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void notifyListeners() {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public boolean isOpen() {
return this.open;
}
@Override
public int getChannelNumber() {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public Connection getConnection() {
return this.connection;
}
@Override
public void close() throws IOException, TimeoutException {
this.open = false;
}
@Override
public void close(int closeCode, String closeMessage) throws IOException, TimeoutException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public boolean flowBlocked() {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void abort() throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void abort(int closeCode, String closeMessage) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void addReturnListener(ReturnListener listener) {
this.returnListeners.add(listener);
}
@Override
public boolean removeReturnListener(ReturnListener listener) {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void clearReturnListeners() {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void addFlowListener(FlowListener listener) {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public boolean removeFlowListener(FlowListener listener) {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void clearFlowListeners() {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void addConfirmListener(ConfirmListener listener) {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public boolean removeConfirmListener(ConfirmListener listener) {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void clearConfirmListeners() {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public Consumer getDefaultConsumer() {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void setDefaultConsumer(Consumer consumer) {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void basicQos(int prefetchSize, int prefetchCount, boolean global) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void basicQos(int prefetchCount, boolean global) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void basicQos(int prefetchCount) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
throws IOException {
this.basicPublish(exchange, routingKey, true, props, body);
}
@Override
public void basicPublish(final String exchange, final String routingKey, boolean mandatory,
final BasicProperties props, final byte[] body) throws IOException {
if (this.corrupted) {
throw new IOException("Channel is corrupted");
}
if (exchange.equals("")){ // default exchange; routingKey corresponds to a queue.
BlockingQueue<GetResponse> messages = this.getMessageQueue(routingKey);
GetResponse response = new GetResponse(null, props, body, messages.size());
messages.offer(response);
} else {
String rKey = this.exchangeToRoutingKeyMappings.get(exchange);
if (rKey.equals(routingKey)) {
List<String> queueNames = this.routingKeyToQueueMappings.get(routingKey);
if (queueNames == null || queueNames.isEmpty()) {
this.discard(exchange, routingKey, mandatory, props, body);
} else {
for (String queueName : queueNames) {
BlockingQueue<GetResponse> messages = this.getMessageQueue(queueName);
GetResponse response = new GetResponse(null, props, body, messages.size());
messages.offer(response);
}
}
} else {
this.discard(exchange, routingKey, mandatory, props, body);
}
}
}
private void discard(final String exchange, final String routingKey, boolean mandatory, final BasicProperties props,
final byte[] body) {
// NO ROUTE. Invoke return listener async
for (final ReturnListener listener : returnListeners) {
this.executorService.execute(new Runnable() {
@Override
public void run() {
try {
listener.handleReturn(-9, "Rejecting", exchange, routingKey, props, body);
} catch (Exception e) {
throw new IllegalStateException("Failed to send return message", e);
}
}
});
}
}
private BlockingQueue<GetResponse> getMessageQueue(String name) {
BlockingQueue<GetResponse> messages = this.enqueuedMessages.get(name);
if (messages == null) {
messages = new ArrayBlockingQueue<>(100);
this.enqueuedMessages.put(name, messages);
}
return messages;
}
@Override
public void basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate,
BasicProperties props, byte[] body) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public DeclareOk exchangeDeclare(String exchange, String type) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public DeclareOk exchangeDeclare(String exchange, String type, boolean durable) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public DeclareOk exchangeDeclare(String exchange, String type, boolean durable, boolean autoDelete,
Map<String, Object> arguments) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public DeclareOk exchangeDeclare(String exchange, String type, boolean durable, boolean autoDelete,
boolean internal, Map<String, Object> arguments) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void exchangeDeclareNoWait(String exchange, String type, boolean durable, boolean autoDelete,
boolean internal, Map<String, Object> arguments) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public DeclareOk exchangeDeclarePassive(String name) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public DeleteOk exchangeDelete(String exchange, boolean ifUnused) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void exchangeDeleteNoWait(String exchange, boolean ifUnused) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public DeleteOk exchangeDelete(String exchange) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public BindOk exchangeBind(String destination, String source, String routingKey) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public BindOk exchangeBind(String destination, String source, String routingKey, Map<String, Object> arguments)
throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void exchangeBindNoWait(String destination, String source, String routingKey, Map<String, Object> arguments)
throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public UnbindOk exchangeUnbind(String destination, String source, String routingKey) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public UnbindOk exchangeUnbind(String destination, String source, String routingKey, Map<String, Object> arguments)
throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void exchangeUnbindNoWait(String destination, String source, String routingKey,
Map<String, Object> arguments) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public com.rabbitmq.client.AMQP.Queue.DeclareOk queueDeclare() throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public com.rabbitmq.client.AMQP.Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive,
boolean autoDelete, Map<String, Object> arguments) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void queueDeclareNoWait(String queue, boolean durable, boolean exclusive, boolean autoDelete,
Map<String, Object> arguments) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public com.rabbitmq.client.AMQP.Queue.DeclareOk queueDeclarePassive(String queue) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public com.rabbitmq.client.AMQP.Queue.DeleteOk queueDelete(String queue) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public com.rabbitmq.client.AMQP.Queue.DeleteOk queueDelete(String queue, boolean ifUnused, boolean ifEmpty)
throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void queueDeleteNoWait(String queue, boolean ifUnused, boolean ifEmpty) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public com.rabbitmq.client.AMQP.Queue.BindOk queueBind(String queue, String exchange, String routingKey)
throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public com.rabbitmq.client.AMQP.Queue.BindOk queueBind(String queue, String exchange, String routingKey,
Map<String, Object> arguments) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void queueBindNoWait(String queue, String exchange, String routingKey, Map<String, Object> arguments)
throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public com.rabbitmq.client.AMQP.Queue.UnbindOk queueUnbind(String queue, String exchange, String routingKey)
throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public com.rabbitmq.client.AMQP.Queue.UnbindOk queueUnbind(String queue, String exchange, String routingKey,
Map<String, Object> arguments) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public PurgeOk queuePurge(String queue) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public GetResponse basicGet(String queue, boolean autoAck) throws IOException {
BlockingQueue<GetResponse> messages = this.enqueuedMessages.get(queue);
if (messages == null) {
throw new IOException("Queue is not defined");
} else {
return messages.poll();
}
}
@Override
public void basicAck(long deliveryTag, boolean multiple) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void basicNack(long deliveryTag, boolean multiple, boolean requeue) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void basicReject(long deliveryTag, boolean requeue) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public String basicConsume(String queue, Consumer callback) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public String basicConsume(String queue, boolean autoAck, Map<String, Object> arguments, Consumer callback)
throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public String basicConsume(String queue, boolean autoAck, String consumerTag, Consumer callback)
throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public String basicConsume(String queue, boolean autoAck, String consumerTag, boolean noLocal, boolean exclusive,
Map<String, Object> arguments, Consumer callback) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void basicCancel(String consumerTag) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public RecoverOk basicRecover() throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public RecoverOk basicRecover(boolean requeue) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public SelectOk txSelect() throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public CommitOk txCommit() throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public RollbackOk txRollback() throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public com.rabbitmq.client.AMQP.Confirm.SelectOk confirmSelect() throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public long getNextPublishSeqNo() {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public boolean waitForConfirms() throws InterruptedException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public boolean waitForConfirms(long timeout) throws InterruptedException, TimeoutException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void waitForConfirmsOrDie() throws IOException, InterruptedException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void waitForConfirmsOrDie(long timeout) throws IOException, InterruptedException, TimeoutException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public void asyncRpc(Method method) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public Command rpc(Method method) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public long messageCount(String queue) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
@Override
public long consumerCount(String queue) throws IOException {
throw new UnsupportedOperationException(
"This method is not currently supported as it is not used by current API in testing");
}
}