Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**
* 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.activemq.artemis.core.server.federation.address;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.Message;
import org.apache.activemq.artemis.api.core.QueueConfiguration;
import org.apache.activemq.artemis.api.core.RoutingType;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.client.ClientSession;
import org.apache.activemq.artemis.core.config.WildcardConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationAddressPolicyConfiguration;
import org.apache.activemq.artemis.core.postoffice.Binding;
import org.apache.activemq.artemis.core.postoffice.QueueBinding;
import org.apache.activemq.artemis.core.postoffice.impl.DivertBinding;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
import org.apache.activemq.artemis.core.server.Queue;
import org.apache.activemq.artemis.core.server.federation.FederatedAbstract;
import org.apache.activemq.artemis.core.server.federation.FederatedConsumerKey;
import org.apache.activemq.artemis.core.server.federation.Federation;
import org.apache.activemq.artemis.core.server.federation.FederationUpstream;
import org.apache.activemq.artemis.core.server.impl.AddressInfo;
import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerAddressPlugin;
import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerBindingPlugin;
import org.apache.activemq.artemis.core.server.transformer.Transformer;
import org.apache.activemq.artemis.core.settings.impl.Match;
import org.apache.activemq.artemis.core.transaction.Transaction;
import org.apache.activemq.artemis.utils.ByteUtil;
/**
* Federated Address, replicate messages from the remote brokers address to itself.
*
* Only when a queue exists on the local broker do we replicate, this is to avoid un-needed replication
*
* All messages are replicated, this is on purpose so should a number queues exist with different filters
* we dont have have a consumer per queue filter.
*
*
*/
public class FederatedAddress extends FederatedAbstract implements ActiveMQServerBindingPlugin, ActiveMQServerAddressPlugin, Serializable {
public static final String FEDERATED_QUEUE_PREFIX = "federated";
public static final SimpleString HDR_HOPS = new SimpleString("_AMQ_Hops");
private final SimpleString queueNameFormat;
private final SimpleString filterString;
private final Set includes;
private final Set excludes;
private final FederationAddressPolicyConfiguration config;
private final Map> matchingDiverts = new HashMap<>();
public FederatedAddress(Federation federation, FederationAddressPolicyConfiguration config, ActiveMQServer server, FederationUpstream upstream) {
super(federation, server, upstream);
Objects.requireNonNull(config.getName());
this.config = config;
if (config.getMaxHops() == -1) {
this.filterString = null;
} else {
this.filterString = HDR_HOPS.concat(" IS NULL OR ").concat(HDR_HOPS).concat("<").concat(Integer.toString(config.getMaxHops()));
}
this.queueNameFormat = SimpleString.toSimpleString(FEDERATED_QUEUE_PREFIX + ".${federation}.${upstream}.${address}.${routeType}");
if (config.getIncludes().isEmpty()) {
includes = Collections.emptySet();
} else {
includes = new HashSet<>(config.getIncludes().size());
for (FederationAddressPolicyConfiguration.Matcher include : config.getIncludes()) {
includes.add(new Matcher(include, wildcardConfiguration));
}
}
if (config.getExcludes().isEmpty()) {
excludes = Collections.emptySet();
} else {
excludes = new HashSet<>(config.getExcludes().size());
for (FederationAddressPolicyConfiguration.Matcher exclude : config.getExcludes()) {
excludes.add(new Matcher(exclude, wildcardConfiguration));
}
}
}
@Override
public synchronized void start() {
if (!isStarted()) {
super.start();
server.getPostOffice()
.getAllBindings()
.filter(b -> b instanceof QueueBinding || b instanceof DivertBinding)
.forEach(this::afterAddBinding);
}
}
private void conditionalCreateRemoteConsumer(Queue queue) {
if (server.hasBrokerFederationPlugins()) {
final AtomicBoolean conditionalCreate = new AtomicBoolean(true);
try {
server.callBrokerFederationPlugins(plugin -> {
conditionalCreate.set(conditionalCreate.get() && plugin.federatedAddressConditionalCreateConsumer(queue));
});
} catch (ActiveMQException t) {
ActiveMQServerLogger.LOGGER.federationPluginExecutionError(t, "federatedAddressConditionalCreateConsumer");
throw new IllegalStateException(t.getMessage(), t.getCause());
}
if (!conditionalCreate.get()) {
return;
}
}
createRemoteConsumer(queue);
}
@Override
public void afterAddAddress(AddressInfo addressInfo, boolean reload) {
if (match(addressInfo)) {
try {
//Diverts can be added without the source address existing yet so
//if a new address is added we need to see if there are matching divert bindings
server.getPostOffice()
.getDirectBindings(addressInfo.getName())
.stream().filter(binding -> binding instanceof DivertBinding)
.forEach(this::afterAddBinding);
} catch (Exception e) {
ActiveMQServerLogger.LOGGER.federationBindingsLookupError(e, addressInfo.getName());
}
}
}
@Override
public void afterAddBinding(Binding binding) {
if (binding instanceof QueueBinding) {
conditionalCreateRemoteConsumer(((QueueBinding) binding).getQueue());
if (config.isEnableDivertBindings()) {
synchronized (this) {
for (Map.Entry> entry : matchingDiverts.entrySet()) {
//for each divert check the new QueueBinding to see if the divert matches and is not already tracking
if (!entry.getValue().contains(((QueueBinding) binding).getQueue().getName())) {
//conditionalCreateRemoteConsumer will check if the queue is a target of the divert before adding
conditionalCreateRemoteConsumer(entry.getKey(), entry.getValue(), (QueueBinding) binding);
}
}
}
}
} else if (config.isEnableDivertBindings() && binding instanceof DivertBinding) {
final DivertBinding divertBinding = (DivertBinding) binding;
final AddressInfo addressInfo = server.getPostOffice().getAddressInfo(binding.getAddress());
synchronized (this) {
if (match(addressInfo) && matchingDiverts.get(divertBinding) == null) {
final Set matchingQueues = new HashSet<>();
matchingDiverts.put(divertBinding, matchingQueues);
//find existing matching queue bindings for the divert to create consumers for
final SimpleString forwardAddress = divertBinding.getDivert().getForwardAddress();
try {
//create demand for each matching queue binding that isn't already tracked by the divert
//conditionalCreateRemoteConsumer will check if the queue is a target of the divert before adding
server.getPostOffice().getBindingsForAddress(forwardAddress).getBindings()
.stream().filter(b -> b instanceof QueueBinding).map(b -> (QueueBinding) b)
.forEach(queueBinding -> conditionalCreateRemoteConsumer(divertBinding, matchingQueues, queueBinding));
} catch (Exception e) {
ActiveMQServerLogger.LOGGER.federationBindingsLookupError(e, forwardAddress);
}
}
}
}
}
private void conditionalCreateRemoteConsumer(DivertBinding divertBinding, Set matchingQueues, QueueBinding queueBinding) {
if (server.hasBrokerFederationPlugins()) {
final AtomicBoolean conditionalCreate = new AtomicBoolean(true);
try {
server.callBrokerFederationPlugins(plugin -> {
conditionalCreate.set(conditionalCreate.get() && plugin.federatedAddressConditionalCreateDivertConsumer(divertBinding, queueBinding));
});
} catch (ActiveMQException t) {
ActiveMQServerLogger.LOGGER.federationPluginExecutionError(t, "federatedAddressConditionalCreateDivertConsumer");
throw new IllegalStateException(t.getMessage(), t.getCause());
}
if (!conditionalCreate.get()) {
return;
}
}
createRemoteConsumer(divertBinding, matchingQueues, queueBinding);
}
private void createRemoteConsumer(DivertBinding divertBinding, final Set matchingQueues, QueueBinding queueBinding) {
final AddressInfo addressInfo = server.getPostOffice().getAddressInfo(divertBinding.getAddress());
//If the divert address matches and if the new queueBinding matches the forwarding address of the divert
//then create a remote consumer if not already being tracked by the divert
if (match(addressInfo) && queueBinding.getAddress().equals(divertBinding.getDivert().getForwardAddress())
&& matchingQueues.add(queueBinding.getQueue().getName())) {
FederatedConsumerKey key = getKey(addressInfo);
Transformer transformer = getTransformer(config.getTransformerRef());
Transformer addHop = FederatedAddress::addHop;
createRemoteConsumer(key, mergeTransformers(addHop, transformer), clientSession -> createRemoteQueue(clientSession, key));
}
}
@Override
public void beforeRemoveBinding(SimpleString uniqueName, Transaction tx, boolean deleteData) {
final Binding binding = server.getPostOffice().getBinding(uniqueName);
if (binding instanceof QueueBinding) {
final Queue queue = ((QueueBinding) binding).getQueue();
//Remove any direct queue demand
removeRemoteConsumer(getKey(queue));
if (config.isEnableDivertBindings()) {
//See if there is any matching diverts that match this queue binding and remove demand now that
//the queue is going away
synchronized (this) {
matchingDiverts.entrySet().forEach(entry -> {
if (entry.getKey().getDivert().getForwardAddress().equals(queue.getAddress())) {
final AddressInfo addressInfo = server.getPostOffice().getAddressInfo(entry.getKey().getAddress());
//check if the queue has been tracked by this divert and if so remove the consumer
if (entry.getValue().remove(queue.getAddress())) {
removeRemoteConsumer(getKey(addressInfo));
}
}
});
}
}
} else if (config.isEnableDivertBindings() && binding instanceof DivertBinding) {
final DivertBinding divertBinding = (DivertBinding) binding;
final SimpleString forwardAddress = divertBinding.getDivert().getForwardAddress();
//Check if we have added this divert binding as a matching binding
//If we have then we need to look for any still existing queue bindings that map to this divert
//and remove consumers if they haven't already been removed
synchronized (this) {
final Set matchingQueues;
if ((matchingQueues = matchingDiverts.remove(binding)) != null) {
try {
final AddressInfo addressInfo = server.getPostOffice().getAddressInfo(binding.getAddress());
if (addressInfo != null) {
//remove queue binding demand if tracked by the divert
server.getPostOffice().getBindingsForAddress(forwardAddress)
.getBindings().stream().filter(b -> b instanceof QueueBinding && matchingQueues.remove(((QueueBinding) b).getQueue().getName()))
.forEach(queueBinding -> removeRemoteConsumer(getKey(addressInfo)));
}
} catch (Exception e) {
ActiveMQServerLogger.LOGGER.federationBindingsLookupError(e, forwardAddress);
}
}
}
}
}
public FederationAddressPolicyConfiguration getConfig() {
return config;
}
private void createRemoteConsumer(Queue queue) {
if (match(queue)) {
FederatedConsumerKey key = getKey(queue);
Transformer transformer = getTransformer(config.getTransformerRef());
Transformer addHop = FederatedAddress::addHop;
createRemoteConsumer(key, mergeTransformers(addHop, transformer), clientSession -> createRemoteQueue(clientSession, key));
}
}
private void createRemoteQueue(ClientSession clientSession, FederatedConsumerKey key) throws ActiveMQException {
if (!clientSession.queueQuery(key.getQueueName()).isExists()) {
clientSession.createQueue(new QueueConfiguration(key.getQueueName())
.setAddress(key.getAddress())
.setRoutingType(key.getRoutingType())
.setFilterString(key.getQueueFilterString())
.setDurable(true)
.setAutoDelete(config.getAutoDelete() == null ? true : config.getAutoDelete())
.setAutoDeleteDelay(config.getAutoDeleteDelay() == null ? TimeUnit.HOURS.toMillis(1) : config.getAutoDeleteDelay())
.setAutoDeleteMessageCount(config.getAutoDeleteMessageCount() == null ? -1 : config.getAutoDeleteMessageCount())
.setMaxConsumers(-1)
.setPurgeOnNoConsumers(false)
.setAutoCreated(false));
}
}
private boolean match(Queue queue) {
return match(queue.getAddress(), queue.getRoutingType());
}
private boolean match(AddressInfo addressInfo) {
return addressInfo != null ? match(addressInfo.getName(), addressInfo.getRoutingType()) : false;
}
private boolean match(SimpleString address, RoutingType routingType) {
//Currently only supporting Multicast currently.
if (RoutingType.ANYCAST.equals(routingType)) {
return false;
}
for (Matcher exclude : excludes) {
if (exclude.test(address.toString())) {
return false;
}
}
if (includes.isEmpty()) {
return true;
} else {
for (Matcher include : includes) {
if (include.test(address.toString())) {
return true;
}
}
return false;
}
}
private static Message addHop(Message message) {
if (message != null) {
int hops = toInt(message.getExtraBytesProperty(HDR_HOPS));
hops++;
message.putExtraBytesProperty(HDR_HOPS, ByteUtil.intToBytes(hops));
}
return message;
}
private static int toInt(byte[] bytes) {
if (bytes != null && bytes.length == 4) {
return ByteUtil.bytesToInt(bytes);
} else {
return 0;
}
}
private FederatedConsumerKey getKey(Queue queue) {
return new FederatedAddressConsumerKey(federation.getName(), upstream.getName(), queue.getAddress(), queue.getRoutingType(), queueNameFormat, filterString);
}
private FederatedConsumerKey getKey(AddressInfo address) {
return new FederatedAddressConsumerKey(federation.getName(), upstream.getName(), address.getName(), address.getRoutingType(), queueNameFormat, filterString);
}
public static class Matcher {
Predicate addressPredicate;
Matcher(FederationAddressPolicyConfiguration.Matcher config, WildcardConfiguration wildcardConfiguration) {
if (config.getAddressMatch() != null && !config.getAddressMatch().isEmpty()) {
addressPredicate = new Match<>(config.getAddressMatch(), null, wildcardConfiguration).getPattern().asPredicate();
}
}
public boolean test(String address) {
return addressPredicate == null || addressPredicate.test(address);
}
}
}