Merge 'ARTEMIS-2549'

This closes #2892
This commit is contained in:
Christopher L. Shannon (cshannon) 2019-11-20 10:10:38 -05:00
commit 2bf2dba8d3
74 changed files with 5109 additions and 282 deletions

View File

@ -677,6 +677,15 @@ public interface ActiveMQBuffer extends DataInput {
@Override
int readInt();
/**
* Gets a (potentially {@code null}) 32-bit integer at the current {@code readerIndex}
* and increases the {@code readerIndex} by {@code 4} in this buffer.
*
* @return a (potentially {@code null}) 32-bit integer at the current {@code readerIndex}
* @throws IndexOutOfBoundsException if {@code this.readableBytes} is less than {@code 4}
*/
Integer readNullableInt();
/**
* Gets an unsigned 32-bit integer at the current {@code readerIndex}
* and increases the {@code readerIndex} by {@code 4} in this buffer.
@ -696,6 +705,15 @@ public interface ActiveMQBuffer extends DataInput {
@Override
long readLong();
/**
* Gets a (potentially {@code null}) 64-bit integer at the current {@code readerIndex}
* and increases the {@code readerIndex} by {@code 8} in this buffer.
*
* @return a (potentially {@code null}) 64-bit integer at the current {@code readerIndex}
* @throws IndexOutOfBoundsException if {@code this.readableBytes} is less than {@code 8}
*/
Long readNullableLong();
/**
* Gets a char at the current {@code readerIndex}
* and increases the {@code readerIndex} by {@code 2} in this buffer.
@ -736,6 +754,15 @@ public interface ActiveMQBuffer extends DataInput {
@Override
boolean readBoolean();
/**
* Gets a (potentially {@code null}) boolean at the current {@code readerIndex}
* and increases the {@code readerIndex} by {@code 1} in this buffer.
*
* @return a (potentially {@code null}) boolean at the current {@code readerIndex}
* @throws IndexOutOfBoundsException if {@code this.readableBytes} is less than {@code 1}
*/
Boolean readNullableBoolean();
/**
* Gets a SimpleString (potentially {@code null}) at the current {@code readerIndex}
*
@ -917,6 +944,15 @@ public interface ActiveMQBuffer extends DataInput {
*/
void writeInt(int value);
/**
* Sets the specified (potentially {@code null}) 32-bit integer at the current {@code writerIndex}
* and increases the {@code writerIndex} by {@code 4} in this buffer.
*
* @param value The specified (potentially {@code null}) 32-bit integer
* @throws IndexOutOfBoundsException if {@code this.writableBytes} is less than {@code 4}
*/
void writeNullableInt(Integer value);
/**
* Sets the specified 64-bit long integer at the current
* {@code writerIndex} and increases the {@code writerIndex} by {@code 8}
@ -927,6 +963,16 @@ public interface ActiveMQBuffer extends DataInput {
*/
void writeLong(long value);
/**
* Sets the specified (potentially {@code null}) 64-bit long integer at the current
* {@code writerIndex} and increases the {@code writerIndex} by {@code 8}
* in this buffer.
*
* @param value The specified (potentially {@code null}) 64-bit long integer
* @throws IndexOutOfBoundsException if {@code this.writableBytes} is less than {@code 8}
*/
void writeNullableLong(Long value);
/**
* Sets the specified char at the current {@code writerIndex}
* and increases the {@code writerIndex} by {@code 2} in this buffer.
@ -961,6 +1007,13 @@ public interface ActiveMQBuffer extends DataInput {
*/
void writeBoolean(boolean val);
/**
* Sets the specified (potentially {@code null}) Boolean at the current {@code writerIndex}
*
* @param val The specified boolean
*/
void writeNullableBoolean(Boolean val);
/**
* Sets the specified SimpleString (potentially {@code null}) at the current {@code writerIndex}
*

View File

@ -64,6 +64,16 @@ public class ChannelBufferWrapper implements ActiveMQBuffer {
return readByte() != 0;
}
@Override
public Boolean readNullableBoolean() {
int b = readByte();
if (b == DataConstants.NULL) {
return null;
} else {
return readBoolean();
}
}
@Override
public SimpleString readNullableSimpleString() {
return SimpleString.readNullableSimpleString(buffer);
@ -125,6 +135,16 @@ public class ChannelBufferWrapper implements ActiveMQBuffer {
buffer.writeByte((byte) (val ? -1 : 0));
}
@Override
public void writeNullableBoolean(Boolean val) {
if (val == null) {
buffer.writeByte(DataConstants.NULL);
} else {
buffer.writeByte(DataConstants.NOT_NULL);
writeBoolean(val);
}
}
@Override
public void writeNullableSimpleString(final SimpleString val) {
SimpleString.writeNullableSimpleString(buffer, val);
@ -345,11 +365,31 @@ public class ChannelBufferWrapper implements ActiveMQBuffer {
return buffer.readInt();
}
@Override
public Integer readNullableInt() {
int b = readByte();
if (b == DataConstants.NULL) {
return null;
} else {
return readInt();
}
}
@Override
public long readLong() {
return buffer.readLong();
}
@Override
public Long readNullableLong() {
int b = readByte();
if (b == DataConstants.NULL) {
return null;
} else {
return readLong();
}
}
@Override
public short readShort() {
return buffer.readShort();
@ -565,11 +605,31 @@ public class ChannelBufferWrapper implements ActiveMQBuffer {
buffer.writeInt(value);
}
@Override
public void writeNullableInt(final Integer value) {
if (value == null) {
buffer.writeByte(DataConstants.NULL);
} else {
buffer.writeByte(DataConstants.NOT_NULL);
writeInt(value);
}
}
@Override
public void writeLong(final long value) {
buffer.writeLong(value);
}
@Override
public void writeNullableLong(Long value) {
if (value == null) {
buffer.writeByte(DataConstants.NULL);
} else {
buffer.writeByte(DataConstants.NOT_NULL);
writeLong(value);
}
}
@Override
public int writerIndex() {
return buffer.writerIndex();

View File

@ -572,6 +572,33 @@ public final class ActiveMQDefaultConfiguration {
public static final CriticalAnalyzerPolicy DEFAULT_ANALYZE_CRITICAL_POLICY = CriticalAnalyzerPolicy.LOG;
// The period (in milliseconds) used to check if the federation connection has failed to receive pings from another server
private static long DEFAULT_FEDERATION_FAILURE_CHECK_PERIOD = 30000;
// how long to keep a connection alive in the absence of any data arriving from the client
private static long DEFAULT_FEDERATION_CONNECTION_TTL = getDefaultConnectionTtl();
// How long to wait for a reply
private static long DEFAULT_FEDERATION_CALL_TIMEOUT = 30000;
// period (in ms) between successive retries
private static long DEFAULT_FEDERATION_RETRY_INTERVAL = 500;
// multiplier to apply to the retry-interval
private static double DEFAULT_FEDERATION_RETRY_INTERVAL_MULTIPLIER = getDefaultRetryIntervalMultiplier();
// Maximum value for retry-interval
private static long DEFAULT_FEDERATION_MAX_RETRY_INTERVAL = getDefaultMaxRetryInterval();
// How many attempts should be made to connect initially
private static int DEFAULT_FEDERATION_INITIAL_CONNECT_ATTEMPTS = -1;
// How many attempts should be made to reconnect after failure
private static int DEFAULT_FEDERATION_RECONNECT_ATTEMPTS = -1;
// How long to wait for a reply if in the middle of a fail-over. -1 means wait forever.
private static long DEFAULT_FEDERATION_CALL_FAILOVER_TIMEOUT = -1;
/**
* If true then the ActiveMQ Artemis Server will make use of any Protocol Managers that are in available on the classpath. If false then only the core protocol will be available, unless in Embedded mode where users can inject their own Protocol Managers.
*/
@ -1537,4 +1564,68 @@ public final class ActiveMQDefaultConfiguration {
public static long getDefaultRetryReplicationWait() {
return DEFAULT_RETRY_REPLICATION_WAIT;
}
/**
* The period (in milliseconds) used to check if the federation connection has failed to receive pings from another server
*/
public static long getDefaultFederationFailureCheckPeriod() {
return DEFAULT_FEDERATION_FAILURE_CHECK_PERIOD;
}
/**
* how long to keep a connection alive in the absence of any data arriving from the client
*/
public static long getDefaultFederationConnectionTtl() {
return DEFAULT_FEDERATION_CONNECTION_TTL;
}
/**
* How long to wait for a reply
*/
public static long getDefaultFederationCallTimeout() {
return DEFAULT_FEDERATION_CALL_TIMEOUT;
}
/**
* period (in ms) between successive retries
*/
public static long getDefaultFederationRetryInterval() {
return DEFAULT_FEDERATION_RETRY_INTERVAL;
}
/**
* multiplier to apply to the retry-interval
*/
public static double getDefaultFederationRetryIntervalMultiplier() {
return DEFAULT_FEDERATION_RETRY_INTERVAL_MULTIPLIER;
}
/**
* Maximum value for retry-interval
*/
public static long getDefaultFederationMaxRetryInterval() {
return DEFAULT_FEDERATION_MAX_RETRY_INTERVAL;
}
/**
* How many attempts should be made to connect initially
*/
public static int getDefaultFederationInitialConnectAttempts() {
return DEFAULT_FEDERATION_INITIAL_CONNECT_ATTEMPTS;
}
/**
* How many attempts should be made to reconnect after failure
*/
public static int getDefaultFederationReconnectAttempts() {
return DEFAULT_FEDERATION_RECONNECT_ATTEMPTS;
}
/**
* How long to wait for a reply if in the middle of a fail-over. -1 means wait forever.
*/
public static long getDefaultFederationCallFailoverTimeout() {
return DEFAULT_FEDERATION_CALL_FAILOVER_TIMEOUT;
}
}

View File

@ -226,6 +226,13 @@ public final class ResetLimitWrappedActiveMQBuffer extends ChannelBufferWrapper
super.writeBoolean(val);
}
@Override
public void writeNullableBoolean(final Boolean val) {
changed();
super.writeNullableBoolean(val);
}
@Override
public void writeByte(final byte value) {
changed();
@ -304,6 +311,13 @@ public final class ResetLimitWrappedActiveMQBuffer extends ChannelBufferWrapper
super.writeInt(value);
}
@Override
public void writeNullableInt(final Integer value) {
changed();
super.writeNullableInt(value);
}
@Override
public void writeLong(final long value) {
changed();
@ -311,6 +325,13 @@ public final class ResetLimitWrappedActiveMQBuffer extends ChannelBufferWrapper
super.writeLong(value);
}
@Override
public void writeNullableLong(final Long value) {
changed();
super.writeNullableLong(value);
}
@Override
public void writeNullableSimpleString(final SimpleString val) {
changed();

View File

@ -375,6 +375,16 @@ final class CompressedLargeMessageControllerImpl implements LargeMessageControll
}
}
@Override
public Integer readNullableInt() {
int b = readByte();
if (b == DataConstants.NULL) {
return null;
} else {
return readInt();
}
}
@Override
public long readUnsignedInt() {
return readInt() & 0xFFFFFFFFL;
@ -389,6 +399,16 @@ final class CompressedLargeMessageControllerImpl implements LargeMessageControll
}
}
@Override
public Long readNullableLong() {
int b = readByte();
if (b == DataConstants.NULL) {
return null;
} else {
return readLong();
}
}
@Override
public void readBytes(final byte[] dst, final int dstIndex, final int length) {
try {
@ -487,11 +507,21 @@ final class CompressedLargeMessageControllerImpl implements LargeMessageControll
throw new IllegalAccessError(OPERATION_NOT_SUPPORTED);
}
@Override
public void writeNullableInt(final Integer value) {
throw new IllegalAccessError(OPERATION_NOT_SUPPORTED);
}
@Override
public void writeLong(final long value) {
throw new IllegalAccessError(OPERATION_NOT_SUPPORTED);
}
@Override
public void writeNullableLong(final Long value) {
throw new IllegalAccessError(OPERATION_NOT_SUPPORTED);
}
@Override
public void writeBytes(final byte[] src, final int srcIndex, final int length) {
throw new IllegalAccessError(OPERATION_NOT_SUPPORTED);
@ -532,6 +562,16 @@ final class CompressedLargeMessageControllerImpl implements LargeMessageControll
return readByte() != 0;
}
@Override
public Boolean readNullableBoolean() {
int b = readByte();
if (b == DataConstants.NULL) {
return null;
} else {
return readBoolean();
}
}
@Override
public char readChar() {
return (char) readShort();
@ -624,6 +664,11 @@ final class CompressedLargeMessageControllerImpl implements LargeMessageControll
throw new IllegalAccessError(OPERATION_NOT_SUPPORTED);
}
@Override
public void writeNullableBoolean(final Boolean val) {
throw new IllegalAccessError(OPERATION_NOT_SUPPORTED);
}
@Override
public void writeChar(final char val) {
throw new IllegalAccessError(OPERATION_NOT_SUPPORTED);

View File

@ -744,6 +744,16 @@ public class LargeMessageControllerImpl implements LargeMessageController {
return getInt(pos);
}
@Override
public Integer readNullableInt() {
int b = readByte();
if (b == DataConstants.NULL) {
return null;
} else {
return readInt();
}
}
@Override
public long readUnsignedInt() {
return readInt() & 0xFFFFFFFFL;
@ -756,6 +766,16 @@ public class LargeMessageControllerImpl implements LargeMessageController {
return v;
}
@Override
public Long readNullableLong() {
int b = readByte();
if (b == DataConstants.NULL) {
return null;
} else {
return readLong();
}
}
@Override
public void readBytes(final byte[] dst, final int dstIndex, final int length) {
getBytes(readerIndex, dst, dstIndex, length);
@ -833,11 +853,21 @@ public class LargeMessageControllerImpl implements LargeMessageController {
throw new IllegalAccessError(LargeMessageControllerImpl.READ_ONLY_ERROR_MESSAGE);
}
@Override
public void writeNullableInt(final Integer value) {
throw new IllegalAccessError(LargeMessageControllerImpl.READ_ONLY_ERROR_MESSAGE);
}
@Override
public void writeLong(final long value) {
throw new IllegalAccessError(LargeMessageControllerImpl.READ_ONLY_ERROR_MESSAGE);
}
@Override
public void writeNullableLong(final Long value) {
throw new IllegalAccessError(LargeMessageControllerImpl.READ_ONLY_ERROR_MESSAGE);
}
@Override
public void writeBytes(final byte[] src, final int srcIndex, final int length) {
throw new IllegalAccessError(LargeMessageControllerImpl.READ_ONLY_ERROR_MESSAGE);
@ -915,6 +945,16 @@ public class LargeMessageControllerImpl implements LargeMessageController {
return readByte() != 0;
}
@Override
public Boolean readNullableBoolean() {
int b = readByte();
if (b == DataConstants.NULL) {
return null;
} else {
return readBoolean();
}
}
@Override
public char readChar() {
return (char) readShort();
@ -1008,6 +1048,11 @@ public class LargeMessageControllerImpl implements LargeMessageController {
throw new IllegalAccessError(LargeMessageControllerImpl.READ_ONLY_ERROR_MESSAGE);
}
@Override
public void writeNullableBoolean(final Boolean val) {
throw new IllegalAccessError(LargeMessageControllerImpl.READ_ONLY_ERROR_MESSAGE);
}
@Override
public void writeChar(final char val) {
throw new IllegalAccessError(LargeMessageControllerImpl.READ_ONLY_ERROR_MESSAGE);

View File

@ -22,6 +22,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.core.config.federation.FederationDownstreamConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationPolicy;
import org.apache.activemq.artemis.core.config.federation.FederationTransformerConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationUpstreamConfiguration;
@ -34,6 +37,8 @@ public class FederationConfiguration implements Serializable {
private List<FederationUpstreamConfiguration> upstreamConfigurations = new ArrayList<>();
private List<FederationDownstreamConfiguration> downstreamConfigurations = new ArrayList<>();
private Map<String, FederationPolicy> federationPolicyMap = new HashMap<>();
private Map<String, FederationTransformerConfiguration> transformerConfigurationMap = new HashMap<>();
@ -47,11 +52,28 @@ public class FederationConfiguration implements Serializable {
return this;
}
public List<FederationDownstreamConfiguration> getDownstreamConfigurations() {
return downstreamConfigurations;
}
public FederationConfiguration addDownstreamConfiguration(FederationDownstreamConfiguration federationDownstreamConfiguration) {
this.downstreamConfigurations.add(federationDownstreamConfiguration);
return this;
}
public FederationConfiguration addFederationPolicy(FederationPolicy federationPolicy) {
federationPolicyMap.put(federationPolicy.getName(), federationPolicy);
return this;
}
public void clearDownstreamConfigurations() {
this.downstreamConfigurations.clear();
}
public void clearUpstreamConfigurations() {
this.upstreamConfigurations.clear();
}
public Map<String, FederationPolicy> getFederationPolicyMap() {
return federationPolicyMap;
}
@ -121,6 +143,16 @@ public class FederationConfiguration implements Serializable {
public int hashCode() {
return Objects.hash(user, password);
}
public void encode(ActiveMQBuffer buffer) {
buffer.writeNullableString(user);
buffer.writeNullableString(password);
}
public void decode(ActiveMQBuffer buffer) {
user = buffer.readNullableString();
password = buffer.readNullableString();
}
}
@Override

View File

@ -21,6 +21,9 @@ import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.utils.Preconditions;
public class FederationAddressPolicyConfiguration implements FederationPolicy<FederationAddressPolicyConfiguration>, Serializable {
private String name;
@ -106,6 +109,55 @@ public class FederationAddressPolicyConfiguration implements FederationPolicy<Fe
return this;
}
@Override
public void encode(ActiveMQBuffer buffer) {
Preconditions.checkArgument(name != null, "name can not be null");
buffer.writeString(name);
buffer.writeNullableBoolean(autoDelete);
buffer.writeNullableLong(autoDeleteDelay);
buffer.writeNullableLong(autoDeleteMessageCount);
buffer.writeInt(maxHops);
buffer.writeNullableString(transformerRef);
encodeMatchers(buffer, includes);
encodeMatchers(buffer, excludes);
}
@Override
public void decode(ActiveMQBuffer buffer) {
name = buffer.readString();
autoDelete = buffer.readNullableBoolean();
autoDeleteDelay = buffer.readNullableLong();
autoDeleteMessageCount = buffer.readNullableLong();
maxHops = buffer.readInt();
transformerRef = buffer.readNullableString();
includes = new HashSet<>();
excludes = new HashSet<>();
decodeMatchers(buffer, includes);
decodeMatchers(buffer, excludes);
}
private void encodeMatchers(final ActiveMQBuffer buffer, final Set<Matcher> matchers) {
buffer.writeInt(matchers == null ? 0 : matchers.size());
if (matchers != null) {
for (Matcher matcher : matchers) {
matcher.encode(buffer);
}
}
}
private void decodeMatchers(final ActiveMQBuffer buffer, final Set<Matcher> matchers) {
final int size = buffer.readInt();
for (int i = 0; i < size; i++) {
Matcher matcher = new Matcher();
matcher.decode(buffer);
matchers.add(matcher);
}
}
public static class Matcher implements Serializable {
private String addressMatch;
@ -131,6 +183,15 @@ public class FederationAddressPolicyConfiguration implements FederationPolicy<Fe
public int hashCode() {
return Objects.hash(addressMatch);
}
public void encode(ActiveMQBuffer buffer) {
Preconditions.checkArgument(addressMatch != null, "addressMatch can not be null");
buffer.writeString(addressMatch);
}
public void decode(ActiveMQBuffer buffer) {
addressMatch = buffer.readString();
}
}
@Override

View File

@ -0,0 +1,275 @@
/*
* 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.config.federation;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
public class FederationConnectionConfiguration implements Serializable {
public static long DEFAULT_CIRCUIT_BREAKER_TIMEOUT = 30000;
private boolean isHA;
private String discoveryGroupName;
private List<String> staticConnectors;
private int priorityAdjustment;
private long circuitBreakerTimeout = DEFAULT_CIRCUIT_BREAKER_TIMEOUT;
private String username;
private String password;
private boolean shareConnection;
private long clientFailureCheckPeriod = ActiveMQDefaultConfiguration.getDefaultFederationFailureCheckPeriod();
private long connectionTTL = ActiveMQDefaultConfiguration.getDefaultFederationConnectionTtl();
private long retryInterval = ActiveMQDefaultConfiguration.getDefaultFederationRetryInterval();
private double retryIntervalMultiplier = ActiveMQDefaultConfiguration.getDefaultFederationRetryIntervalMultiplier();
private long maxRetryInterval = ActiveMQDefaultConfiguration.getDefaultFederationMaxRetryInterval();
private int initialConnectAttempts = ActiveMQDefaultConfiguration.getDefaultFederationInitialConnectAttempts();
private int reconnectAttempts = ActiveMQDefaultConfiguration.getDefaultFederationReconnectAttempts();
private long callTimeout = ActiveMQDefaultConfiguration.getDefaultFederationCallTimeout();
private long callFailoverTimeout = ActiveMQDefaultConfiguration.getDefaultFederationCallFailoverTimeout();
public String getDiscoveryGroupName() {
return discoveryGroupName;
}
public FederationConnectionConfiguration setDiscoveryGroupName(String discoveryGroupName) {
this.discoveryGroupName = discoveryGroupName;
return this;
}
public List<String> getStaticConnectors() {
return staticConnectors;
}
public FederationConnectionConfiguration setStaticConnectors(List<String> staticConnectors) {
this.staticConnectors = staticConnectors;
return this;
}
public boolean isHA() {
return isHA;
}
public FederationConnectionConfiguration setHA(boolean HA) {
isHA = HA;
return this;
}
public long getCircuitBreakerTimeout() {
return circuitBreakerTimeout;
}
public FederationConnectionConfiguration setCircuitBreakerTimeout(long circuitBreakerTimeout) {
this.circuitBreakerTimeout = circuitBreakerTimeout;
return this;
}
public String getUsername() {
return username;
}
public FederationConnectionConfiguration setUsername(String username) {
this.username = username;
return this;
}
public String getPassword() {
return password;
}
public FederationConnectionConfiguration setPassword(String password) {
this.password = password;
return this;
}
public int getPriorityAdjustment() {
return priorityAdjustment;
}
public FederationConnectionConfiguration setPriorityAdjustment(int priorityAdjustment) {
this.priorityAdjustment = priorityAdjustment;
return this;
}
public boolean isShareConnection() {
return shareConnection;
}
public FederationConnectionConfiguration setShareConnection(boolean shareConnection) {
this.shareConnection = shareConnection;
return this;
}
public long getClientFailureCheckPeriod() {
return clientFailureCheckPeriod;
}
public FederationConnectionConfiguration setClientFailureCheckPeriod(long clientFailureCheckPeriod) {
this.clientFailureCheckPeriod = clientFailureCheckPeriod;
return this;
}
public long getConnectionTTL() {
return connectionTTL;
}
public FederationConnectionConfiguration setConnectionTTL(long connectionTTL) {
this.connectionTTL = connectionTTL;
return this;
}
public long getRetryInterval() {
return retryInterval;
}
public FederationConnectionConfiguration setRetryInterval(long retryInterval) {
this.retryInterval = retryInterval;
return this;
}
public double getRetryIntervalMultiplier() {
return retryIntervalMultiplier;
}
public FederationConnectionConfiguration setRetryIntervalMultiplier(double retryIntervalMultiplier) {
this.retryIntervalMultiplier = retryIntervalMultiplier;
return this;
}
public long getMaxRetryInterval() {
return maxRetryInterval;
}
public FederationConnectionConfiguration setMaxRetryInterval(long maxRetryInterval) {
this.maxRetryInterval = maxRetryInterval;
return this;
}
public int getInitialConnectAttempts() {
return initialConnectAttempts;
}
public FederationConnectionConfiguration setInitialConnectAttempts(int initialConnectAttempts) {
this.initialConnectAttempts = initialConnectAttempts;
return this;
}
public int getReconnectAttempts() {
return reconnectAttempts;
}
public FederationConnectionConfiguration setReconnectAttempts(int reconnectAttempts) {
this.reconnectAttempts = reconnectAttempts;
return this;
}
public long getCallTimeout() {
return callTimeout;
}
public FederationConnectionConfiguration setCallTimeout(long callTimeout) {
this.callTimeout = callTimeout;
return this;
}
public long getCallFailoverTimeout() {
return callFailoverTimeout;
}
public FederationConnectionConfiguration setCallFailoverTimeout(long callFailoverTimeout) {
this.callFailoverTimeout = callFailoverTimeout;
return this;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
FederationConnectionConfiguration that = (FederationConnectionConfiguration) o;
return clientFailureCheckPeriod == that.clientFailureCheckPeriod &&
connectionTTL == that.connectionTTL &&
retryInterval == that.retryInterval &&
Double.compare(that.retryIntervalMultiplier, retryIntervalMultiplier) == 0 &&
maxRetryInterval == that.maxRetryInterval &&
initialConnectAttempts == that.initialConnectAttempts &&
reconnectAttempts == that.reconnectAttempts &&
callTimeout == that.callTimeout &&
callFailoverTimeout == that.callFailoverTimeout &&
isHA == that.isHA &&
priorityAdjustment == that.priorityAdjustment &&
circuitBreakerTimeout == that.circuitBreakerTimeout &&
shareConnection == that.shareConnection &&
Objects.equals(discoveryGroupName, that.discoveryGroupName) &&
Objects.equals(staticConnectors, that.staticConnectors) &&
Objects.equals(username, that.username) &&
Objects.equals(password, that.password);
}
@Override
public int hashCode() {
return Objects
.hash(clientFailureCheckPeriod, connectionTTL, retryInterval, retryIntervalMultiplier,
maxRetryInterval, initialConnectAttempts, reconnectAttempts, callTimeout,
callFailoverTimeout, isHA, discoveryGroupName, staticConnectors, priorityAdjustment,
circuitBreakerTimeout, username, password, shareConnection);
}
public void encode(ActiveMQBuffer buffer) {
buffer.writeNullableString(username);
buffer.writeNullableString(password);
buffer.writeBoolean(shareConnection);
buffer.writeInt(priorityAdjustment);
buffer.writeLong(clientFailureCheckPeriod);
buffer.writeLong(connectionTTL);
buffer.writeLong(retryInterval);
buffer.writeDouble(retryIntervalMultiplier);
buffer.writeLong(retryInterval);
buffer.writeInt(initialConnectAttempts);
buffer.writeInt(reconnectAttempts);
buffer.writeLong(callTimeout);
buffer.writeLong(callFailoverTimeout);
}
public void decode(ActiveMQBuffer buffer) {
username = buffer.readNullableString();
password = buffer.readNullableString();
shareConnection = buffer.readBoolean();
priorityAdjustment = buffer.readInt();
clientFailureCheckPeriod = buffer.readLong();
connectionTTL = buffer.readLong();
retryInterval = buffer.readLong();
retryIntervalMultiplier = buffer.readDouble();
maxRetryInterval = buffer.readLong();
initialConnectAttempts = buffer.readInt();
reconnectAttempts = buffer.readInt();
callTimeout = buffer.readLong();
callFailoverTimeout = buffer.readLong();
}
}

View File

@ -0,0 +1,96 @@
/*
* 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.config.federation;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.api.core.TransportConfiguration;
import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants;
public class FederationDownstreamConfiguration extends FederationStreamConfiguration<FederationDownstreamConfiguration> {
private String upstreamConfigurationRef;
private TransportConfiguration upstreamConfiguration;
public String getUpstreamConfigurationRef() {
return upstreamConfigurationRef;
}
public void setUpstreamConfigurationRef(String upstreamConfigurationRef) {
this.upstreamConfigurationRef = upstreamConfigurationRef;
}
public TransportConfiguration getUpstreamConfiguration() {
return upstreamConfiguration;
}
public void setUpstreamConfiguration(TransportConfiguration transportConfiguration) {
final Map<String, Object> params = new HashMap<>(transportConfiguration.getParams());
//clear any TLS settings as they won't apply to the federated server that uses this config
//The federated server that creates the upstream back will rely on its config from the acceptor for TLS
params.remove(TransportConstants.SSL_ENABLED_PROP_NAME);
params.remove(TransportConstants.SSL_PROVIDER);
params.remove(TransportConstants.SSL_KRB5_CONFIG_PROP_NAME);
params.remove(TransportConstants.KEYSTORE_PATH_PROP_NAME);
params.remove(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME);
params.remove(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME);
params.remove(TransportConstants.TRUSTSTORE_PATH_PROP_NAME);
params.remove(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME);
params.remove(TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME);
this.upstreamConfiguration = new TransportConfiguration(transportConfiguration.getFactoryClassName(), params,
transportConfiguration.getName(), transportConfiguration.getExtraParams());
}
@Override
public void encode(ActiveMQBuffer buffer) {
super.encode(buffer);
upstreamConfiguration.encode(buffer);
}
@Override
public void decode(ActiveMQBuffer buffer) {
super.decode(buffer);
upstreamConfiguration = new TransportConfiguration();
upstreamConfiguration.decode(buffer);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
FederationDownstreamConfiguration that = (FederationDownstreamConfiguration) o;
return Objects.equals(upstreamConfigurationRef, that.upstreamConfigurationRef) &&
Objects.equals(upstreamConfiguration, that.upstreamConfiguration);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), upstreamConfigurationRef, upstreamConfiguration);
}
}

View File

@ -16,9 +16,16 @@
*/
package org.apache.activemq.artemis.core.config.federation;
import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
public interface FederationPolicy<T> {
String getName();
T setName(String name);
void encode(ActiveMQBuffer buffer);
void decode(ActiveMQBuffer buffer);
}

View File

@ -22,6 +22,9 @@ import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.utils.Preconditions;
public class FederationPolicySet implements FederationPolicy<FederationPolicySet>, Serializable {
private String name;
@ -65,4 +68,29 @@ public class FederationPolicySet implements FederationPolicy<FederationPolicySet
public int hashCode() {
return Objects.hash(name, policyRefs);
}
@Override
public void encode(ActiveMQBuffer buffer) {
Preconditions.checkArgument(name != null, "name can not be null");
buffer.writeString(name);
buffer.writeInt(policyRefs == null ? 0 : policyRefs.size());
if (policyRefs != null) {
for (String policyRef : policyRefs) {
buffer.writeString(policyRef);
}
}
}
@Override
public void decode(ActiveMQBuffer buffer) {
name = buffer.readString();
final int policyRefsSize = buffer.readInt();
policyRefs = new HashSet<>();
for (int i = 0; i < policyRefsSize; i++) {
policyRefs.add(buffer.readString());
}
}
}

View File

@ -21,6 +21,9 @@ import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.utils.Preconditions;
public class FederationQueuePolicyConfiguration implements FederationPolicy<FederationQueuePolicyConfiguration>, Serializable {
private String name;
@ -86,6 +89,49 @@ public class FederationQueuePolicyConfiguration implements FederationPolicy<Fede
return this;
}
@Override
public void encode(ActiveMQBuffer buffer) {
Preconditions.checkArgument(name != null, "name can not be null");
buffer.writeString(name);
buffer.writeBoolean(includeFederated);
buffer.writeNullableInt(priorityAdjustment);
buffer.writeNullableString(transformerRef);
encodeMatchers(buffer, includes);
encodeMatchers(buffer, excludes);
}
@Override
public void decode(ActiveMQBuffer buffer) {
name = buffer.readString();
includeFederated = buffer.readBoolean();
priorityAdjustment = buffer.readNullableInt();
transformerRef = buffer.readNullableString();
includes = new HashSet<>();
excludes = new HashSet<>();
decodeMatchers(buffer, includes);
decodeMatchers(buffer, excludes);
}
private void encodeMatchers(final ActiveMQBuffer buffer, final Set<Matcher> matchers) {
buffer.writeInt(matchers == null ? 0 : matchers.size());
if (matchers != null) {
for (Matcher matcher : matchers) {
matcher.encode(buffer);
}
}
}
private void decodeMatchers(final ActiveMQBuffer buffer, final Set<Matcher> matchers) {
final int size = buffer.readInt();
for (int i = 0; i < size; i++) {
Matcher matcher = new Matcher();
matcher.decode(buffer);
matchers.add(matcher);
}
}
public static class Matcher implements Serializable {
private String queueMatch;
@ -122,6 +168,16 @@ public class FederationQueuePolicyConfiguration implements FederationPolicy<Fede
public int hashCode() {
return Objects.hash(queueMatch, addressMatch);
}
public void encode(ActiveMQBuffer buffer) {
buffer.writeNullableString(addressMatch);
buffer.writeNullableString(queueMatch);
}
public void decode(ActiveMQBuffer buffer) {
addressMatch = buffer.readNullableString();
queueMatch = buffer.readNullableString();
}
}
@Override

View File

@ -22,7 +22,9 @@ import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public class FederationUpstreamConfiguration implements Serializable {
import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
public abstract class FederationStreamConfiguration <T extends FederationStreamConfiguration<T>> implements Serializable {
private String name;
@ -33,23 +35,23 @@ public class FederationUpstreamConfiguration implements Serializable {
return name;
}
public FederationUpstreamConfiguration setName(String name) {
public T setName(String name) {
this.name = name;
return this;
return (T) this;
}
public Set<String> getPolicyRefs() {
return policyRefs;
}
public FederationUpstreamConfiguration addPolicyRef(String name) {
public T addPolicyRef(String name) {
policyRefs.add(name);
return this;
return (T) this;
}
public FederationUpstreamConfiguration addPolicyRefs(Collection<String> names) {
public T addPolicyRefs(Collection<String> names) {
policyRefs.addAll(names);
return this;
return (T) this;
}
public FederationConnectionConfiguration getConnectionConfiguration() {
@ -58,16 +60,35 @@ public class FederationUpstreamConfiguration implements Serializable {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof FederationUpstreamConfiguration)) return false;
FederationUpstreamConfiguration that = (FederationUpstreamConfiguration) o;
return Objects.equals(name, that.name) &&
Objects.equals(connectionConfiguration, that.connectionConfiguration) &&
Objects.equals(policyRefs, that.policyRefs);
if (this == o)
return true;
if (!(o instanceof FederationStreamConfiguration))
return false;
FederationStreamConfiguration that = (FederationStreamConfiguration) o;
return Objects.equals(name, that.name) && Objects.equals(connectionConfiguration, that.connectionConfiguration) && Objects.equals(policyRefs, that.policyRefs);
}
@Override
public int hashCode() {
return Objects.hash(name, connectionConfiguration, policyRefs);
}
public void encode(ActiveMQBuffer buffer) {
buffer.writeString(name);
connectionConfiguration.encode(buffer);
buffer.writeInt(policyRefs == null ? 0 : policyRefs.size());
for (String policyRef : policyRefs) {
buffer.writeString(policyRef);
}
}
public void decode(ActiveMQBuffer buffer) {
name = buffer.readString();
connectionConfiguration.decode(buffer);
int policyRefNum = buffer.readInt();
for (int i = 0; i < policyRefNum; i++) {
policyRefs.add(buffer.readString());
}
}
}

View File

@ -17,8 +17,12 @@
package org.apache.activemq.artemis.core.config.federation;
import java.io.Serializable;
import java.util.Map;
import java.util.Objects;
import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.core.config.TransformerConfiguration;
import org.apache.activemq.artemis.utils.Preconditions;
public class FederationTransformerConfiguration implements Serializable {
@ -26,6 +30,9 @@ public class FederationTransformerConfiguration implements Serializable {
private TransformerConfiguration transformerConfiguration;
public FederationTransformerConfiguration() {
}
public FederationTransformerConfiguration(String name, TransformerConfiguration transformerConfiguration) {
this.name = name;
this.transformerConfiguration = transformerConfiguration;
@ -52,4 +59,30 @@ public class FederationTransformerConfiguration implements Serializable {
public int hashCode() {
return Objects.hash(name, transformerConfiguration);
}
public void encode(ActiveMQBuffer buffer) {
Preconditions.checkArgument(name != null, "name can not be null");
Preconditions.checkArgument(transformerConfiguration != null, "transformerConfiguration can not be null");
buffer.writeString(name);
buffer.writeString(transformerConfiguration.getClassName());
buffer.writeInt(transformerConfiguration.getProperties() == null ? 0 : transformerConfiguration.getProperties().size());
if (transformerConfiguration.getProperties() != null) {
for (Map.Entry<String, String> entry : transformerConfiguration.getProperties().entrySet()) {
buffer.writeString(entry.getKey());
buffer.writeString(entry.getValue());
}
}
}
public void decode(ActiveMQBuffer buffer) {
name = buffer.readString();
transformerConfiguration = new TransformerConfiguration(buffer.readString());
final int propertiesSize = buffer.readInt();
for (int i = 0; i < propertiesSize; i++) {
transformerConfiguration.getProperties().put(buffer.readString(), buffer.readString());
}
}
}

View File

@ -0,0 +1,21 @@
/*
* 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.config.federation;
public class FederationUpstreamConfiguration extends FederationStreamConfiguration<FederationUpstreamConfiguration> {
}

View File

@ -64,6 +64,9 @@ public final class ChannelImpl implements Channel {
* cluster used for controlling nodes in a cluster remotely
*/
CLUSTER(3),
FEDERATION(4),
/**
* Channels [0-9] are reserved for the system, user channels must be greater than that.
*/

View File

@ -40,6 +40,7 @@ import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.Disconnect
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.DisconnectConsumerWithKillMessage;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.DisconnectMessage;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.DisconnectMessage_V2;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.FederationDownstreamConnectMessage;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.NullResponseMessage;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.NullResponseMessage_V2;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.PacketsConfirmedMessage;
@ -109,6 +110,7 @@ import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.DIS
import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.DISCONNECT_CONSUMER;
import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.DISCONNECT_V2;
import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.EXCEPTION;
import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.FEDERATION_DOWNSTREAM_CONNECT;
import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.NULL_RESPONSE;
import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.PACKETS_CONFIRMED;
import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.PING;
@ -471,6 +473,10 @@ public abstract class PacketDecoder implements Serializable {
packet = new DisconnectConsumerWithKillMessage();
break;
}
case FEDERATION_DOWNSTREAM_CONNECT: {
packet = new FederationDownstreamConnectMessage();
break;
}
default: {
throw ActiveMQClientMessageBundle.BUNDLE.invalidType(packetType);
}

View File

@ -277,6 +277,8 @@ public class PacketImpl implements Packet {
public static final byte SESS_BINDINGQUERY_RESP_V4 = -15;
public static final byte FEDERATION_DOWNSTREAM_CONNECT = -16;
// Static --------------------------------------------------------

View File

@ -0,0 +1,36 @@
/*
* 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.protocol.core.impl.wireformat;
import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.core.config.federation.FederationDownstreamConfiguration;
public class FederationDownstreamConnectMessage extends FederationStreamConnectMessage<FederationDownstreamConfiguration> {
public static final String UPSTREAM_SUFFIX = "-upstream";
public FederationDownstreamConnectMessage() {
super(FEDERATION_DOWNSTREAM_CONNECT);
}
@Override
protected FederationDownstreamConfiguration decodeStreamConfiguration(ActiveMQBuffer buffer) {
final FederationDownstreamConfiguration downstreamConfiguration = new FederationDownstreamConfiguration();
downstreamConfiguration.decode(buffer);
return downstreamConfiguration;
}
}

View File

@ -0,0 +1,156 @@
/*
* 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.protocol.core.impl.wireformat;
import java.util.HashMap;
import java.util.Map;
import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.core.config.FederationConfiguration.Credentials;
import org.apache.activemq.artemis.core.config.federation.FederationPolicy;
import org.apache.activemq.artemis.core.config.federation.FederationStreamConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationTransformerConfiguration;
import org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl;
import org.apache.activemq.artemis.utils.Preconditions;
public abstract class FederationStreamConnectMessage <T extends FederationStreamConfiguration> extends PacketImpl {
private String name;
private Credentials credentials;
private Map<String, FederationPolicy> federationPolicyMap = new HashMap<>();
private Map<String, FederationTransformerConfiguration> transformerConfigurationMap = new HashMap<>();
private T streamConfiguration;
public FederationStreamConnectMessage(final byte type) {
super(type);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Credentials getCredentials() {
return credentials;
}
public void setCredentials(
Credentials credentials) {
this.credentials = credentials;
}
public T getStreamConfiguration() {
return streamConfiguration;
}
public void setStreamConfiguration(T streamConfiguration) {
this.streamConfiguration = streamConfiguration;
}
public Map<String, FederationPolicy> getFederationPolicyMap() {
return federationPolicyMap;
}
public void setFederationPolicyMap(
Map<String, FederationPolicy> federationPolicyMap) {
this.federationPolicyMap = federationPolicyMap;
}
public Map<String, FederationTransformerConfiguration> getTransformerConfigurationMap() {
return transformerConfigurationMap;
}
public void setTransformerConfigurationMap(
Map<String, FederationTransformerConfiguration> transformerConfigurationMap) {
this.transformerConfigurationMap = transformerConfigurationMap;
}
@Override
public void encodeRest(ActiveMQBuffer buffer) {
Preconditions.checkNotNull(streamConfiguration);
super.encodeRest(buffer);
buffer.writeString(name);
if (credentials != null) {
buffer.writeBoolean(true);
credentials.encode(buffer);
} else {
buffer.writeBoolean(false);
}
buffer.writeInt(federationPolicyMap == null ? 0 : federationPolicyMap.size());
if (federationPolicyMap != null) {
for (FederationPolicy policy : federationPolicyMap.values()) {
buffer.writeString(policy.getClass().getName());
policy.encode(buffer);
}
}
buffer.writeInt(transformerConfigurationMap == null ? 0 : transformerConfigurationMap.size());
if (transformerConfigurationMap != null) {
for (FederationTransformerConfiguration transformerConfiguration : transformerConfigurationMap.values()) {
transformerConfiguration.encode(buffer);
}
}
streamConfiguration.encode(buffer);
}
@Override
public void decodeRest(ActiveMQBuffer buffer) {
super.decodeRest(buffer);
this.name = buffer.readString();
boolean hasCredentials = buffer.readBoolean();
if (hasCredentials) {
credentials = new Credentials();
credentials.decode(buffer);
}
int policySize = buffer.readInt();
for (int i = 0; i < policySize; i++) {
String clazz = buffer.readString();
FederationPolicy policy = getFederationPolicy(clazz);
policy.decode(buffer);
federationPolicyMap.put(policy.getName(), policy);
}
int transformerSize = buffer.readInt();
for (int i = 0; i < transformerSize; i++) {
FederationTransformerConfiguration transformerConfiguration
= new FederationTransformerConfiguration();
transformerConfiguration.decode(buffer);
transformerConfigurationMap.put(transformerConfiguration.getName(), transformerConfiguration);
}
streamConfiguration = decodeStreamConfiguration(buffer);
}
protected abstract T decodeStreamConfiguration(ActiveMQBuffer buffer);
private FederationPolicy getFederationPolicy(String clazz) {
try {
return (FederationPolicy) Class.forName(clazz).getConstructor(null).newInstance();
} catch (Exception e) {
throw new IllegalStateException("Error. Unable to instantiate FederationPolicy: " + e.getMessage(), e);
}
}
}

View File

@ -1,117 +0,0 @@
/*
* 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.config.federation;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;
public class FederationConnectionConfiguration implements Serializable {
public static long DEFAULT_CIRCUIT_BREAKER_TIMEOUT = 30000;
private boolean isHA;
private String discoveryGroupName;
private List<String> staticConnectors;
private int priorityAdjustment;
private long circuitBreakerTimeout = DEFAULT_CIRCUIT_BREAKER_TIMEOUT;
private String username;
private String password;
public String getDiscoveryGroupName() {
return discoveryGroupName;
}
public FederationConnectionConfiguration setDiscoveryGroupName(String discoveryGroupName) {
this.discoveryGroupName = discoveryGroupName;
return this;
}
public List<String> getStaticConnectors() {
return staticConnectors;
}
public FederationConnectionConfiguration setStaticConnectors(List<String> staticConnectors) {
this.staticConnectors = staticConnectors;
return this;
}
public boolean isHA() {
return isHA;
}
public FederationConnectionConfiguration setHA(boolean HA) {
isHA = HA;
return this;
}
public long getCircuitBreakerTimeout() {
return circuitBreakerTimeout;
}
public FederationConnectionConfiguration setCircuitBreakerTimeout(long circuitBreakerTimeout) {
this.circuitBreakerTimeout = circuitBreakerTimeout;
return this;
}
public String getUsername() {
return username;
}
public FederationConnectionConfiguration setUsername(String username) {
this.username = username;
return this;
}
public String getPassword() {
return password;
}
public FederationConnectionConfiguration setPassword(String password) {
this.password = password;
return this;
}
public int getPriorityAdjustment() {
return priorityAdjustment;
}
public FederationConnectionConfiguration setPriorityAdjustment(int priorityAdjustment) {
this.priorityAdjustment = priorityAdjustment;
return this;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof FederationConnectionConfiguration)) return false;
FederationConnectionConfiguration that = (FederationConnectionConfiguration) o;
return isHA == that.isHA &&
circuitBreakerTimeout == that.circuitBreakerTimeout &&
Objects.equals(discoveryGroupName, that.discoveryGroupName) &&
Objects.equals(staticConnectors, that.staticConnectors) &&
Objects.equals(priorityAdjustment, that.priorityAdjustment) &&
Objects.equals(username, that.username) &&
Objects.equals(password, that.password);
}
@Override
public int hashCode() {
return Objects.hash(isHA, discoveryGroupName, staticConnectors, priorityAdjustment, circuitBreakerTimeout, username, password);
}
}

View File

@ -57,8 +57,11 @@ import org.apache.activemq.artemis.core.config.ScaleDownConfiguration;
import org.apache.activemq.artemis.core.config.TransformerConfiguration;
import org.apache.activemq.artemis.core.config.WildcardConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationAddressPolicyConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationDownstreamConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationPolicySet;
import org.apache.activemq.artemis.core.config.federation.FederationQueuePolicyConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationStreamConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationTransformerConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationUpstreamConfiguration;
import org.apache.activemq.artemis.core.config.ha.ColocatedPolicyConfiguration;
import org.apache.activemq.artemis.core.config.ha.LiveOnlyPolicyConfiguration;
@ -2002,12 +2005,18 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
if (child.getNodeName().equals("upstream")) {
config.addUpstreamConfiguration(getUpstream((Element) child, mainConfig));
} else if (child.getNodeName().equals("downstream")) {
config.addDownstreamConfiguration(getDownstream((Element) child, mainConfig));
} else if (child.getNodeName().equals("policy-set")) {
config.addFederationPolicy(getPolicySet((Element)child, mainConfig));
} else if (child.getNodeName().equals("queue-policy")) {
config.addFederationPolicy(getQueuePolicy((Element)child, mainConfig));
} else if (child.getNodeName().equals("address-policy")) {
config.addFederationPolicy(getAddressPolicy((Element)child, mainConfig));
} else if (child.getNodeName().equals("transformer")) {
TransformerConfiguration transformerConfiguration = getTransformerConfiguration(child);
config.addTransformerConfiguration(new FederationTransformerConfiguration(
((Element)child).getAttribute("name"), transformerConfiguration));
}
}
@ -2128,9 +2137,8 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
return config;
}
private FederationUpstreamConfiguration getUpstream(Element upstreamNode, final Configuration mainConfig) throws Exception {
FederationUpstreamConfiguration config = new FederationUpstreamConfiguration();
private <T extends FederationStreamConfiguration> T getFederationStream(final T config, final Element upstreamNode,
final Configuration mainConfig) throws Exception {
String name = upstreamNode.getAttribute("name");
config.setName(name);
@ -2158,6 +2166,16 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
long circuitBreakerTimeout = getLong(upstreamNode, "circuit-breaker-timeout", config.getConnectionConfiguration().getCircuitBreakerTimeout(), Validators.MINUS_ONE_OR_GE_ZERO);
long clientFailureCheckPeriod = getLong(upstreamNode, "check-period", ActiveMQDefaultConfiguration.getDefaultFederationFailureCheckPeriod(), Validators.GT_ZERO);
long connectionTTL = getLong(upstreamNode, "connection-ttl", ActiveMQDefaultConfiguration.getDefaultFederationConnectionTtl(), Validators.GT_ZERO);
long retryInterval = getLong(upstreamNode, "retry-interval", ActiveMQDefaultConfiguration.getDefaultFederationRetryInterval(), Validators.GT_ZERO);
long callTimeout = getLong(upstreamNode, "call-timeout", ActiveMQClient.DEFAULT_CALL_TIMEOUT, Validators.GT_ZERO);
long callFailoverTimeout = getLong(upstreamNode, "call-failover-timeout", ActiveMQClient.DEFAULT_CALL_FAILOVER_TIMEOUT, Validators.MINUS_ONE_OR_GT_ZERO);
double retryIntervalMultiplier = getDouble(upstreamNode, "retry-interval-multiplier", ActiveMQDefaultConfiguration.getDefaultFederationRetryIntervalMultiplier(), Validators.GT_ZERO);
long maxRetryInterval = getLong(upstreamNode, "max-retry-interval", ActiveMQDefaultConfiguration.getDefaultFederationMaxRetryInterval(), Validators.GT_ZERO);
int initialConnectAttempts = getInteger(upstreamNode, "initial-connect-attempts", ActiveMQDefaultConfiguration.getDefaultFederationInitialConnectAttempts(), Validators.MINUS_ONE_OR_GE_ZERO);
int reconnectAttempts = getInteger(upstreamNode, "reconnect-attempts", ActiveMQDefaultConfiguration.getDefaultFederationReconnectAttempts(), Validators.MINUS_ONE_OR_GE_ZERO);
List<String> staticConnectorNames = new ArrayList<>();
String discoveryGroupName = null;
@ -2181,8 +2199,17 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
config.addPolicyRefs(policyRefs);
config.getConnectionConfiguration()
.setCircuitBreakerTimeout(circuitBreakerTimeout)
.setHA(ha);
.setCircuitBreakerTimeout(circuitBreakerTimeout)
.setHA(ha)
.setClientFailureCheckPeriod(clientFailureCheckPeriod)
.setConnectionTTL(connectionTTL)
.setRetryInterval(retryInterval)
.setRetryIntervalMultiplier(retryIntervalMultiplier)
.setMaxRetryInterval(maxRetryInterval)
.setInitialConnectAttempts(initialConnectAttempts)
.setReconnectAttempts(reconnectAttempts)
.setCallTimeout(callTimeout)
.setCallFailoverTimeout(callFailoverTimeout);
if (!staticConnectorNames.isEmpty()) {
config.getConnectionConfiguration().setStaticConnectors(staticConnectorNames);
@ -2192,6 +2219,20 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
return config;
}
private FederationUpstreamConfiguration getUpstream(final Element upstreamNode, final Configuration mainConfig) throws Exception {
return getFederationStream(new FederationUpstreamConfiguration(), upstreamNode, mainConfig);
}
private FederationDownstreamConfiguration getDownstream(final Element downstreamNode, final Configuration mainConfig) throws Exception {
final FederationDownstreamConfiguration downstreamConfiguration =
getFederationStream(new FederationDownstreamConfiguration(), downstreamNode, mainConfig);
final String upstreamRef = getString(downstreamNode,"upstream-connector-ref", null, Validators.NOT_NULL_OR_EMPTY);
downstreamConfiguration.setUpstreamConfigurationRef(upstreamRef);
return downstreamConfiguration;
}
private void getStaticConnectors(List<String> staticConnectorNames, Node child) {
NodeList children2 = ((Element) child).getElementsByTagName("connector-ref");

View File

@ -27,6 +27,7 @@ import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.BackupRequ
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.BackupResponseMessage;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.ClusterConnectMessage;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.ClusterConnectReplyMessage;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.FederationDownstreamConnectMessage;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.NodeAnnounceMessage;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.QuorumVoteMessage;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.QuorumVoteReplyMessage;
@ -59,6 +60,7 @@ import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.BAC
import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.BACKUP_REQUEST_RESPONSE;
import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.CLUSTER_CONNECT;
import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.CLUSTER_CONNECT_REPLY;
import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.FEDERATION_DOWNSTREAM_CONNECT;
import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.NODE_ANNOUNCE;
import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.QUORUM_VOTE;
import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.QUORUM_VOTE_REPLY;
@ -252,6 +254,10 @@ public class ServerPacketDecoder extends ClientPacketDecoder {
packet = new ScaleDownAnnounceMessage();
break;
}
case FEDERATION_DOWNSTREAM_CONNECT: {
packet = new FederationDownstreamConnectMessage();
break;
}
default: {
packet = super.decode(packetType, connection);
}

View File

@ -16,6 +16,7 @@
*/
package org.apache.activemq.artemis.core.protocol.core.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@ -27,6 +28,7 @@ import java.util.concurrent.RejectedExecutionException;
import io.netty.channel.ChannelPipeline;
import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.api.core.ActiveMQBuffers;
import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.BaseInterceptor;
import org.apache.activemq.artemis.api.core.Interceptor;
import org.apache.activemq.artemis.api.core.Pair;
@ -37,6 +39,12 @@ import org.apache.activemq.artemis.api.core.client.ActiveMQClient;
import org.apache.activemq.artemis.api.core.client.ClusterTopologyListener;
import org.apache.activemq.artemis.api.core.client.TopologyMember;
import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.config.FederationConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationConnectionConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationDownstreamConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationPolicy;
import org.apache.activemq.artemis.core.config.federation.FederationTransformerConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationUpstreamConfiguration;
import org.apache.activemq.artemis.core.protocol.ServerPacketDecoder;
import org.apache.activemq.artemis.core.protocol.core.Channel;
import org.apache.activemq.artemis.core.protocol.core.ChannelHandler;
@ -47,10 +55,12 @@ import org.apache.activemq.artemis.core.protocol.core.impl.ChannelImpl.CHANNEL_I
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.ClusterTopologyChangeMessage;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.ClusterTopologyChangeMessage_V2;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.ClusterTopologyChangeMessage_V3;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.FederationDownstreamConnectMessage;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.Ping;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SubscribeClusterTopologyUpdatesMessage;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SubscribeClusterTopologyUpdatesMessageV2;
import org.apache.activemq.artemis.core.remoting.CloseListener;
import org.apache.activemq.artemis.core.remoting.FailureListener;
import org.apache.activemq.artemis.core.remoting.impl.netty.ActiveMQFrameDecoder2;
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyServerConnection;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
@ -111,12 +121,15 @@ public class CoreProtocolManager implements ProtocolManager<Interceptor> {
}
@Override
public ConnectionEntry createConnectionEntry(final Acceptor acceptorUsed, final Connection connection) {
public ConnectionEntry createConnectionEntry(final Acceptor acceptorUsed,
final Connection connection) {
final Configuration config = server.getConfiguration();
Executor connectionExecutor = server.getExecutorFactory().getExecutor();
final CoreRemotingConnection rc = new RemotingConnectionImpl(new ServerPacketDecoder(), connection, incomingInterceptors, outgoingInterceptors, server.getNodeID(), connectionExecutor);
final CoreRemotingConnection rc = new RemotingConnectionImpl(new ServerPacketDecoder(),
connection, incomingInterceptors, outgoingInterceptors, server.getNodeID(),
connectionExecutor);
Channel channel1 = rc.getChannel(CHANNEL_ID.SESSION.id, -1);
@ -130,13 +143,19 @@ public class CoreProtocolManager implements ProtocolManager<Interceptor> {
ttl = config.getConnectionTTLOverride();
}
final ConnectionEntry entry = new ConnectionEntry(rc, connectionExecutor, System.currentTimeMillis(), ttl);
final ConnectionEntry entry = new ConnectionEntry(rc, connectionExecutor,
System.currentTimeMillis(), ttl);
final Channel channel0 = rc.getChannel(ChannelImpl.CHANNEL_ID.PING.id, -1);
channel0.setHandler(new LocalChannelHandler(config, entry, channel0, acceptorUsed, rc));
server.getClusterManager().addClusterChannelHandler(rc.getChannel(CHANNEL_ID.CLUSTER.id, -1), acceptorUsed, rc, server.getActivation());
server.getClusterManager()
.addClusterChannelHandler(rc.getChannel(CHANNEL_ID.CLUSTER.id, -1), acceptorUsed, rc,
server.getActivation());
final Channel federationChannel = rc.getChannel(CHANNEL_ID.FEDERATION.id, -1);
federationChannel.setHandler(new LocalChannelHandler(config, entry, channel0, acceptorUsed, rc));
return entry;
}
@ -249,18 +268,22 @@ public class CoreProtocolManager implements ProtocolManager<Interceptor> {
// Just send a ping back
channel0.send(packet);
} else if (packet.getType() == PacketImpl.SUBSCRIBE_TOPOLOGY || packet.getType() == PacketImpl.SUBSCRIBE_TOPOLOGY_V2) {
} else if (packet.getType() == PacketImpl.SUBSCRIBE_TOPOLOGY
|| packet.getType() == PacketImpl.SUBSCRIBE_TOPOLOGY_V2) {
SubscribeClusterTopologyUpdatesMessage msg = (SubscribeClusterTopologyUpdatesMessage) packet;
if (packet.getType() == PacketImpl.SUBSCRIBE_TOPOLOGY_V2) {
channel0.getConnection().setChannelVersion(((SubscribeClusterTopologyUpdatesMessageV2) msg).getClientVersion());
channel0.getConnection().setChannelVersion(
((SubscribeClusterTopologyUpdatesMessageV2) msg).getClientVersion());
}
final ClusterTopologyListener listener = new ClusterTopologyListener() {
@Override
public void nodeUP(final TopologyMember topologyMember, final boolean last) {
try {
final Pair<TransportConfiguration, TransportConfiguration> connectorPair = BackwardsCompatibilityUtils.checkTCPPairConversion(channel0.getConnection().getChannelVersion(), topologyMember);
final Pair<TransportConfiguration, TransportConfiguration> connectorPair = BackwardsCompatibilityUtils
.checkTCPPairConversion(
channel0.getConnection().getChannelVersion(), topologyMember);
final String nodeID = topologyMember.getNodeId();
// Using an executor as most of the notifications on the Topology
@ -270,11 +293,20 @@ public class CoreProtocolManager implements ProtocolManager<Interceptor> {
@Override
public void run() {
if (channel0.supports(PacketImpl.CLUSTER_TOPOLOGY_V3)) {
channel0.send(new ClusterTopologyChangeMessage_V3(topologyMember.getUniqueEventID(), nodeID, topologyMember.getBackupGroupName(), topologyMember.getScaleDownGroupName(), connectorPair, last));
channel0.send(new ClusterTopologyChangeMessage_V3(
topologyMember.getUniqueEventID(), nodeID,
topologyMember.getBackupGroupName(),
topologyMember.getScaleDownGroupName(), connectorPair,
last));
} else if (channel0.supports(PacketImpl.CLUSTER_TOPOLOGY_V2)) {
channel0.send(new ClusterTopologyChangeMessage_V2(topologyMember.getUniqueEventID(), nodeID, topologyMember.getBackupGroupName(), connectorPair, last));
channel0.send(new ClusterTopologyChangeMessage_V2(
topologyMember.getUniqueEventID(), nodeID,
topologyMember.getBackupGroupName(), connectorPair,
last));
} else {
channel0.send(new ClusterTopologyChangeMessage(nodeID, connectorPair, last));
channel0.send(
new ClusterTopologyChangeMessage(nodeID, connectorPair,
last));
}
}
});
@ -295,7 +327,9 @@ public class CoreProtocolManager implements ProtocolManager<Interceptor> {
@Override
public void run() {
if (channel0.supports(PacketImpl.CLUSTER_TOPOLOGY_V2)) {
channel0.send(new ClusterTopologyChangeMessage_V2(uniqueEventID, nodeID));
channel0.send(
new ClusterTopologyChangeMessage_V2(uniqueEventID,
nodeID));
} else {
channel0.send(new ClusterTopologyChangeMessage(nodeID));
}
@ -309,7 +343,8 @@ public class CoreProtocolManager implements ProtocolManager<Interceptor> {
@Override
public String toString() {
return "Remote Proxy on channel " + Integer.toHexString(System.identityHashCode(this));
return "Remote Proxy on channel " + Integer
.toHexString(System.identityHashCode(this));
}
};
@ -319,7 +354,8 @@ public class CoreProtocolManager implements ProtocolManager<Interceptor> {
rc.addCloseListener(new CloseListener() {
@Override
public void connectionClosed() {
acceptorUsed.getClusterConnection().removeClusterTopologyListener(listener);
acceptorUsed.getClusterConnection()
.removeClusterTopologyListener(listener);
}
});
} else {
@ -329,20 +365,120 @@ public class CoreProtocolManager implements ProtocolManager<Interceptor> {
@Override
public void run() {
String nodeId = server.getNodeID().toString();
Pair<TransportConfiguration, TransportConfiguration> emptyConfig = new Pair<>(null, null);
Pair<TransportConfiguration, TransportConfiguration> emptyConfig = new Pair<>(
null, null);
if (channel0.supports(PacketImpl.CLUSTER_TOPOLOGY_V2)) {
channel0.send(new ClusterTopologyChangeMessage_V2(System.currentTimeMillis(), nodeId, null, emptyConfig, true));
channel0.send(
new ClusterTopologyChangeMessage_V2(System.currentTimeMillis(),
nodeId, null, emptyConfig, true));
} else {
channel0.send(new ClusterTopologyChangeMessage(nodeId, emptyConfig, true));
channel0.send(
new ClusterTopologyChangeMessage(nodeId, emptyConfig, true));
}
}
});
}
} else if (packet.getType() == PacketImpl.FEDERATION_DOWNSTREAM_CONNECT) {
//If we receive this packet then a remote broker is requesting us to create federated upstream connection
//back to it which simulates a downstream connection
final FederationDownstreamConnectMessage message = (FederationDownstreamConnectMessage) packet;
final FederationDownstreamConfiguration downstreamConfiguration = message.getStreamConfiguration();
//Create a new Upstream Federation configuration based on the received Downstream connection message
//from the remote broker
//The idea here is to set all the same configuration parameters that apply to the upstream connection
final FederationConfiguration config = new FederationConfiguration();
config.setName(message.getName() + FederationDownstreamConnectMessage.UPSTREAM_SUFFIX);
config.setCredentials(message.getCredentials());
//Add the policy map configuration
for (FederationPolicy policy : message.getFederationPolicyMap().values()) {
config.addFederationPolicy(policy);
}
//Add any transformer configurations
for (FederationTransformerConfiguration transformerConfiguration : message.getTransformerConfigurationMap().values()) {
config.addTransformerConfiguration(transformerConfiguration);
}
//Create an upstream configuration with the same name but apply the upstream suffix so it is unique
final FederationUpstreamConfiguration upstreamConfiguration = new FederationUpstreamConfiguration()
.setName(downstreamConfiguration.getName() + FederationDownstreamConnectMessage.UPSTREAM_SUFFIX)
.addPolicyRefs(downstreamConfiguration.getPolicyRefs());
//Use the provided Transport Configuration information to create an upstream connection back to the broker that
//created the downstream connection
final TransportConfiguration upstreamConfig = downstreamConfiguration.getUpstreamConfiguration();
//Initialize the upstream transport with the config from the acceptor as this will apply
//relevant settings such as SSL, then override with settings from the downstream config
final Map<String, Object> params = new HashMap<>(acceptorUsed.getConfiguration());
params.putAll(upstreamConfig.getParams());
//Add the new upstream configuration that was created so we can connect back to the downstream server
final TransportConfiguration upstreamConf = new TransportConfiguration(
upstreamConfig.getFactoryClassName(), params, upstreamConfig.getName() + FederationDownstreamConnectMessage.UPSTREAM_SUFFIX,
new HashMap<>());
server.getConfiguration()
.addConnectorConfiguration(upstreamConf.getName() + FederationDownstreamConnectMessage.UPSTREAM_SUFFIX, upstreamConf);
//Create a new upstream connection config based on the downstream configuration
FederationConnectionConfiguration downstreamConConf = downstreamConfiguration.getConnectionConfiguration();
FederationConnectionConfiguration upstreamConConf = upstreamConfiguration.getConnectionConfiguration();
List<String> connectorNames = new ArrayList<>();
connectorNames.add(upstreamConf.getName() + FederationDownstreamConnectMessage.UPSTREAM_SUFFIX);
//Configure all of the upstream connection parameters from the downstream connection that are relevant
//Note that HA and discoveryGroupName are skipped because the downstream connection will manage that
//In this case we just want to create a connection back to the broker that sent the downstream packet.
//If this broker goes down then the original broker (if configured with HA) will re-establish a new
//connection to another broker which will then create another upstream, etc
upstreamConConf.setStaticConnectors(connectorNames);
upstreamConConf.setUsername(downstreamConConf.getUsername());
upstreamConConf.setPassword(downstreamConConf.getPassword());
upstreamConConf.setShareConnection(downstreamConConf.isShareConnection());
upstreamConConf.setPriorityAdjustment(downstreamConConf.getPriorityAdjustment());
upstreamConConf.setClientFailureCheckPeriod(downstreamConConf.getClientFailureCheckPeriod());
upstreamConConf.setConnectionTTL(downstreamConConf.getConnectionTTL());
upstreamConConf.setRetryInterval(downstreamConConf.getRetryInterval());
upstreamConConf.setRetryIntervalMultiplier(downstreamConConf.getRetryIntervalMultiplier());
upstreamConConf.setMaxRetryInterval(downstreamConConf.getMaxRetryInterval());
upstreamConConf.setInitialConnectAttempts(downstreamConConf.getInitialConnectAttempts());
upstreamConConf.setReconnectAttempts(downstreamConConf.getReconnectAttempts());
upstreamConConf.setCallTimeout(downstreamConConf.getCallTimeout());
upstreamConConf.setCallFailoverTimeout(downstreamConConf.getCallFailoverTimeout());
config.addUpstreamConfiguration(upstreamConfiguration);
//Register close and failure listeners, if the initial downstream connection goes down then we
//want to terminate the upstream connection
rc.addCloseListener(() -> {
server.getFederationManager().undeploy(config.getName());
});
rc.addFailureListener(new FailureListener() {
@Override
public void connectionFailed(ActiveMQException exception, boolean failedOver) {
server.getFederationManager().undeploy(config.getName());
}
@Override
public void connectionFailed(ActiveMQException exception, boolean failedOver,
String scaleDownTargetNodeID) {
server.getFederationManager().undeploy(config.getName());
}
});
try {
server.getFederationManager().deploy(config);
} catch (Exception e) {
logger.error("Error deploying federation: " + e.getMessage(), e);
}
}
}
private Pair<TransportConfiguration, TransportConfiguration> getPair(TransportConfiguration conn,
boolean isBackup) {
private Pair<TransportConfiguration, TransportConfiguration> getPair(
TransportConfiguration conn,
boolean isBackup) {
if (isBackup) {
return new Pair<>(null, conn);
}

View File

@ -16,6 +16,7 @@
*/
package org.apache.activemq.artemis.core.server;
import javax.management.MBeanServer;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
@ -25,14 +26,13 @@ import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.management.MBeanServer;
import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.RoutingType;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.config.BridgeConfiguration;
import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.config.DivertConfiguration;
import org.apache.activemq.artemis.core.config.FederationConfiguration;
import org.apache.activemq.artemis.core.management.impl.ActiveMQServerControlImpl;
import org.apache.activemq.artemis.core.paging.PagingManager;
import org.apache.activemq.artemis.core.persistence.OperationContext;
@ -46,7 +46,7 @@ import org.apache.activemq.artemis.core.security.SecurityAuth;
import org.apache.activemq.artemis.core.security.SecurityStore;
import org.apache.activemq.artemis.core.server.cluster.ClusterManager;
import org.apache.activemq.artemis.core.server.cluster.ha.HAPolicy;
import org.apache.activemq.artemis.core.config.FederationConfiguration;
import org.apache.activemq.artemis.core.server.federation.FederationManager;
import org.apache.activemq.artemis.core.server.group.GroupingHandler;
import org.apache.activemq.artemis.core.server.impl.Activation;
import org.apache.activemq.artemis.core.server.impl.AddressInfo;
@ -64,7 +64,6 @@ import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerCriticalPlug
import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerMessagePlugin;
import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerQueuePlugin;
import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerSessionPlugin;
import org.apache.activemq.artemis.core.server.federation.FederationManager;
import org.apache.activemq.artemis.core.server.reload.ReloadManager;
import org.apache.activemq.artemis.core.settings.HierarchicalRepository;
import org.apache.activemq.artemis.core.settings.impl.AddressSettings;
@ -749,5 +748,4 @@ public interface ActiveMQServer extends ServiceComponent {
void removeAddressInfo(SimpleString address, SecurityAuth auth, boolean force) throws Exception;
String getInternalNamingPrefix();
}

View File

@ -1638,7 +1638,19 @@ public interface ActiveMQServerLogger extends BasicLogger {
void federationAvoidStackOverflowPolicyRef(String upstreamName, String policyRef);
@LogMessage(level = Logger.Level.WARN)
@Message(id = 222282, value = "File {0} at {1} is empty. Delete the empty file to stop this message.",
@Message(id = 222282, value = "Federation downstream {0} upstream transport configuration ref {1} could not be resolved in federation configuration", format = Message.Format.MESSAGE_FORMAT)
void federationCantFindUpstreamConnector(String downstreamName, String upstreamRef);
@LogMessage(level = Logger.Level.INFO)
@Message(id = 222283, value = "Federation downstream {0} has been deployed", format = Message.Format.MESSAGE_FORMAT)
void federationDownstreamDeployed(String downstreamName);
@LogMessage(level = Logger.Level.INFO)
@Message(id = 222284, value = "Federation downstream {0} has been undeployed", format = Message.Format.MESSAGE_FORMAT)
void federationDownstreamUnDeployed(String downstreamName);
@LogMessage(level = Logger.Level.WARN)
@Message(id = 222285, value = "File {0} at {1} is empty. Delete the empty file to stop this message.",
format = Message.Format.MESSAGE_FORMAT)
void emptyAddressFile(String addressFile, String directory);

View File

@ -20,15 +20,16 @@ package org.apache.activemq.artemis.core.server.federation;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException;
import org.apache.activemq.artemis.api.core.Message;
import org.apache.activemq.artemis.api.core.client.ClientConsumer;
import org.apache.activemq.artemis.api.core.client.ClientMessage;
import org.apache.activemq.artemis.api.core.client.ClientSession;
import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
import org.apache.activemq.artemis.api.core.client.MessageHandler;
import org.apache.activemq.artemis.api.core.client.SessionFailureListener;
import org.apache.activemq.artemis.core.client.impl.ClientSessionFactoryInternal;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.transformer.Transformer;
@ -47,7 +48,7 @@ public class FederatedQueueConsumer implements MessageHandler, SessionFailureLis
private final int intialConnectDelayMax = 30;
private final ClientSessionCallback clientSessionCallback;
private ClientSessionFactory clientSessionFactory;
private ClientSessionFactoryInternal clientSessionFactory;
private ClientSession clientSession;
private ClientConsumer clientConsumer;
@ -83,7 +84,7 @@ public class FederatedQueueConsumer implements MessageHandler, SessionFailureLis
}, delay, TimeUnit.SECONDS);
}
static int getNextDelay(int delay, int delayMultiplier, int delayMax) {
public static int getNextDelay(int delay, int delayMultiplier, int delayMax) {
int nextDelay;
if (delay == 0) {
nextDelay = 1;
@ -100,7 +101,7 @@ public class FederatedQueueConsumer implements MessageHandler, SessionFailureLis
try {
if (clientConsumer == null) {
synchronized (this) {
this.clientSessionFactory = upstream.getConnection().clientSessionFactory();
this.clientSessionFactory = (ClientSessionFactoryInternal) upstream.getConnection().clientSessionFactory();
this.clientSession = clientSessionFactory.createSession(upstream.getUser(), upstream.getPassword(), false, true, true, clientSessionFactory.getServerLocator().isPreAcknowledge(), clientSessionFactory.getServerLocator().getAckBatchSize());
this.clientSession.addFailureListener(this);
this.clientSession.addMetaData(FEDERATION_NAME, federation.getName().toString());
@ -149,13 +150,14 @@ public class FederatedQueueConsumer implements MessageHandler, SessionFailureLis
if (clientSession != null) {
clientSession.close();
}
if (clientSessionFactory != null) {
clientSessionFactory.close();
}
clientConsumer = null;
clientSession = null;
clientSessionFactory = null;
if (clientSessionFactory != null && (!upstream.getConnection().isSharedConnection() ||
clientSessionFactory.numSessions() == 0)) {
clientSessionFactory.close();
clientSessionFactory = null;
}
}
@Override

View File

@ -20,9 +20,11 @@ package org.apache.activemq.artemis.core.server.federation;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.config.FederationConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationDownstreamConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationPolicy;
import org.apache.activemq.artemis.core.config.federation.FederationUpstreamConfiguration;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
@ -34,9 +36,12 @@ public class Federation {
private final SimpleString name;
private final Map<String, FederationUpstream> upstreams = new HashMap<>();
private final Map<String, FederationDownstream> downstreams = new HashMap<>();
private final FederationConfiguration config;
private FederationManager.State state;
private final Map<String, FederationConnection> connections = new HashMap<>();
enum State {
STOPPED,
STOPPING,
@ -64,6 +69,9 @@ public class Federation {
for (FederationUpstream connection : upstreams.values()) {
connection.start();
}
for (FederationDownstream connection : downstreams.values()) {
connection.start();
}
state = FederationManager.State.STARTED;
}
@ -74,7 +82,11 @@ public class Federation {
for (FederationUpstream connection : upstreams.values()) {
connection.stop();
}
for (FederationDownstream connection : downstreams.values()) {
connection.stop();
}
upstreams.clear();
downstreams.clear();
state = FederationManager.State.STOPPED;
}
@ -82,6 +94,9 @@ public class Federation {
for (FederationUpstreamConfiguration upstreamConfiguration : config.getUpstreamConfigurations()) {
deploy(upstreamConfiguration, config.getFederationPolicyMap());
}
for (FederationDownstreamConfiguration downstreamConfiguration : config.getDownstreamConfigurations()) {
deploy(downstreamConfiguration, config.getFederationPolicyMap());
}
if (state != FederationManager.State.STARTED) {
state = FederationManager.State.DEPLOYED;
}
@ -96,11 +111,14 @@ public class Federation {
if (federationConnection != null) {
federationConnection.stop();
}
FederationDownstream federationConnectionDownstream = downstreams.remove(name);
if (federationConnectionDownstream != null) {
federationConnectionDownstream.undeploy();
federationConnectionDownstream.stop();
}
return true;
}
public synchronized boolean deploy(FederationUpstreamConfiguration upstreamConfiguration, Map<String, FederationPolicy> federationPolicyMap) throws ActiveMQException {
String name = upstreamConfiguration.getName();
FederationUpstream upstream = upstreams.get(name);
@ -127,11 +145,52 @@ public class Federation {
return upstream;
}
public synchronized boolean deploy(FederationDownstreamConfiguration downstreamConfiguration, Map<String, FederationPolicy> federationPolicyMap) throws ActiveMQException {
String name = downstreamConfiguration.getName();
FederationDownstream downstream = downstreams.get(name);
//If connection has changed we will need to do a full undeploy and redeploy.
if (downstream == null) {
undeploy(name);
downstream = deploy(name, downstreamConfiguration);
} else if (!downstream.getConnection().getConfig().equals(downstreamConfiguration.getConnectionConfiguration())) {
undeploy(name);
downstream = deploy(name, downstreamConfiguration);
}
downstream.deploy(config);
return true;
}
private synchronized FederationDownstream deploy(String name, FederationDownstreamConfiguration downstreamConfiguration) {
//If we have a matching upstream connection already configured then use it for the initiating downstream connection
FederationConnection connection = null;
if (downstreamConfiguration.getConnectionConfiguration().isShareConnection()) {
for (FederationUpstream upstream : upstreams.values()) {
if (upstream.getConfig().getConnectionConfiguration()
.equals(downstreamConfiguration.getConnectionConfiguration())) {
connection = upstream.getConnection();
connection.setSharedConnection(true);
break;
}
}
}
FederationDownstream downstream = new FederationDownstream(server, this, name, downstreamConfiguration, connection);
downstreams.put(name, downstream);
if (state == FederationManager.State.STARTED) {
downstream.start();
}
return downstream;
}
public FederationUpstream get(String name) {
return upstreams.get(name);
}
public FederationDownstream getDownstream(String name) {
return downstreams.get(name);
}
public void register(FederatedAbstract federatedAbstract) {
server.registerBrokerPlugin(federatedAbstract);

View File

@ -34,6 +34,7 @@ public class FederationConnection {
private final long circuitBreakerTimeout;
private volatile ClientSessionFactory clientSessionFactory;
private volatile boolean started;
private volatile boolean sharedConnection;
public FederationConnection(Configuration configuration, String name, FederationConnectionConfiguration config) {
this.config = config;
@ -67,6 +68,17 @@ public class FederationConnection {
serverLocator = ActiveMQClient.createServerLocatorWithoutHA(tcConfigs);
}
}
serverLocator.setConnectionTTL(config.getConnectionTTL());
serverLocator.setClientFailureCheckPeriod(config.getClientFailureCheckPeriod());
serverLocator.setReconnectAttempts(config.getReconnectAttempts());
serverLocator.setInitialConnectAttempts(config.getInitialConnectAttempts());
serverLocator.setRetryInterval(config.getRetryInterval());
serverLocator.setRetryIntervalMultiplier(config.getRetryIntervalMultiplier());
serverLocator.setMaxRetryInterval(config.getMaxRetryInterval());
serverLocator.setCallTimeout(config.getCallTimeout());
serverLocator.setCallFailoverTimeout(config.getCallFailoverTimeout());
}
public synchronized void start() {
@ -87,6 +99,14 @@ public class FederationConnection {
return started;
}
public boolean isSharedConnection() {
return sharedConnection;
}
public void setSharedConnection(boolean sharedConnection) {
this.sharedConnection = sharedConnection;
}
public final ClientSessionFactory clientSessionFactory() throws Exception {
ClientSessionFactory clientSessionFactory = this.clientSessionFactory;
if (started) {

View File

@ -0,0 +1,212 @@
/**
* 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;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.ActiveMQExceptionType;
import org.apache.activemq.artemis.api.core.TransportConfiguration;
import org.apache.activemq.artemis.api.core.client.SessionFailureListener;
import org.apache.activemq.artemis.core.client.impl.ClientSessionFactoryInternal;
import org.apache.activemq.artemis.core.client.impl.ClientSessionInternal;
import org.apache.activemq.artemis.core.config.FederationConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationDownstreamConfiguration;
import org.apache.activemq.artemis.core.protocol.core.Channel;
import org.apache.activemq.artemis.core.protocol.core.CoreRemotingConnection;
import org.apache.activemq.artemis.core.protocol.core.impl.ChannelImpl.CHANNEL_ID;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.FederationDownstreamConnectMessage;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.FederationStreamConnectMessage;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
import org.jboss.logging.Logger;
public class FederationDownstream extends FederationStream implements SessionFailureListener {
private static final Logger logger = Logger.getLogger(FederationDownstream.class);
private FederationDownstreamConfiguration config;
private ClientSessionFactoryInternal clientSessionFactory;
private ClientSessionInternal clientSession;
private Channel channel;
private AtomicBoolean initialized = new AtomicBoolean();
private final ScheduledExecutorService scheduledExecutorService;
private final int intialConnectDelayMultiplier = 2;
private final int intialConnectDelayMax = 30;
public static final String FEDERATION_DOWNSTREAM_NAME = "federation-downstream-name";
private AtomicBoolean started = new AtomicBoolean();
private FederationConfiguration federationConfiguration;
public FederationDownstream(ActiveMQServer server, Federation federation, String name, FederationDownstreamConfiguration config,
final FederationConnection connection) {
super(server, federation, name, config, connection);
this.config = config;
this.scheduledExecutorService = server.getScheduledPool();
}
@Override
public synchronized void start() {
super.start();
try {
deploy(federationConfiguration);
} catch (ActiveMQException e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
@Override
public synchronized void stop() {
super.stop();
}
public void deploy(FederationConfiguration federationConfiguration)
throws ActiveMQException {
this.federationConfiguration = federationConfiguration;
if (connection.isStarted() && started.compareAndSet(false, true)) {
final FederationStreamConnectMessage message = new FederationDownstreamConnectMessage();
message.setName(federationConfiguration.getName());
message.setCredentials(federationConfiguration.getCredentials());
message.setStreamConfiguration(config);
message.setFederationPolicyMap(federationConfiguration.getFederationPolicyMap());
message.setTransformerConfigurationMap(federationConfiguration.getTransformerConfigurationMap());
if (config.getUpstreamConfigurationRef() != null
&& config.getUpstreamConfiguration() == null) {
TransportConfiguration[] configs = server.getConfiguration()
.getTransportConfigurations(config.getUpstreamConfigurationRef());
if (configs != null && configs.length > 0) {
config.setUpstreamConfiguration(configs[0]);
} else {
ActiveMQServerLogger.LOGGER.federationCantFindUpstreamConnector(config.getName(), config
.getUpstreamConfigurationRef());
throw new ActiveMQException("Could not locate upstream transport configuration for federation downstream: " + config.getName()
+ "; upstream ref: " + config.getUpstreamConfigurationRef());
}
}
try {
scheduleConnect(0, message);
} catch (Exception e) {
throw new ActiveMQException(e.getMessage(), e, ActiveMQExceptionType.GENERIC_EXCEPTION);
}
ActiveMQServerLogger.LOGGER.federationDownstreamDeployed(config.getName());
}
}
public void undeploy() {
try {
if (started.compareAndSet(true, false)) {
disconnect();
ActiveMQServerLogger.LOGGER.federationDownstreamUnDeployed(config.getName());
}
} catch (ActiveMQException e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
private void scheduleConnect(final int delay, final FederationStreamConnectMessage message) {
scheduledExecutorService.schedule(() -> {
try {
connect();
if (initialized.compareAndSet(false, true)) {
channel.send(message);
}
} catch (Exception e) {
scheduleConnect(FederatedQueueConsumer.getNextDelay(delay, intialConnectDelayMultiplier, intialConnectDelayMax),
message);
}
}, delay, TimeUnit.SECONDS);
}
private void connect() throws Exception {
try {
if (clientSession == null) {
synchronized (this) {
this.clientSessionFactory = (ClientSessionFactoryInternal) getConnection().clientSessionFactory();
this.clientSession = (ClientSessionInternal) clientSessionFactory.createSession(getUser(), getPassword(), false, true, true, clientSessionFactory.getServerLocator().isPreAcknowledge(), clientSessionFactory.getServerLocator().getAckBatchSize());
this.clientSession.addFailureListener(this);
this.clientSession.addMetaData(FederatedQueueConsumer.FEDERATION_NAME, federation.getName().toString());
this.clientSession.addMetaData(FEDERATION_DOWNSTREAM_NAME, config.getName().toString());
this.clientSession.start();
CoreRemotingConnection connection = (CoreRemotingConnection) clientSessionFactory.getConnection();
channel = connection.getChannel(CHANNEL_ID.FEDERATION.id, -1);
}
}
} catch (Exception e) {
try {
if (clientSessionFactory != null) {
clientSessionFactory.cleanup();
}
disconnect();
} catch (ActiveMQException ignored) {
}
throw e;
}
}
@Override
public void connectionFailed(ActiveMQException exception, boolean failedOver) {
connectionFailed(exception, failedOver, null);
}
@Override
public void connectionFailed(ActiveMQException exception, boolean failedOver, String scaleDownTargetNodeID) {
try {
started.set(false);
initialized.set(false);
channel.close();
clientSessionFactory.cleanup();
clientSessionFactory.close();
channel = null;
clientSession = null;
clientSessionFactory = null;
} catch (Throwable dontCare) {
}
start();
}
private void disconnect() throws ActiveMQException {
initialized.set(false);
if (channel != null) {
channel.close();
}
if (clientSession != null) {
clientSession.close();
}
channel = null;
clientSession = null;
if (clientSessionFactory != null && (!getConnection().isSharedConnection() || clientSessionFactory.numSessions() == 0)) {
clientSessionFactory.close();
clientSessionFactory = null;
}
}
@Override
public void beforeReconnect(ActiveMQException exception) {
}
}

View File

@ -122,8 +122,6 @@ public class FederationManager implements ActiveMQComponent {
return federations.get(name);
}
public void register(FederatedAbstract federatedAbstract) {
server.registerBrokerPlugin(federatedAbstract);
}

View File

@ -0,0 +1,109 @@
/**
* 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;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.config.federation.FederationStreamConfiguration;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.federation.address.FederatedAddress;
import org.apache.activemq.artemis.core.server.federation.queue.FederatedQueue;
import org.jboss.logging.Logger;
public abstract class FederationStream {
private static final Logger logger = Logger.getLogger(FederationStream.class);
protected final ActiveMQServer server;
protected final Federation federation;
protected final SimpleString name;
protected final FederationConnection connection;
private FederationStreamConfiguration config;
protected Map<String, FederatedQueue> federatedQueueMap = new HashMap<>();
protected Map<String, FederatedAddress> federatedAddressMap = new HashMap<>();
public FederationStream(ActiveMQServer server, Federation federation, String name, FederationStreamConfiguration config) {
this(server, federation, name, config, null);
}
public FederationStream(final ActiveMQServer server, final Federation federation, final String name, final FederationStreamConfiguration config,
final FederationConnection connection) {
this.server = server;
this.federation = federation;
Objects.requireNonNull(config.getName());
this.name = SimpleString.toSimpleString(config.getName());
this.config = config;
this.connection = connection != null ? connection : new FederationConnection(server.getConfiguration(), name, config.getConnectionConfiguration());
}
public synchronized void start() {
if (connection != null) {
connection.start();
}
}
public synchronized void stop() {
if (connection != null) {
connection.stop();
}
}
public Federation getFederation() {
return federation;
}
public FederationStreamConfiguration getConfig() {
return config;
}
public SimpleString getName() {
return name;
}
public FederationConnection getConnection() {
return connection;
}
public String getUser() {
String user = config.getConnectionConfiguration().getUsername();
if (user == null || user.isEmpty()) {
return federation.getFederationUser();
} else {
return user;
}
}
public String getPassword() {
String password = config.getConnectionConfiguration().getPassword();
if (password == null || password.isEmpty()) {
return federation.getFederationPassword();
} else {
return password;
}
}
public int getPriorityAdjustment() {
return config.getConnectionConfiguration().getPriorityAdjustment();
}
}

View File

@ -17,12 +17,10 @@
package org.apache.activemq.artemis.core.server.federation;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.config.federation.FederationAddressPolicyConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationPolicy;
import org.apache.activemq.artemis.core.config.federation.FederationPolicySet;
@ -33,28 +31,18 @@ import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
import org.apache.activemq.artemis.core.server.federation.address.FederatedAddress;
import org.apache.activemq.artemis.core.server.federation.queue.FederatedQueue;
public class FederationUpstream {
private final ActiveMQServer server;
private final Federation federation;
private final SimpleString name;
private FederationConnection connection;
public class FederationUpstream extends FederationStream {
private FederationUpstreamConfiguration config;
private Map<String, FederatedQueue> federatedQueueMap = new HashMap<>();
private Map<String, FederatedAddress> federatedAddressMap = new HashMap<>();
public FederationUpstream(ActiveMQServer server, Federation federation, String name, FederationUpstreamConfiguration config) {
this.server = server;
this.federation = federation;
Objects.requireNonNull(config.getName());
this.name = SimpleString.toSimpleString(config.getName());
super(server, federation, name, config);
this.config = config;
this.connection = new FederationConnection(server.getConfiguration(), name, config.getConnectionConfiguration());
}
@Override
public synchronized void start() {
connection.start();
super.start();
for (FederatedQueue federatedQueue : federatedQueueMap.values()) {
federatedQueue.start();
}
@ -63,6 +51,7 @@ public class FederationUpstream {
}
}
@Override
public synchronized void stop() {
for (FederatedAddress federatedAddress : federatedAddressMap.values()) {
federatedAddress.stop();
@ -74,7 +63,7 @@ public class FederationUpstream {
}
federatedQueueMap.clear();
connection.stop();
super.stop();
}
public void deploy(Set<String> policyRefsToDeploy, Map<String, FederationPolicy> policyMap) throws ActiveMQException {
@ -156,41 +145,8 @@ public class FederationUpstream {
}
}
@Override
public FederationUpstreamConfiguration getConfig() {
return config;
}
private Exception circuitBreakerException;
private long lastCreateClientSessionFactoryExceptionTimestamp;
public SimpleString getName() {
return name;
}
public FederationConnection getConnection() {
return connection;
}
public String getUser() {
String user = config.getConnectionConfiguration().getUsername();
if (user == null || user.isEmpty()) {
return federation.getFederationUser();
} else {
return user;
}
}
public String getPassword() {
String password = config.getConnectionConfiguration().getPassword();
if (password == null || password.isEmpty()) {
return federation.getFederationPassword();
} else {
return password;
}
}
public int getPriorityAdjustment() {
return config.getConnectionConfiguration().getPriorityAdjustment();
}
}

View File

@ -24,6 +24,7 @@ import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
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.QueueAttributes;
@ -31,11 +32,11 @@ 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.QueueBinding;
import org.apache.activemq.artemis.core.security.SecurityAuth;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.Queue;
import org.apache.activemq.artemis.core.config.federation.FederationAddressPolicyConfiguration;
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;
@ -57,6 +58,7 @@ import org.apache.activemq.artemis.utils.ByteUtil;
*/
public class FederatedAddress extends FederatedAbstract implements ActiveMQServerQueuePlugin, 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;
@ -74,7 +76,7 @@ public class FederatedAddress extends FederatedAbstract implements ActiveMQServe
} else {
this.filterString = HDR_HOPS.concat(" IS NULL OR ").concat(HDR_HOPS).concat("<").concat(Integer.toString(config.getMaxHops()));
}
this.queueNameFormat = SimpleString.toSimpleString("federated.${federation}.${upstream}.${address}.${routeType}");
this.queueNameFormat = SimpleString.toSimpleString(FEDERATED_QUEUE_PREFIX + ".${federation}.${upstream}.${address}.${routeType}");
if (config.getIncludes().isEmpty()) {
includes = Collections.emptySet();
} else {

View File

@ -69,6 +69,7 @@ public class ServiceRegistryImpl implements ServiceRegistry {
this.connectorServices = new ConcurrentHashMap<>();
this.divertTransformers = new ConcurrentHashMap<>();
this.bridgeTransformers = new ConcurrentHashMap<>();
this.federationTransformers = new ConcurrentHashMap<>();
this.acceptorFactories = new ConcurrentHashMap<>();
}

View File

@ -1589,13 +1589,11 @@
<xsd:attributeGroup ref="xml:specialAttrs"/>
</xsd:complexType>
<!-- FEDERATION CONFIGURATION -->
<xsd:complexType name="federationType">
<xsd:sequence>
<xsd:element name="upstream" type="upstreamType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="downstream" type="downstreamType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="policy-set" type="policySetType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="queue-policy" type="queuePolicyType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="address-policy" type="addressPolicyType" minOccurs="0" maxOccurs="unbounded" />
@ -1607,9 +1605,31 @@
<xsd:attributeGroup ref="xml:specialAttrs"/>
</xsd:complexType>
<xsd:complexType name="downstreamType">
<xsd:complexContent>
<xsd:extension base="streamType">
<xsd:sequence>
<xsd:element name="upstream-connector-ref" type="xsd:string" maxOccurs="1" minOccurs="1">
<xsd:annotation>
<xsd:documentation>
Name of the transport connector reference to use for the new upstream connection
back to this broker.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attributeGroup ref="xml:specialAttrs"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="upstreamType">
<xsd:complexContent>
<xsd:extension base="streamType"/>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="streamType">
<xsd:sequence>
<xsd:element name="ha" type="xsd:boolean" default="false" maxOccurs="1" minOccurs="0">
@ -1628,6 +1648,88 @@
</xsd:annotation>
</xsd:element>
<xsd:element name="share-connection" type="xsd:boolean" default="false" maxOccurs="1" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
if there is a downstream and upstream connection configured for the same broker then
the same connection will be shared as long as both stream configs set this flag to true
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="connection-ttl" type="xsd:long" default="60000" maxOccurs="1" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
how long to keep a connection alive in the absence of any data arriving from the client
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="call-timeout" type="xsd:long" default="30000" maxOccurs="1" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
How long to wait for a reply
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="retry-interval" type="xsd:long" default="500" maxOccurs="1" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
period (in ms) between successive retries
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="retry-interval-multiplier" type="xsd:double" default="1" maxOccurs="1" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
multiplier to apply to the retry-interval
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="max-retry-interval" type="xsd:long" default="2000" maxOccurs="1" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
Maximum value for retry-interval
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="initial-connect-attempts" type="xsd:int" default="-1" maxOccurs="1" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
How many attempts should be made to connect initially
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="reconnect-attempts" type="xsd:int" default="-1" maxOccurs="1" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
How many attempts should be made to reconnect after failure
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="check-period" type="xsd:long" default="30000" maxOccurs="1" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
The period (in milliseconds) used to check if the federation connection has failed to receive pings from
another server
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="call-failover-timeout" type="xsd:long" default="-1" maxOccurs="1" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
How long to wait for a reply if in the middle of a fail-over. -1 means wait forever.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:choice>
<xsd:element name="static-connectors" maxOccurs="1" minOccurs="1">
<xsd:complexType>

View File

@ -278,6 +278,36 @@
<property key="federationTransformerKey2" value="federationTransformerValue2"/>
</transformer>
</federation>
<federation name="federation4" user="globaluser" password="32a10275cf4ab4e9">
<upstream name="asia-3">
<static-connectors>
<connector-ref>connector1</connector-ref>
</static-connectors>
<policy ref="queue-federation-asia"/>
<policy ref="address-federation-asia"/>
</upstream>
<downstream name="asia-4" >
<ha>true</ha>
<discovery-group-ref discovery-group-name="dg1"/>
<policy ref="queue-federation-asia"/>
<policy ref="address-federation-asia"/>
<upstream-connector-ref>connector1</upstream-connector-ref>
</downstream>
<queue-policy name="queue-federation-asia2" transformer-ref="federation-transformer-4" >
<exclude queue-match="the_queue" address-match="#" />
</queue-policy>
<address-policy name="address-federation-asia2" transformer-ref="federation-transformer-4" >
<include address-match="the_address" />
</address-policy>
<transformer name="federation-transformer-4">
<class-name>org.foo.FederationTransformer4</class-name>
<property key="federationTransformerKey1" value="federationTransformerValue1"/>
<property key="federationTransformerKey2" value="federationTransformerValue2"/>
</transformer>
</federation>
</federations>
<metrics-plugin class-name="org.apache.activemq.artemis.core.server.metrics.plugins.SimpleMetricsPlugin">

View File

@ -6,12 +6,11 @@ Address federation is like full multicast over the connected brokers, in that ev
on `Broker-A` will be delivered to every queue on that broker, but like wise will be delivered to `Broker-B`
and all attached queues there.
Address federation dynamically links to other addresses in upstream brokers. It automatically creates a queue on the remote address for itself,
Address federation dynamically links to other addresses in upstream or downstream brokers. It automatically creates a queue on the remote address for itself,
to which then it consumes, copying to the local address, as though they were published directly to it.
The upstream brokers do not need to be reconfigured or the address, simply permissions to the address need to be
given to the address for the downstream broker.
given to the address for the downstream broker. Similarly the same applies for downstream configurations.
![Address Federation](images/federation-address.png)
@ -182,14 +181,125 @@ Finally look at `upstream`, this is what defines the upstream broker connection
information about what discovery-groups are and how to configure them, please
see [Discovery Groups](clusters.md).
- `ha`. This optional parameter determines whether or not this bridge should
support high availability. True means it will connect to any available server
in a cluster and support failover. The default value is `false`.
- `circuit-breaker-timeout` in milliseconds, When a connection issue occurs,
as the single connection is shared by many federated queue and address consumers,
to avoid each one trying to reconnect and possibly causing a thrundering heard issue,
to avoid each one trying to reconnect and possibly causing a thundering heard issue,
the first one will try, if unsuccessful the circuit breaker will open,
returning the same exception to all, this is the timeout until the circuit can be closed and connection retried.
- `share-connection`. If there is a downstream and upstream connection configured for the same broker then
the same connection will be shared as long as both stream configs set this flag to true.
Default is false.
- `check-period`. The period (in milliseconds) used to check if the
federation connection has failed to receive pings from another server.
Default is 30000.
- `connection-ttl`. This is how long a federation connection should stay
alive if it stops receiving messages from the remote broker. Default is 60000.
- `call-timeout`. When a packet is sent via a federation connection and
is a blocking call, i.e. for acknowledgements, this is how long it
will wait (in milliseconds) for the reply before throwing an
exception. Default is 30000.
- `call-failover-timeout`. Similar to `call-timeout` but used when a
call is made during a failover attempt. Default is -1 (no timeout).
- `retry-interval`. This optional parameter determines the period in
milliseconds between subsequent reconnection attempts, if the connection to
the target server has failed. The default value is `500` milliseconds.
- `retry-interval-multiplier`. This is a multiplier used to increase
the `retry-interval` after each reconnect attempt, default is 1.
- `max-retry-interval`. The maximum delay (in milliseconds) for
retries. Default is 2000.
- `initial-connect-attempts`. The number of times the system will try
to connect to the remote broker in the federation. If the max-retry is
achieved this broker will be considered permanently down and the
system will not route messages to this broker. Default is -1 (infinite
retries).
- `reconnect-attempts`. The number of times the system will try to
reconnect to the remote broker in the federation. If the max-retry is achieved
this broker will be considered permanently down and the system will
stop routing messages to this broker. Default is -1 (infinite
retries).
## Configuring Downstream Federation
Similarly to `upstream` configuration, a downstream configuration can be configured. This works by sending a command
to the `downstream` broker to have it create an `upstream` connection back to the downstream broker. The benefit of
this is being able to configure everything for federation on one broker in some cases to make it easier, such
as a hub and spoke topology
All of the same configuration options apply to to `downstream` as does `upstream` with the exception of one
extra configuration flag that needs to be set:
The `transport-connector-ref` is an element pointing to a
`connector` elements defined elsewhere. This ref is used to tell the downstream broker
what connector to use to create a new upstream connection back to the downstream broker.
A *connector* encapsulates knowledge of what transport to use (TCP, SSL, HTTP etc) as well as
the server connection parameters (host, port etc). For more information about what connectors are and
how to configure them, please see [Configuring the
Transport](configuring-transports.md).
Sample Downstream Address Federation setup:
```xml
<!--Other config Here -->
<connectors>
<connector name="netty-connector">tcp://localhost:61616</connector>
<connector name="eu-west-1-connector">tcp://localhost:61616</connector>
<connector name="eu-east-1-connector">tcp://localhost:61617</connector>
</connectors>
<acceptors>
<acceptor name="netty-acceptor">tcp://localhost:61616</acceptor>
</acceptors>
<!--Other config Here -->
<federations>
<federation name="eu-north-1" user="federation_username" password="32a10275cf4ab4e9">
<downstream name="eu-east-1">
<static-connectors>
<connector-ref>eu-east-connector1</connector-ref>
</static-connectors>
<transport-connector-ref>netty-connector</transport-connector-ref>
<policy ref="news-address-federation"/>
</downstream>
<downstream name="eu-west-1" >
<static-connectors>
<connector-ref>eu-west-connector1</connector-ref>
</static-connectors>
<transport-connector-ref>netty-connector</transport-connector-ref>
<policy ref="news-address-federation"/>
</downstream>
<address-policy name="news-address-federation" max-hops="1" auto-delete="true" auto-delete-delay="300000" auto-delete-message-count="-1" transformer-ref="federation-transformer-3">
<include address-match="queue.bbc.new" />
<include address-match="queue.usatoday" />
<include address-match="queue.news.#" />
<exclude address-match="queue.news.sport.#" />
</address-policy>
<transformer name="news-transformer">
<class-name>org.foo.NewsTransformer</class-name>
<property key="key1" value="value1"/>
<property key="key2" value="value2"/>
</transformer>
</federation>
</federations>
```

View File

@ -162,7 +162,6 @@ Finally look at `upstream`, this is what defines the upstream broker connection
information about what discovery-groups are and how to configure them, please
see [Discovery Groups](clusters.md).
- `ha`. This optional parameter determines whether or not this bridge should
support high availability. True means it will connect to any available server
in a cluster and support failover. The default value is `false`.
@ -173,3 +172,116 @@ to avoid each one trying to reconnect and possibly causing a thrundering heard i
the first one will try, if unsuccessful the circuit breaker will open,
returning the same exception to all, this is the timeout until the circuit can be closed and connection retried.
- `share-connection`. If there is a downstream and upstream connection configured for the same broker then
the same connection will be shared as long as both stream configs set this flag to true.
Default is false.
- `check-period`. The period (in milliseconds) used to check if the
federation connection has failed to receive pings from another server.
Default is 30000.
- `connection-ttl`. This is how long a federation connection should stay
alive if it stops receiving messages from the remote broker. Default is 60000.
- `call-timeout`. When a packet is sent via a federation connection and
is a blocking call, i.e. for acknowledgements, this is how long it
will wait (in milliseconds) for the reply before throwing an
exception. Default is 30000.
- `call-failover-timeout`. Similar to `call-timeout` but used when a
call is made during a failover attempt. Default is -1 (no timeout).
- `retry-interval`. This optional parameter determines the period in
milliseconds between subsequent reconnection attempts, if the connection to
the target server has failed. The default value is `500` milliseconds.
- `retry-interval-multiplier`. This is a multiplier used to increase
the `retry-interval` after each reconnect attempt, default is 1.
- `max-retry-interval`. The maximum delay (in milliseconds) for
retries. Default is 2000.
- `initial-connect-attempts`. The number of times the system will try
to connect to the remote broker in the federation. If the max-retry is
achieved this broker will be considered permanently down and the
system will not route messages to this broker. Default is -1 (infinite
retries).
- `reconnect-attempts`. The number of times the system will try to
reconnect to the remote broker in the federation. If the max-retry is achieved
this broker will be considered permanently down and the system will
stop routing messages to this broker. Default is -1 (infinite
retries).
## Configuring Downstream Federation
Similarly to `upstream` configuration, a downstream configuration can be configured. This works by sending a command
to the `downstream` broker to have it create an `upstream` connection back to the downstream broker. The benefit of
this is being able to configure everything for federation on one broker in some cases to make it easier, such
as a hub and spoke topology.
All of the same configuration options apply to to `downstream` as does `upstream` with the exception of one
extra configuration flag that needs to be set:
The `transport-connector-ref` is an element pointing to a
`connector` elements defined elsewhere. This ref is used to tell the downstream broker
what connector to use to create a new upstream connection back to the downstream broker.
A *connector* encapsulates knowledge of what transport to use (TCP, SSL, HTTP etc) as well as
the server connection parameters (host, port etc). For more information about what connectors are and
how to configure them, please see [Configuring the
Transport](configuring-transports.md).
Sample Downstream Address Federation setup:
```xml
<!--Other config Here -->
<connectors>
<connector name="netty-connector">tcp://localhost:61616</connector>
<connector name="eu-west-1-connector">tcp://localhost:61616</connector>
<connector name="eu-east-1-connector">tcp://localhost:61617</connector>
</connectors>
<acceptors>
<acceptor name="netty-acceptor">tcp://localhost:61616</acceptor>
</acceptors>
<!--Other config Here -->
<federations>
<federation name="eu-north-1" user="federation_username" password="32a10275cf4ab4e9">
<downstream name="eu-east-1">
<static-connectors>
<connector-ref>eu-east-connector1</connector-ref>
</static-connectors>
<transport-connector-ref>netty-connector</transport-connector-ref>
<policy ref="news-address-federation"/>
</downstream>
<downstream name="eu-west-1" >
<static-connectors>
<connector-ref>eu-west-connector1</connector-ref>
</static-connectors>
<transport-connector-ref>netty-connector</transport-connector-ref>
<policy ref="news-address-federation"/>
</downstream>
<queue-policy name="news-queue-federation" priority-adjustment="-5" include-federated="true" transformer-ref="federation-transformer-3">
<include queue-match="#" address-match="queue.bbc.new" />
<include queue-match="#" address-match="queue.usatoday" />
<include queue-match="#" address-match="queue.news.#" />
<exclude queue-match="#.local" address-match="#" />
</queue-policy>
<transformer name="news-transformer">
<class-name>org.foo.NewsTransformer</class-name>
<property key="key1" value="value1"/>
<property key="key2" value="value2"/>
</transformer>
</federation>
</federations>
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

View File

@ -0,0 +1,215 @@
<?xml version='1.0'?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.activemq.examples.federation</groupId>
<artifactId>broker-federation</artifactId>
<version>2.11.0-SNAPSHOT</version>
</parent>
<artifactId>federated-address-downstream-upstream</artifactId>
<packaging>jar</packaging>
<name>ActiveMQ Artemis Downstream/Upstream Federated Address Example</name>
<properties>
<activemq.basedir>${project.basedir}/../../../..</activemq.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.activemq.examples.federation</groupId>
<artifactId>federated-address</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.activemq</groupId>
<artifactId>artemis-maven-plugin</artifactId>
<executions>
<execution>
<id>create0</id>
<goals>
<goal>create</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<instance>${basedir}/target/server0</instance>
<configuration>${basedir}/target/classes/activemq/server0</configuration>
<!-- this makes it easier in certain envs -->
<javaOptions>-Djava.net.preferIPv4Stack=true</javaOptions>
</configuration>
</execution>
<execution>
<id>create1</id>
<goals>
<goal>create</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<instance>${basedir}/target/server1</instance>
<configuration>${basedir}/target/classes/activemq/server1</configuration>
<!-- this makes it easier in certain envs -->
<javaOptions>-Djava.net.preferIPv4Stack=true</javaOptions>
</configuration>
</execution>
<execution>
<id>create2</id>
<goals>
<goal>create</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<instance>${basedir}/target/server2</instance>
<configuration>${basedir}/target/classes/activemq/server2</configuration>
<!-- this makes it easier in certain envs -->
<javaOptions>-Djava.net.preferIPv4Stack=true</javaOptions>
</configuration>
</execution>
<execution>
<id>start0</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<spawn>true</spawn>
<location>${basedir}/target/server0</location>
<testURI>tcp://localhost:61616</testURI>
<args>
<param>run</param>
</args>
<name>eu-west-1</name>
</configuration>
</execution>
<execution>
<id>start1</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<spawn>true</spawn>
<location>${basedir}/target/server1</location>
<testURI>tcp://localhost:61617</testURI>
<args>
<param>run</param>
</args>
<name>eu-east-1</name>
</configuration>
</execution>
<execution>
<id>start2</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<spawn>true</spawn>
<location>${basedir}/target/server2</location>
<testURI>tcp://localhost:61618</testURI>
<args>
<param>run</param>
</args>
<name>us-central-1</name>
</configuration>
</execution>
<execution>
<id>runClient</id>
<goals>
<goal>runClient</goal>
</goals>
<configuration>
<clientClass>org.apache.activemq.artemis.jms.example.FederatedAddressDownstreamUpstreamExample</clientClass>
</configuration>
</execution>
<execution>
<id>stop0</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<location>${basedir}/target/server0</location>
<args>
<param>stop</param>
</args>
</configuration>
</execution>
<execution>
<id>stop1</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<location>${basedir}/target/server1</location>
<args>
<param>stop</param>
</args>
</configuration>
</execution>
<execution>
<id>stop2</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<location>${basedir}/target/server2</location>
<args>
<param>stop</param>
</args>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.apache.activemq.examples.federation</groupId>
<artifactId>federated-address-downstream-upstream</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>com.vladsch.flexmark</groupId>
<artifactId>markdown-page-generator-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,34 @@
# Federated Address Example
To run the example, simply type **mvn verify** from this directory, or **mvn -PnoServer verify** if you want to start and create the broker manually.
This example demonstrates a core multicast address deployed on three different brokers. The three brokers are configured to form a federated address mesh.
In the example we name the brokers, eu-west, eu-east and us-central to give an idea of the use case.
![EU West, EU East and US Central Diagram](eu-west-east-us-central.png)
The following is then carried out:
1. create a consumer on the queue on each node, and we create a producer on only one of the nodes.
2. send some messages via the producer on EU West, and we verify that **all** the consumers receive the sent messages, in essence multicasting the messages within and accross brokers.
3. Next then verify the same on US Central.
4. Next then verify the same on EU East.
In other words, we are showing how with Federated Address, ActiveMQ Artemis **replicates** sent messages to all addresses and subsequently delivered to all all consumers, regardless if the consumer is local or is on a distant broker. Decoupling the location where producers and consumers need to be.
The config that defines the federation you can see in the broker.xml for each broker is within the following tags. You will note upstreams are different in each as well as the federation name, which has to be globally unique.
```
<federations>
...
</federations>
```
For more information on ActiveMQ Artemis Federation please see the federation section of the user manual.

View File

@ -0,0 +1,29 @@
/*
* 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.jms.example;
/**
* A simple example that demonstrates multicast address replication between remote servers,
* using Address Federation downstream and upstream feature combined.
*/
public class FederatedAddressDownstreamUpstreamExample {
public static void main(final String[] args) throws Exception {
//Re-use the same Federated address test for upstream
FederatedAddressExample.main(args);
}
}

View File

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
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.
-->
<configuration xmlns="urn:activemq" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd">
<core xmlns="urn:activemq:core">
<name>eu-west-1-master</name>
<bindings-directory>./data/bindings</bindings-directory>
<journal-directory>./data/journal</journal-directory>
<large-messages-directory>./data/largemessages</large-messages-directory>
<paging-directory>./data/paging</paging-directory>
<!-- Connectors -->
<connectors>
<connector name="netty-connector">tcp://localhost:61616</connector>
<connector name="eu-west-1-connector">tcp://localhost:61616</connector>
<connector name="eu-east-1-connector">tcp://localhost:61617</connector>
<connector name="us-central-1-connector">tcp://localhost:61618</connector>
</connectors>
<!-- Acceptors -->
<acceptors>
<acceptor name="netty-acceptor">tcp://localhost:61616</acceptor>
</acceptors>
<!-- Federation -->
<federations>
<federation name="eu-west-1-federation">
<upstream name="eu-east-1-upstream">
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<share-connection>true</share-connection>
<static-connectors>
<connector-ref>eu-east-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
</upstream>
<upstream name="us-central-1-upstream">
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<share-connection>true</share-connection>
<static-connectors>
<connector-ref>us-central-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
</upstream>
<downstream name="eu-east-1-downstream" >
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<share-connection>true</share-connection>
<static-connectors>
<connector-ref>eu-east-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
<upstream-connector-ref>netty-connector</upstream-connector-ref>
</downstream>
<downstream name="us-central-1-downstream">
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<share-connection>true</share-connection>
<static-connectors>
<connector-ref>us-central-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
<upstream-connector-ref>netty-connector</upstream-connector-ref>
</downstream>
<policy-set name="policySetA">
<policy ref="address-federation" />
</policy-set>
<address-policy name="address-federation">
<include address-match="exampleTopic" />
</address-policy>
</federation>
</federations>
<!-- Other config -->
<security-settings>
<!--security for example queue-->
<security-setting match="exampleTopic">
<permission roles="guest" type="createDurableQueue"/>
<permission roles="guest" type="deleteDurableQueue"/>
<permission roles="guest" type="createNonDurableQueue"/>
<permission roles="guest" type="deleteNonDurableQueue"/>
<permission roles="guest" type="consume"/>
<permission roles="guest" type="send"/>
</security-setting>
</security-settings>
<addresses>
<address name="exampleTopic">
<multicast>
<queue name="exampleSubscription"/>
</multicast>
</address>
</addresses>
</core>
</configuration>

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
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.
-->
<configuration xmlns="urn:activemq" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd">
<core xmlns="urn:activemq:core">
<name>eu-east-1-master</name>
<bindings-directory>target/server1/data/messaging/bindings</bindings-directory>
<journal-directory>target/server1/data/messaging/journal</journal-directory>
<large-messages-directory>target/server1/data/messaging/largemessages</large-messages-directory>
<paging-directory>target/server1/data/messaging/paging</paging-directory>
<!-- Connectors -->
<connectors>
<connector name="netty-connector">tcp://localhost:61617</connector>
<connector name="eu-east-1-connector">tcp://localhost:61617</connector>
<connector name="us-central-1-connector">tcp://localhost:61618</connector>
</connectors>
<!-- Acceptors -->
<acceptors>
<acceptor name="netty-acceptor">tcp://localhost:61617</acceptor>
</acceptors>
<!-- Federation -->
<federations>
<federation name="eu-east-1-federation">
<upstream name="us-central-1-upstream">
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<share-connection>true</share-connection>
<static-connectors>
<connector-ref>us-central-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
</upstream>
<downstream name="us-central-1-downstream">
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<share-connection>true</share-connection>
<static-connectors>
<connector-ref>us-central-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
<upstream-connector-ref>netty-connector</upstream-connector-ref>
</downstream>
<policy-set name="policySetA">
<policy ref="address-federation" />
</policy-set>
<address-policy name="address-federation" >
<include address-match="exampleTopic" />
</address-policy>
</federation>
</federations>
<!-- Other config -->
<security-settings>
<!--security for example queue-->
<security-setting match="exampleTopic">
<permission roles="guest" type="createDurableQueue"/>
<permission roles="guest" type="deleteDurableQueue"/>
<permission roles="guest" type="createNonDurableQueue"/>
<permission roles="guest" type="deleteNonDurableQueue"/>
<permission roles="guest" type="consume"/>
<permission roles="guest" type="send"/>
</security-setting>
</security-settings>
<addresses>
<address name="exampleTopic">
<multicast>
<queue name="exampleSubscription"/>
</multicast>
</address>
</addresses>
</core>
</configuration>

View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
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.
-->
<configuration xmlns="urn:activemq" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd">
<core xmlns="urn:activemq:core">
<name>us-central-1-master</name>
<bindings-directory>target/server2/data/messaging/bindings</bindings-directory>
<journal-directory>target/server2/data/messaging/journal</journal-directory>
<large-messages-directory>target/server2/data/messaging/largemessages</large-messages-directory>
<paging-directory>target/server2/data/messaging/paging</paging-directory>
<!-- Connectors -->
<connectors>
<connector name="netty-connector">tcp://localhost:61618</connector>
</connectors>
<!-- Acceptors -->
<acceptors>
<acceptor name="netty-acceptor">tcp://localhost:61618</acceptor>
</acceptors>
<!-- Federation -->
<!--No federation configuration necessary as the other brokers will be creating downstreams/upstreams
to this broker-->
<!-- Other config -->
<security-settings>
<!--security for example queue-->
<security-setting match="exampleTopic">
<permission roles="guest" type="createDurableQueue"/>
<permission roles="guest" type="deleteDurableQueue"/>
<permission roles="guest" type="createNonDurableQueue"/>
<permission roles="guest" type="deleteNonDurableQueue"/>
<permission roles="guest" type="consume"/>
<permission roles="guest" type="send"/>
</security-setting>
</security-settings>
<addresses>
<address name="exampleTopic">
<multicast>
<queue name="exampleSubscription"/>
</multicast>
</address>
</addresses>
</core>
</configuration>

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

View File

@ -0,0 +1,215 @@
<?xml version='1.0'?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.activemq.examples.federation</groupId>
<artifactId>broker-federation</artifactId>
<version>2.11.0-SNAPSHOT</version>
</parent>
<artifactId>federated-address-downstream</artifactId>
<packaging>jar</packaging>
<name>ActiveMQ Artemis Downstream Federated Address Example</name>
<properties>
<activemq.basedir>${project.basedir}/../../../..</activemq.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.activemq.examples.federation</groupId>
<artifactId>federated-address</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.activemq</groupId>
<artifactId>artemis-maven-plugin</artifactId>
<executions>
<execution>
<id>create0</id>
<goals>
<goal>create</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<instance>${basedir}/target/server0</instance>
<configuration>${basedir}/target/classes/activemq/server0</configuration>
<!-- this makes it easier in certain envs -->
<javaOptions>-Djava.net.preferIPv4Stack=true</javaOptions>
</configuration>
</execution>
<execution>
<id>create1</id>
<goals>
<goal>create</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<instance>${basedir}/target/server1</instance>
<configuration>${basedir}/target/classes/activemq/server1</configuration>
<!-- this makes it easier in certain envs -->
<javaOptions>-Djava.net.preferIPv4Stack=true</javaOptions>
</configuration>
</execution>
<execution>
<id>create2</id>
<goals>
<goal>create</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<instance>${basedir}/target/server2</instance>
<configuration>${basedir}/target/classes/activemq/server2</configuration>
<!-- this makes it easier in certain envs -->
<javaOptions>-Djava.net.preferIPv4Stack=true</javaOptions>
</configuration>
</execution>
<execution>
<id>start0</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<spawn>true</spawn>
<location>${basedir}/target/server0</location>
<testURI>tcp://localhost:61616</testURI>
<args>
<param>run</param>
</args>
<name>eu-west-1</name>
</configuration>
</execution>
<execution>
<id>start1</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<spawn>true</spawn>
<location>${basedir}/target/server1</location>
<testURI>tcp://localhost:61617</testURI>
<args>
<param>run</param>
</args>
<name>eu-east-1</name>
</configuration>
</execution>
<execution>
<id>start2</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<spawn>true</spawn>
<location>${basedir}/target/server2</location>
<testURI>tcp://localhost:61618</testURI>
<args>
<param>run</param>
</args>
<name>us-central-1</name>
</configuration>
</execution>
<execution>
<id>runClient</id>
<goals>
<goal>runClient</goal>
</goals>
<configuration>
<clientClass>org.apache.activemq.artemis.jms.example.FederatedAddressDownstreamExample</clientClass>
</configuration>
</execution>
<execution>
<id>stop0</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<location>${basedir}/target/server0</location>
<args>
<param>stop</param>
</args>
</configuration>
</execution>
<execution>
<id>stop1</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<location>${basedir}/target/server1</location>
<args>
<param>stop</param>
</args>
</configuration>
</execution>
<execution>
<id>stop2</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<location>${basedir}/target/server2</location>
<args>
<param>stop</param>
</args>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.apache.activemq.examples.federation</groupId>
<artifactId>federated-address-downstream</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>com.vladsch.flexmark</groupId>
<artifactId>markdown-page-generator-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,34 @@
# Federated Address Example
To run the example, simply type **mvn verify** from this directory, or **mvn -PnoServer verify** if you want to start and create the broker manually.
This example demonstrates a core multicast address deployed on three different brokers. The three brokers are configured to form a federated address mesh.
In the example we name the brokers, eu-west, eu-east and us-central to give an idea of the use case.
![EU West, EU East and US Central Diagram](eu-west-east-us-central.png)
The following is then carried out:
1. create a consumer on the queue on each node, and we create a producer on only one of the nodes.
2. send some messages via the producer on EU West, and we verify that **all** the consumers receive the sent messages, in essence multicasting the messages within and accross brokers.
3. Next then verify the same on US Central.
4. Next then verify the same on EU East.
In other words, we are showing how with Federated Address, ActiveMQ Artemis **replicates** sent messages to all addresses and subsequently delivered to all all consumers, regardless if the consumer is local or is on a distant broker. Decoupling the location where producers and consumers need to be.
The config that defines the federation you can see in the broker.xml for each broker is within the following tags. You will note upstreams are different in each as well as the federation name, which has to be globally unique.
```
<federations>
...
</federations>
```
For more information on ActiveMQ Artemis Federation please see the federation section of the user manual.

View File

@ -0,0 +1,29 @@
/*
* 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.jms.example;
/**
* A simple example that demonstrates multicast address replication between remote servers,
* using Address Federation downstream feature.
*/
public class FederatedAddressDownstreamExample {
public static void main(final String[] args) throws Exception {
//Re-use the same Federated address test for upstream
FederatedAddressExample.main(args);
}
}

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
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.
-->
<configuration xmlns="urn:activemq" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd">
<core xmlns="urn:activemq:core">
<name>eu-west-1-master</name>
<bindings-directory>./data/bindings</bindings-directory>
<journal-directory>./data/journal</journal-directory>
<large-messages-directory>./data/largemessages</large-messages-directory>
<paging-directory>./data/paging</paging-directory>
<!-- Connectors -->
<connectors>
<connector name="netty-connector">tcp://localhost:61616</connector>
<connector name="eu-west-1-connector">tcp://localhost:61616</connector>
<connector name="eu-east-1-connector">tcp://localhost:61617</connector>
<connector name="us-central-1-connector">tcp://localhost:61618</connector>
</connectors>
<!-- Acceptors -->
<acceptors>
<acceptor name="netty-acceptor">tcp://localhost:61616</acceptor>
</acceptors>
<!-- Federation -->
<federations>
<federation name="eu-west-1-federation">
<downstream name="eu-east-1-downstream" >
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<static-connectors>
<connector-ref>eu-east-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
<upstream-connector-ref>netty-connector</upstream-connector-ref>
</downstream>
<downstream name="us-central-1-downstream">
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<static-connectors>
<connector-ref>us-central-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
<upstream-connector-ref>netty-connector</upstream-connector-ref>
</downstream>
<policy-set name="policySetA">
<policy ref="address-federation" />
</policy-set>
<address-policy name="address-federation">
<include address-match="exampleTopic" />
</address-policy>
</federation>
</federations>
<!-- Other config -->
<security-settings>
<!--security for example queue-->
<security-setting match="exampleTopic">
<permission roles="guest" type="createDurableQueue"/>
<permission roles="guest" type="deleteDurableQueue"/>
<permission roles="guest" type="createNonDurableQueue"/>
<permission roles="guest" type="deleteNonDurableQueue"/>
<permission roles="guest" type="consume"/>
<permission roles="guest" type="send"/>
</security-setting>
</security-settings>
<addresses>
<address name="exampleTopic">
<multicast>
<queue name="exampleSubscription"/>
</multicast>
</address>
</addresses>
</core>
</configuration>

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
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.
-->
<configuration xmlns="urn:activemq" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd">
<core xmlns="urn:activemq:core">
<name>eu-east-1-master</name>
<bindings-directory>target/server1/data/messaging/bindings</bindings-directory>
<journal-directory>target/server1/data/messaging/journal</journal-directory>
<large-messages-directory>target/server1/data/messaging/largemessages</large-messages-directory>
<paging-directory>target/server1/data/messaging/paging</paging-directory>
<!-- Connectors -->
<connectors>
<connector name="netty-connector">tcp://localhost:61617</connector>
<connector name="eu-west-1-connector">tcp://localhost:61616</connector>
<connector name="eu-east-1-connector">tcp://localhost:61617</connector>
<connector name="us-central-1-connector">tcp://localhost:61618</connector>
</connectors>
<!-- Acceptors -->
<acceptors>
<acceptor name="netty-acceptor">tcp://localhost:61617</acceptor>
</acceptors>
<!-- Federation -->
<federations>
<federation name="eu-east-1-federation">
<downstream name="eu-west-1-downstream">
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<static-connectors>
<connector-ref>eu-west-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
<upstream-connector-ref>netty-connector</upstream-connector-ref>
</downstream>
<downstream name="us-central-1-downstream">
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<static-connectors>
<connector-ref>us-central-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
<upstream-connector-ref>netty-connector</upstream-connector-ref>
</downstream>
<policy-set name="policySetA">
<policy ref="address-federation" />
</policy-set>
<address-policy name="address-federation" >
<include address-match="exampleTopic" />
</address-policy>
</federation>
</federations>
<!-- Other config -->
<security-settings>
<!--security for example queue-->
<security-setting match="exampleTopic">
<permission roles="guest" type="createDurableQueue"/>
<permission roles="guest" type="deleteDurableQueue"/>
<permission roles="guest" type="createNonDurableQueue"/>
<permission roles="guest" type="deleteNonDurableQueue"/>
<permission roles="guest" type="consume"/>
<permission roles="guest" type="send"/>
</security-setting>
</security-settings>
<addresses>
<address name="exampleTopic">
<multicast>
<queue name="exampleSubscription"/>
</multicast>
</address>
</addresses>
</core>
</configuration>

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
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.
-->
<configuration xmlns="urn:activemq" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd">
<core xmlns="urn:activemq:core">
<name>us-central-1-master</name>
<bindings-directory>target/server2/data/messaging/bindings</bindings-directory>
<journal-directory>target/server2/data/messaging/journal</journal-directory>
<large-messages-directory>target/server2/data/messaging/largemessages</large-messages-directory>
<paging-directory>target/server2/data/messaging/paging</paging-directory>
<!-- Connectors -->
<connectors>
<connector name="netty-connector">tcp://localhost:61618</connector>
<connector name="eu-west-1-connector">tcp://localhost:61616</connector>
<connector name="eu-east-1-connector">tcp://localhost:61617</connector>
<connector name="us-central-1-connector">tcp://localhost:61618</connector>
</connectors>
<!-- Acceptors -->
<acceptors>
<acceptor name="netty-acceptor">tcp://localhost:61618</acceptor>
</acceptors>
<!-- Federation -->
<federations>
<federation name="us-central-1-federation">
<downstream name="eu-east-1-downstream">
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<static-connectors>
<connector-ref>eu-east-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
<upstream-connector-ref>netty-connector</upstream-connector-ref>
</downstream>
<downstream name="eu-west-1-downstream">
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<static-connectors>
<connector-ref>eu-west-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
<upstream-connector-ref>netty-connector</upstream-connector-ref>
</downstream>
<policy-set name="policySetA">
<policy ref="address-federation" />
</policy-set>
<address-policy name="address-federation">
<include address-match="exampleTopic" />
</address-policy>
</federation>
</federations>
<!-- Other config -->
<security-settings>
<!--security for example queue-->
<security-setting match="exampleTopic">
<permission roles="guest" type="createDurableQueue"/>
<permission roles="guest" type="deleteDurableQueue"/>
<permission roles="guest" type="createNonDurableQueue"/>
<permission roles="guest" type="deleteNonDurableQueue"/>
<permission roles="guest" type="consume"/>
<permission roles="guest" type="send"/>
</security-setting>
</security-settings>
<addresses>
<address name="exampleTopic">
<multicast>
<queue name="exampleSubscription"/>
</multicast>
</address>
</addresses>
</core>
</configuration>

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

View File

@ -0,0 +1,215 @@
<?xml version='1.0'?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.activemq.examples.federation</groupId>
<artifactId>broker-federation</artifactId>
<version>2.11.0-SNAPSHOT</version>
</parent>
<artifactId>federated-queue-downstream-upstream</artifactId>
<packaging>jar</packaging>
<name>ActiveMQ Artemis Downstream/Upstream Federated Queue Example</name>
<properties>
<activemq.basedir>${project.basedir}/../../../..</activemq.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.activemq.examples.federation</groupId>
<artifactId>federated-queue</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.activemq</groupId>
<artifactId>artemis-maven-plugin</artifactId>
<executions>
<execution>
<id>create0</id>
<goals>
<goal>create</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<instance>${basedir}/target/server0</instance>
<configuration>${basedir}/target/classes/activemq/server0</configuration>
<!-- this makes it easier in certain envs -->
<javaOptions>-Djava.net.preferIPv4Stack=true</javaOptions>
</configuration>
</execution>
<execution>
<id>create1</id>
<goals>
<goal>create</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<instance>${basedir}/target/server1</instance>
<configuration>${basedir}/target/classes/activemq/server1</configuration>
<!-- this makes it easier in certain envs -->
<javaOptions>-Djava.net.preferIPv4Stack=true</javaOptions>
</configuration>
</execution>
<execution>
<id>create2</id>
<goals>
<goal>create</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<instance>${basedir}/target/server2</instance>
<configuration>${basedir}/target/classes/activemq/server2</configuration>
<!-- this makes it easier in certain envs -->
<javaOptions>-Djava.net.preferIPv4Stack=true</javaOptions>
</configuration>
</execution>
<execution>
<id>start0</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<spawn>true</spawn>
<location>${basedir}/target/server0</location>
<testURI>tcp://localhost:61616</testURI>
<args>
<param>run</param>
</args>
<name>eu-west-1</name>
</configuration>
</execution>
<execution>
<id>start1</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<spawn>true</spawn>
<location>${basedir}/target/server1</location>
<testURI>tcp://localhost:61617</testURI>
<args>
<param>run</param>
</args>
<name>eu-east-1</name>
</configuration>
</execution>
<execution>
<id>start2</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<spawn>true</spawn>
<location>${basedir}/target/server2</location>
<testURI>tcp://localhost:61618</testURI>
<args>
<param>run</param>
</args>
<name>us-central-1</name>
</configuration>
</execution>
<execution>
<id>runClient</id>
<goals>
<goal>runClient</goal>
</goals>
<configuration>
<clientClass>org.apache.activemq.artemis.jms.example.FederatedQueueDownstreamUpstreamExample</clientClass>
</configuration>
</execution>
<execution>
<id>stop0</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<location>${basedir}/target/server0</location>
<args>
<param>stop</param>
</args>
</configuration>
</execution>
<execution>
<id>stop1</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<location>${basedir}/target/server1</location>
<args>
<param>stop</param>
</args>
</configuration>
</execution>
<execution>
<id>stop2</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<location>${basedir}/target/server2</location>
<args>
<param>stop</param>
</args>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.apache.activemq.examples.federation</groupId>
<artifactId>federated-queue-downstream-upstream</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>com.vladsch.flexmark</groupId>
<artifactId>markdown-page-generator-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,51 @@
# Federated Queue Example
To run the example, simply type **mvn verify** from this directory, or **mvn -PnoServer verify** if you want to start and create the broker manually.
This example demonstrates a core queue deployed on three different brokers. The three brokers are configured to form a federated queue mesh.
In the example we name the brokers, eu-west, eu-east and us-central to give an idea of the use case.
![EU West, EU East and US Central Diagram](eu-west-east-us-central.png)
The following is then carried out:
1. create a consumer on the queue on each node, and we create a producer on only one of the nodes.
2. send some messages via the producer on EU West, and we verify that **only the local** consumer receives the sent messages.
3. Next then verify the same on US Central.
4. Now the consumer on EU West is closed leaving it no local consumers.
5. Send some more messages to server EU West
6. We now consume those messages on EU East demonstrating that messages will **re-route** to the another broker based on upstream priority. You will note, US Central is configured to be -1 priority compared to EU East,
there for messages should re-route to EU East as it will have a slightly higher priority for its consumers to consume.
If US Central and EU East were even priority then the re-direct would be loaded between the two.
7. Next the consumer on US Central is closed leaving it no local consumers. And we send some more messages to US Cental
8. Again we consume on EU East demonstrating that US Central messages also can **re-route**, if no local-consumer.
9. Now we restart EU West and US Centrals consumers.
10. We produce and consume on US Central, showing that dynamically re-adjusts now local consumers exist and messages delivery by priority to local consumer.
11. And repeat the same on EU West.
In other words, we are showing how with Federated Queues, ActiveMQ Artemis **routes** sent messages to local consumers as priority, but is able to re-route the sent messages to other distant brokers if consumers are attached to those brokers. Decoupling the location where producers and consumers need to be.
Here's the relevant snippet from the broker configuration, which tells the broker to form a cluster between the two nodes and to load balance the messages between the nodes.
The config that defines the federation you can see in the broker.xml for each broker is within the following tags. You will note upstreams are different in each as well as the federation name, which has to be globally unique.
```
<federations>
...
</federations>
```
For more information on ActiveMQ Artemis Federation please see the federation section of the user manual.

View File

@ -0,0 +1,29 @@
/*
* 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.jms.example;
/**
* A simple example that demonstrates dynamic queue messaging routing between remote servers,
* as consumers come and go, routing based on priorities.
* using Queue Federation feature.
*/
public class FederatedQueueDownstreamUpstreamExample {
public static void main(final String[] args) throws Exception {
FederatedQueueExample.main(args);
}
}

View File

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
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.
-->
<configuration xmlns="urn:activemq" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd">
<core xmlns="urn:activemq:core">
<name>eu-west-1-master</name>
<bindings-directory>./data/bindings</bindings-directory>
<journal-directory>./data/journal</journal-directory>
<large-messages-directory>./data/largemessages</large-messages-directory>
<paging-directory>./data/paging</paging-directory>
<!-- Connectors -->
<connectors>
<connector name="netty-connector">tcp://localhost:61616</connector>
<connector name="eu-west-1-connector">tcp://localhost:61616</connector>
<connector name="eu-east-1-connector">tcp://localhost:61617</connector>
<connector name="us-central-1-connector">tcp://localhost:61618</connector>
</connectors>
<!-- Acceptors -->
<acceptors>
<acceptor name="netty-acceptor">tcp://localhost:61616</acceptor>
</acceptors>
<!-- Federation -->
<federations>
<federation name="eu-west-1-federation">
<upstream name="eu-east-1-upstream">
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<share-connection>true</share-connection>
<static-connectors>
<connector-ref>eu-east-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
</upstream>
<upstream name="us-central-1-upstream">
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<share-connection>true</share-connection>
<static-connectors>
<connector-ref>us-central-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
</upstream>
<downstream name="eu-east-1-downstream">
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<share-connection>true</share-connection>
<static-connectors>
<connector-ref>eu-east-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
<upstream-connector-ref>netty-connector</upstream-connector-ref>
</downstream>
<downstream name="us-central-1-downstream" priority-adjustment="-1">
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<share-connection>true</share-connection>
<static-connectors>
<connector-ref>us-central-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
<upstream-connector-ref>netty-connector</upstream-connector-ref>
</downstream>
<policy-set name="policySetA">
<policy ref="queue-federation" />
</policy-set>
<queue-policy name="queue-federation" >
<include queue-match="exampleQueue" address-match="#" />
</queue-policy>
</federation>
</federations>
<!-- Other config -->
<security-settings>
<!--security for example queue-->
<security-setting match="exampleQueue">
<permission roles="guest" type="createDurableQueue"/>
<permission roles="guest" type="deleteDurableQueue"/>
<permission roles="guest" type="createNonDurableQueue"/>
<permission roles="guest" type="deleteNonDurableQueue"/>
<permission roles="guest" type="consume"/>
<permission roles="guest" type="send"/>
</security-setting>
</security-settings>
<addresses>
<address name="exampleQueue">
<anycast>
<queue name="exampleQueue"/>
</anycast>
</address>
</addresses>
</core>
</configuration>

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
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.
-->
<configuration xmlns="urn:activemq" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd">
<core xmlns="urn:activemq:core">
<name>eu-east-1-master</name>
<bindings-directory>target/server1/data/messaging/bindings</bindings-directory>
<journal-directory>target/server1/data/messaging/journal</journal-directory>
<large-messages-directory>target/server1/data/messaging/largemessages</large-messages-directory>
<paging-directory>target/server1/data/messaging/paging</paging-directory>
<!-- Connectors -->
<connectors>
<connector name="netty-connector">tcp://localhost:61617</connector>
<connector name="eu-east-1-connector">tcp://localhost:61617</connector>
<connector name="us-central-1-connector">tcp://localhost:61618</connector>
</connectors>
<!-- Acceptors -->
<acceptors>
<acceptor name="netty-acceptor">tcp://localhost:61617</acceptor>
</acceptors>
<!-- Federation -->
<federations>
<federation name="eu-east-1-federation">
<upstream name="us-central-1-upstream">
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<share-connection>true</share-connection>
<static-connectors>
<connector-ref>us-central-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
</upstream>
<downstream name="us-central-1-downstream" priority-adjustment="-1">
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<share-connection>true</share-connection>
<static-connectors>
<connector-ref>us-central-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
<upstream-connector-ref>netty-connector</upstream-connector-ref>
</downstream>
<policy-set name="policySetA">
<policy ref="queue-federation" />
</policy-set>
<queue-policy name="queue-federation" >
<include queue-match="exampleQueue" address-match="#" />
</queue-policy>
</federation>
</federations>
<!-- Other config -->
<security-settings>
<!--security for example queue-->
<security-setting match="exampleQueue">
<permission roles="guest" type="createDurableQueue"/>
<permission roles="guest" type="deleteDurableQueue"/>
<permission roles="guest" type="createNonDurableQueue"/>
<permission roles="guest" type="deleteNonDurableQueue"/>
<permission roles="guest" type="consume"/>
<permission roles="guest" type="send"/>
</security-setting>
</security-settings>
<addresses>
<address name="exampleQueue">
<anycast>
<queue name="exampleQueue"/>
</anycast>
</address>
</addresses>
</core>
</configuration>

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
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.
-->
<configuration xmlns="urn:activemq" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd">
<core xmlns="urn:activemq:core">
<name>us-central-1-master</name>
<bindings-directory>target/server2/data/messaging/bindings</bindings-directory>
<journal-directory>target/server2/data/messaging/journal</journal-directory>
<large-messages-directory>target/server2/data/messaging/largemessages</large-messages-directory>
<paging-directory>target/server2/data/messaging/paging</paging-directory>
<!-- Connectors -->
<connectors>
<connector name="netty-connector">tcp://localhost:61618</connector>
<connector name="eu-west-1-connector">tcp://localhost:61616</connector>
<connector name="eu-east-1-connector">tcp://localhost:61617</connector>
<connector name="us-central-1-connector">tcp://localhost:61618</connector>
</connectors>
<!-- Acceptors -->
<acceptors>
<acceptor name="netty-acceptor">tcp://localhost:61618</acceptor>
</acceptors>
<!-- Federation -->
<!--No federation configuration necessary as the other brokers will be creating downstreams/upstreams
to this broker-->
<!-- Other config -->
<security-settings>
<!--security for example queue-->
<security-setting match="exampleQueue">
<permission roles="guest" type="createDurableQueue"/>
<permission roles="guest" type="deleteDurableQueue"/>
<permission roles="guest" type="createNonDurableQueue"/>
<permission roles="guest" type="deleteNonDurableQueue"/>
<permission roles="guest" type="consume"/>
<permission roles="guest" type="send"/>
</security-setting>
</security-settings>
<addresses>
<address name="exampleQueue">
<anycast>
<queue name="exampleQueue"/>
</anycast>
</address>
</addresses>
</core>
</configuration>

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

View File

@ -0,0 +1,215 @@
<?xml version='1.0'?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.activemq.examples.federation</groupId>
<artifactId>broker-federation</artifactId>
<version>2.11.0-SNAPSHOT</version>
</parent>
<artifactId>federated-queue-downstream</artifactId>
<packaging>jar</packaging>
<name>ActiveMQ Artemis Downstream Federated Queue Example</name>
<properties>
<activemq.basedir>${project.basedir}/../../../..</activemq.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.activemq.examples.federation</groupId>
<artifactId>federated-queue</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.activemq</groupId>
<artifactId>artemis-maven-plugin</artifactId>
<executions>
<execution>
<id>create0</id>
<goals>
<goal>create</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<instance>${basedir}/target/server0</instance>
<configuration>${basedir}/target/classes/activemq/server0</configuration>
<!-- this makes it easier in certain envs -->
<javaOptions>-Djava.net.preferIPv4Stack=true</javaOptions>
</configuration>
</execution>
<execution>
<id>create1</id>
<goals>
<goal>create</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<instance>${basedir}/target/server1</instance>
<configuration>${basedir}/target/classes/activemq/server1</configuration>
<!-- this makes it easier in certain envs -->
<javaOptions>-Djava.net.preferIPv4Stack=true</javaOptions>
</configuration>
</execution>
<execution>
<id>create2</id>
<goals>
<goal>create</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<instance>${basedir}/target/server2</instance>
<configuration>${basedir}/target/classes/activemq/server2</configuration>
<!-- this makes it easier in certain envs -->
<javaOptions>-Djava.net.preferIPv4Stack=true</javaOptions>
</configuration>
</execution>
<execution>
<id>start0</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<spawn>true</spawn>
<location>${basedir}/target/server0</location>
<testURI>tcp://localhost:61616</testURI>
<args>
<param>run</param>
</args>
<name>eu-west-1</name>
</configuration>
</execution>
<execution>
<id>start1</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<spawn>true</spawn>
<location>${basedir}/target/server1</location>
<testURI>tcp://localhost:61617</testURI>
<args>
<param>run</param>
</args>
<name>eu-east-1</name>
</configuration>
</execution>
<execution>
<id>start2</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<spawn>true</spawn>
<location>${basedir}/target/server2</location>
<testURI>tcp://localhost:61618</testURI>
<args>
<param>run</param>
</args>
<name>us-central-1</name>
</configuration>
</execution>
<execution>
<id>runClient</id>
<goals>
<goal>runClient</goal>
</goals>
<configuration>
<clientClass>org.apache.activemq.artemis.jms.example.FederatedQueueDownstreamExample</clientClass>
</configuration>
</execution>
<execution>
<id>stop0</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<location>${basedir}/target/server0</location>
<args>
<param>stop</param>
</args>
</configuration>
</execution>
<execution>
<id>stop1</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<location>${basedir}/target/server1</location>
<args>
<param>stop</param>
</args>
</configuration>
</execution>
<execution>
<id>stop2</id>
<goals>
<goal>cli</goal>
</goals>
<configuration>
<ignore>${noServer}</ignore>
<location>${basedir}/target/server2</location>
<args>
<param>stop</param>
</args>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.apache.activemq.examples.federation</groupId>
<artifactId>federated-queue-downstream</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>com.vladsch.flexmark</groupId>
<artifactId>markdown-page-generator-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,51 @@
# Federated Queue Example
To run the example, simply type **mvn verify** from this directory, or **mvn -PnoServer verify** if you want to start and create the broker manually.
This example demonstrates a core queue deployed on three different brokers. The three brokers are configured to form a federated queue mesh.
In the example we name the brokers, eu-west, eu-east and us-central to give an idea of the use case.
![EU West, EU East and US Central Diagram](eu-west-east-us-central.png)
The following is then carried out:
1. create a consumer on the queue on each node, and we create a producer on only one of the nodes.
2. send some messages via the producer on EU West, and we verify that **only the local** consumer receives the sent messages.
3. Next then verify the same on US Central.
4. Now the consumer on EU West is closed leaving it no local consumers.
5. Send some more messages to server EU West
6. We now consume those messages on EU East demonstrating that messages will **re-route** to the another broker based on upstream priority. You will note, US Central is configured to be -1 priority compared to EU East,
there for messages should re-route to EU East as it will have a slightly higher priority for its consumers to consume.
If US Central and EU East were even priority then the re-direct would be loaded between the two.
7. Next the consumer on US Central is closed leaving it no local consumers. And we send some more messages to US Cental
8. Again we consume on EU East demonstrating that US Central messages also can **re-route**, if no local-consumer.
9. Now we restart EU West and US Centrals consumers.
10. We produce and consume on US Central, showing that dynamically re-adjusts now local consumers exist and messages delivery by priority to local consumer.
11. And repeat the same on EU West.
In other words, we are showing how with Federated Queues, ActiveMQ Artemis **routes** sent messages to local consumers as priority, but is able to re-route the sent messages to other distant brokers if consumers are attached to those brokers. Decoupling the location where producers and consumers need to be.
Here's the relevant snippet from the broker configuration, which tells the broker to form a cluster between the two nodes and to load balance the messages between the nodes.
The config that defines the federation you can see in the broker.xml for each broker is within the following tags. You will note upstreams are different in each as well as the federation name, which has to be globally unique.
```
<federations>
...
</federations>
```
For more information on ActiveMQ Artemis Federation please see the federation section of the user manual.

View File

@ -0,0 +1,29 @@
/*
* 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.jms.example;
/**
* A simple example that demonstrates dynamic queue messaging routing between remote servers,
* as consumers come and go, routing based on priorities.
* using Queue Federation feature.
*/
public class FederatedQueueDownstreamExample {
public static void main(final String[] args) throws Exception {
FederatedQueueExample.main(args);
}
}

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
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.
-->
<configuration xmlns="urn:activemq" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd">
<core xmlns="urn:activemq:core">
<name>eu-west-1-master</name>
<bindings-directory>./data/bindings</bindings-directory>
<journal-directory>./data/journal</journal-directory>
<large-messages-directory>./data/largemessages</large-messages-directory>
<paging-directory>./data/paging</paging-directory>
<!-- Connectors -->
<connectors>
<connector name="netty-connector">tcp://localhost:61616</connector>
<connector name="eu-west-1-connector">tcp://localhost:61616</connector>
<connector name="eu-east-1-connector">tcp://localhost:61617</connector>
<connector name="us-central-1-connector">tcp://localhost:61618</connector>
</connectors>
<!-- Acceptors -->
<acceptors>
<acceptor name="netty-acceptor">tcp://localhost:61616</acceptor>
</acceptors>
<!-- Federation -->
<federations>
<federation name="eu-west-1-federation">
<downstream name="eu-east-1-downstream">
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<static-connectors>
<connector-ref>eu-east-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
<upstream-connector-ref>netty-connector</upstream-connector-ref>
</downstream>
<downstream name="us-central-1-downstream" priority-adjustment="-1">
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<static-connectors>
<connector-ref>us-central-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
<upstream-connector-ref>netty-connector</upstream-connector-ref>
</downstream>
<policy-set name="policySetA">
<policy ref="queue-federation" />
</policy-set>
<queue-policy name="queue-federation" >
<include queue-match="exampleQueue" address-match="#" />
</queue-policy>
</federation>
</federations>
<!-- Other config -->
<security-settings>
<!--security for example queue-->
<security-setting match="exampleQueue">
<permission roles="guest" type="createDurableQueue"/>
<permission roles="guest" type="deleteDurableQueue"/>
<permission roles="guest" type="createNonDurableQueue"/>
<permission roles="guest" type="deleteNonDurableQueue"/>
<permission roles="guest" type="consume"/>
<permission roles="guest" type="send"/>
</security-setting>
</security-settings>
<addresses>
<address name="exampleQueue">
<anycast>
<queue name="exampleQueue"/>
</anycast>
</address>
</addresses>
</core>
</configuration>

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
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.
-->
<configuration xmlns="urn:activemq" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd">
<core xmlns="urn:activemq:core">
<name>eu-east-1-master</name>
<bindings-directory>target/server1/data/messaging/bindings</bindings-directory>
<journal-directory>target/server1/data/messaging/journal</journal-directory>
<large-messages-directory>target/server1/data/messaging/largemessages</large-messages-directory>
<paging-directory>target/server1/data/messaging/paging</paging-directory>
<!-- Connectors -->
<connectors>
<connector name="netty-connector">tcp://localhost:61617</connector>
<connector name="eu-west-1-connector">tcp://localhost:61616</connector>
<connector name="eu-east-1-connector">tcp://localhost:61617</connector>
<connector name="us-central-1-connector">tcp://localhost:61618</connector>
</connectors>
<!-- Acceptors -->
<acceptors>
<acceptor name="netty-acceptor">tcp://localhost:61617</acceptor>
</acceptors>
<!-- Federation -->
<federations>
<federation name="eu-east-1-federation">
<downstream name="eu-west-1-downstream">
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<static-connectors>
<connector-ref>eu-west-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
<upstream-connector-ref>netty-connector</upstream-connector-ref>
</downstream>
<downstream name="us-central-1-downstream" priority-adjustment="-1">
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<static-connectors>
<connector-ref>us-central-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
<upstream-connector-ref>netty-connector</upstream-connector-ref>
</downstream>
<policy-set name="policySetA">
<policy ref="queue-federation" />
</policy-set>
<queue-policy name="queue-federation" >
<include queue-match="exampleQueue" address-match="#" />
</queue-policy>
</federation>
</federations>
<!-- Other config -->
<security-settings>
<!--security for example queue-->
<security-setting match="exampleQueue">
<permission roles="guest" type="createDurableQueue"/>
<permission roles="guest" type="deleteDurableQueue"/>
<permission roles="guest" type="createNonDurableQueue"/>
<permission roles="guest" type="deleteNonDurableQueue"/>
<permission roles="guest" type="consume"/>
<permission roles="guest" type="send"/>
</security-setting>
</security-settings>
<addresses>
<address name="exampleQueue">
<anycast>
<queue name="exampleQueue"/>
</anycast>
</address>
</addresses>
</core>
</configuration>

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
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.
-->
<configuration xmlns="urn:activemq" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd">
<core xmlns="urn:activemq:core">
<name>us-central-1-master</name>
<bindings-directory>target/server2/data/messaging/bindings</bindings-directory>
<journal-directory>target/server2/data/messaging/journal</journal-directory>
<large-messages-directory>target/server2/data/messaging/largemessages</large-messages-directory>
<paging-directory>target/server2/data/messaging/paging</paging-directory>
<!-- Connectors -->
<connectors>
<connector name="netty-connector">tcp://localhost:61618</connector>
<connector name="eu-west-1-connector">tcp://localhost:61616</connector>
<connector name="eu-east-1-connector">tcp://localhost:61617</connector>
<connector name="us-central-1-connector">tcp://localhost:61618</connector>
</connectors>
<!-- Acceptors -->
<acceptors>
<acceptor name="netty-acceptor">tcp://localhost:61618</acceptor>
</acceptors>
<!-- Federation -->
<federations>
<federation name="us-central-1-federation">
<downstream name="eu-east-1-downstream" priority-adjustment="-1">
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<static-connectors>
<connector-ref>eu-east-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
<upstream-connector-ref>netty-connector</upstream-connector-ref>
</downstream>
<downstream name="eu-west-1-downstream" priority-adjustment="-1">
<circuit-breaker-timeout>1000</circuit-breaker-timeout>
<static-connectors>
<connector-ref>eu-west-1-connector</connector-ref>
</static-connectors>
<policy ref="policySetA"/>
<upstream-connector-ref>netty-connector</upstream-connector-ref>
</downstream>
<policy-set name="policySetA">
<policy ref="queue-federation" />
</policy-set>
<queue-policy name="queue-federation" >
<include queue-match="exampleQueue" address-match="#" />
</queue-policy>
</federation>
</federations>
<!-- Other config -->
<security-settings>
<!--security for example queue-->
<security-setting match="exampleQueue">
<permission roles="guest" type="createDurableQueue"/>
<permission roles="guest" type="deleteDurableQueue"/>
<permission roles="guest" type="createNonDurableQueue"/>
<permission roles="guest" type="deleteNonDurableQueue"/>
<permission roles="guest" type="consume"/>
<permission roles="guest" type="send"/>
</security-setting>
</security-settings>
<addresses>
<address name="exampleQueue">
<anycast>
<queue name="exampleQueue"/>
</anycast>
</address>
</addresses>
</core>
</configuration>

View File

@ -23,6 +23,7 @@ import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient;
import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;

View File

@ -48,14 +48,21 @@ under the License.
<id>examples</id>
<modules>
<module>federated-queue</module>
<module>federated-queue-downstream</module>
<module>federated-queue-downstream-upstream</module>
<module>federated-address</module>
<module>federated-address-downstream</module>
<module>federated-address-downstream-upstream</module>
</modules>
</profile>
<profile>
<id>release</id>
<modules>
<module>federated-queue</module>
<module>federated-queue-downstream</module>
<module>federated-queue-downstream-upstream</module>
<module>federated-address</module>
<module>federated-address-downstream-upstream</module>
</modules>
</profile>
</profiles>

View File

@ -16,7 +16,6 @@
*/
package org.apache.activemq.artemis.tests.integration.federation;
import java.util.Collections;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
@ -25,10 +24,17 @@ import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.Topic;
import java.util.Collections;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.TransportConfiguration;
import org.apache.activemq.artemis.core.config.FederationConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationUpstreamConfiguration;
import org.apache.activemq.artemis.core.config.TransformerConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationAddressPolicyConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationDownstreamConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationTransformerConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationUpstreamConfiguration;
import org.apache.activemq.artemis.core.server.transformer.Transformer;
import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
import org.apache.activemq.artemis.tests.util.Wait;
import org.junit.Before;
@ -46,25 +52,133 @@ public class FederatedAddressTest extends FederatedTestBase {
super.setUp();
}
protected ConnectionFactory getCF(int i) throws Exception {
return new ActiveMQConnectionFactory("vm://" + i);
}
@Test
public void testFederatedAddressReplication() throws Exception {
public void testDownstreamFederatedAddressReplication() throws Exception {
String address = getName();
FederationConfiguration federationConfiguration = createFederationConfiguration("server1", address);
FederationConfiguration federationConfiguration = createDownstreamFederationConfiguration("server1", address,
getServer(0).getConfiguration().getTransportConfigurations("server0")[0]);
getServer(0).getConfiguration().getFederationConfigurations().add(federationConfiguration);
getServer(0).getFederationManager().deploy();
FederationConfiguration federationConfiguration2 = createFederationConfiguration("server0", address);
FederationConfiguration federationConfiguration2 = createDownstreamFederationConfiguration("server0", address,
getServer(1).getConfiguration().getTransportConfigurations("server1")[0]);
getServer(1).getConfiguration().getFederationConfigurations().add(federationConfiguration2);
getServer(1).getFederationManager().deploy();
testFederatedAddressReplication(address);
}
@Test
public void testDownstreamFederatedAddressReplicationRef() throws Exception {
String address = getName();
FederationConfiguration federationConfiguration = createDownstreamFederationConfiguration("server1", address,
"server0");
getServer(0).getConfiguration().getFederationConfigurations().add(federationConfiguration);
getServer(0).getFederationManager().deploy();
FederationConfiguration federationConfiguration2 = createDownstreamFederationConfiguration("server0", address,
"server1");
getServer(1).getConfiguration().getFederationConfigurations().add(federationConfiguration2);
getServer(1).getFederationManager().deploy();
testFederatedAddressReplication(address);
}
@Test
public void testDownstreamFederatedAddressReplicationRefOneWay() throws Exception {
String address = getName();
FederationConfiguration federationConfiguration2 = createDownstreamFederationConfiguration("server0", address,
"server1");
getServer(1).getConfiguration().getFederationConfigurations().add(federationConfiguration2);
getServer(1).getFederationManager().deploy();
testFederatedAddressReplication(address);
}
@Test
public void testUpstreamFederatedAddressReplication() throws Exception {
String address = getName();
FederationConfiguration federationConfiguration = createUpstreamFederationConfiguration("server1", address);
getServer(0).getConfiguration().getFederationConfigurations().add(federationConfiguration);
getServer(0).getFederationManager().deploy();
FederationConfiguration federationConfiguration2 = createUpstreamFederationConfiguration("server0", address);
getServer(1).getConfiguration().getFederationConfigurations().add(federationConfiguration2);
getServer(1).getFederationManager().deploy();
testFederatedAddressReplication(address);
}
@Test
public void testDownstreamFederatedAddressReplicationRefOneWayTransformer() throws Exception {
String address = getName();
FederationConfiguration federationConfiguration2 = createDownstreamFederationConfiguration("server0", address, "server1");
addTransformerConfiguration(federationConfiguration2, address);
getServer(1).getConfiguration().getFederationConfigurations().add(federationConfiguration2);
getServer(1).getFederationManager().deploy();
verifyTransformer(address);
}
private void verifyTransformer(String address) throws Exception {
ConnectionFactory cf1 = getCF(1);
ConnectionFactory cf0 = getCF(0);
try (Connection connection1 = cf1.createConnection(); Connection connection0 = cf0.createConnection()) {
connection1.start();
connection0.start();
Session session1 = connection1.createSession();
Topic topic1 = session1.createTopic(address);
MessageProducer producer1 = session1.createProducer(topic1);
Session session0 = connection0.createSession();
Topic topic0 = session0.createTopic(address);
MessageConsumer consumer0 = session0.createConsumer(topic0);
assertTrue(Wait.waitFor(() -> getServer(1).getPostOffice().getBindingsForAddress(
SimpleString.toSimpleString(address)).getBindings().size() == 1));
producer1.send(session1.createTextMessage("hello"));
Message message = consumer0.receive(1000);
assertNotNull(message);
assertEquals(message.getBooleanProperty(FederatedQueueTest.TestTransformer.TEST_PROPERTY), true);
}
}
@Test
public void testUpstreamFederatedAddressReplicationOneWay() throws Exception {
String address = getName();
FederationConfiguration federationConfiguration = createUpstreamFederationConfiguration("server1", address);
getServer(0).getConfiguration().getFederationConfigurations().add(federationConfiguration);
getServer(0).getFederationManager().deploy();
testFederatedAddressReplication(address);
}
@Test
public void testUpstreamFederatedAddressReplicationOneWayTransformer() throws Exception {
String address = getName();
FederationConfiguration federationConfiguration = createUpstreamFederationConfiguration("server1", address);
addTransformerConfiguration(federationConfiguration, address);
getServer(0).getConfiguration().getFederationConfigurations().add(federationConfiguration);
getServer(0).getFederationManager().deploy();
verifyTransformer(address);
}
private void testFederatedAddressReplication(String address) throws Exception {
ConnectionFactory cf1 = getCF(1);
ConnectionFactory cf0 = getCF(0);
try (Connection connection1 = cf1.createConnection(); Connection connection0 = cf0.createConnection()) {
@ -81,7 +195,7 @@ public class FederatedAddressTest extends FederatedTestBase {
Topic topic0 = session0.createTopic(address);
MessageConsumer consumer0 = session0.createConsumer(topic0);
Wait.waitFor(() -> getServer(1).getPostOffice().getBindingsForAddress(SimpleString.toSimpleString(address)).getBindings().size() == 1);
assertTrue(Wait.waitFor(() -> getServer(1).getPostOffice().getBindingsForAddress(SimpleString.toSimpleString(address)).getBindings().size() == 1));
producer.send(session1.createTextMessage("hello"));
@ -143,7 +257,7 @@ public class FederatedAddressTest extends FederatedTestBase {
assertNull(consumer0.receive(100));
FederationConfiguration federationConfiguration = createFederationConfiguration("server1", address);
FederationConfiguration federationConfiguration = createUpstreamFederationConfiguration("server1", address);
getServer(0).getConfiguration().getFederationConfigurations().add(federationConfiguration);
getServer(0).getFederationManager().deploy();
@ -161,7 +275,7 @@ public class FederatedAddressTest extends FederatedTestBase {
public void testFederatedAddressRemoteBrokerRestart() throws Exception {
String address = getName();
FederationConfiguration federationConfiguration = createFederationConfiguration("server1", address);
FederationConfiguration federationConfiguration = createUpstreamFederationConfiguration("server1", address);
getServer(0).getConfiguration().getFederationConfigurations().add(federationConfiguration);
getServer(0).getFederationManager().deploy();
@ -219,7 +333,7 @@ public class FederatedAddressTest extends FederatedTestBase {
public void testFederatedAddressLocalBrokerRestart() throws Exception {
String address = getName();
FederationConfiguration federationConfiguration = createFederationConfiguration("server1", address);
FederationConfiguration federationConfiguration = createUpstreamFederationConfiguration("server1", address);
getServer(0).getConfiguration().getFederationConfigurations().add(federationConfiguration);
getServer(0).getFederationManager().deploy();
@ -274,27 +388,125 @@ public class FederatedAddressTest extends FederatedTestBase {
}
}
@Test
public void testFederatedAddressChainOfBrokers() throws Exception {
String address = getName();
private FederationConfiguration createFederationConfiguration(String connector, String address) {
//Set queue up on all three brokers
// for (int i = 0; i < 3; i++) {
// getServer(i).createQueue(SimpleString.toSimpleString(queueName), RoutingType.ANYCAST, SimpleString.toSimpleString(queueName), null, true, false);
// }
//Connect broker 0 (consumer will be here at end of chain) to broker 1
FederationConfiguration federationConfiguration0 = createUpstreamFederationConfiguration("server1", address, 2);
getServer(0).getConfiguration().getFederationConfigurations().add(federationConfiguration0);
getServer(0).getFederationManager().deploy();
//Connect broker 1 (middle of chain) to broker 2
FederationConfiguration federationConfiguration1 = createUpstreamFederationConfiguration("server2", address, 2);
getServer(1).getConfiguration().getFederationConfigurations().add(federationConfiguration1);
getServer(1).getFederationManager().deploy();
//Broker 2 we dont setup any federation as he is the upstream (head of the chain)
//Now the test.
ConnectionFactory cf2 = getCF(2);
ConnectionFactory cf0 = getCF(0);
try (Connection connection2 = cf2.createConnection(); Connection connection0 = cf0.createConnection()) {
connection0.start();
Session session0 = connection0.createSession();
Topic topic0 = session0.createTopic(address);
connection2.start();
Session session2 = connection2.createSession();
Topic topic2 = session2.createTopic(address);
MessageProducer producer2 = session2.createProducer(topic2);
MessageConsumer consumer0 = session0.createConsumer(topic0);
assertTrue(Wait.waitFor(() -> getServer(1).getPostOffice().getBindingsForAddress(SimpleString.toSimpleString(address)).getBindings().size() == 1));
assertTrue(Wait.waitFor(() -> getServer(2).getPostOffice().getBindingsForAddress(SimpleString.toSimpleString(address)).getBindings().size() == 1));
//Test producers being on broker 2 and consumer on broker 0, with broker 2 being in the middle of the chain.
producer2.send(session2.createTextMessage("hello"));
assertNotNull(consumer0.receive(1000));
}
}
private FederationConfiguration createFederationConfiguration(String address, int hops) {
FederationAddressPolicyConfiguration addressPolicyConfiguration = new FederationAddressPolicyConfiguration();
addressPolicyConfiguration.setName( "AddressPolicy" + address);
addressPolicyConfiguration.addInclude(new FederationAddressPolicyConfiguration.Matcher().setAddressMatch(address));
addressPolicyConfiguration.setMaxHops(hops);
FederationConfiguration federationConfiguration = new FederationConfiguration();
federationConfiguration.setName("default");
federationConfiguration.addFederationPolicy(addressPolicyConfiguration);
return federationConfiguration;
}
private FederationConfiguration createUpstreamFederationConfiguration(String connector, String address, int hops) {
FederationUpstreamConfiguration upstreamConfiguration = new FederationUpstreamConfiguration();
upstreamConfiguration.setName(connector);
upstreamConfiguration.getConnectionConfiguration().setStaticConnectors(Collections.singletonList(connector));
upstreamConfiguration.getConnectionConfiguration().setCircuitBreakerTimeout(-1);
upstreamConfiguration.addPolicyRef("AddressPolicy" + address);
FederationAddressPolicyConfiguration addressPolicyConfiguration = new FederationAddressPolicyConfiguration();
addressPolicyConfiguration.setName( "AddressPolicy" + address);
addressPolicyConfiguration.addInclude(new FederationAddressPolicyConfiguration.Matcher().setAddressMatch(address));
addressPolicyConfiguration.setMaxHops(1);
FederationConfiguration federationConfiguration = new FederationConfiguration();
federationConfiguration.setName("default");
FederationConfiguration federationConfiguration = createFederationConfiguration(address, hops);
federationConfiguration.addUpstreamConfiguration(upstreamConfiguration);
federationConfiguration.addFederationPolicy(addressPolicyConfiguration);
return federationConfiguration;
}
private FederationConfiguration createUpstreamFederationConfiguration(String connector, String address) {
return createUpstreamFederationConfiguration(connector, address, 1);
}
private FederationConfiguration createDownstreamFederationConfiguration(String connector, String address, TransportConfiguration transportConfiguration) {
return createDownstreamFederationConfiguration(connector, address, transportConfiguration, 1);
}
private FederationConfiguration createDownstreamFederationConfiguration(String connector, String address, TransportConfiguration transportConfiguration,
int hops) {
FederationDownstreamConfiguration downstreamConfiguration = new FederationDownstreamConfiguration();
downstreamConfiguration.setName(connector);
downstreamConfiguration.getConnectionConfiguration().setStaticConnectors(Collections.singletonList(connector));
downstreamConfiguration.getConnectionConfiguration().setCircuitBreakerTimeout(-1);
downstreamConfiguration.addPolicyRef("AddressPolicy" + address);
downstreamConfiguration.setUpstreamConfiguration(transportConfiguration);
FederationConfiguration federationConfiguration = createFederationConfiguration(address, hops);
federationConfiguration.addDownstreamConfiguration(downstreamConfiguration);
return federationConfiguration;
}
private FederationConfiguration createDownstreamFederationConfiguration(String connector, String address, String transportConfigurationRef,
int hops) {
FederationDownstreamConfiguration downstreamConfiguration = new FederationDownstreamConfiguration();
downstreamConfiguration.setName(connector);
downstreamConfiguration.getConnectionConfiguration().setStaticConnectors(Collections.singletonList(connector));
downstreamConfiguration.getConnectionConfiguration().setCircuitBreakerTimeout(-1);
downstreamConfiguration.addPolicyRef("AddressPolicy" + address);
downstreamConfiguration.setUpstreamConfigurationRef(transportConfigurationRef);
FederationConfiguration federationConfiguration = createFederationConfiguration(address, hops);
federationConfiguration.addDownstreamConfiguration(downstreamConfiguration);
return federationConfiguration;
}
private FederationConfiguration createDownstreamFederationConfiguration(String connector, String address, String transportConfigurationRef) {
return createDownstreamFederationConfiguration(connector, address, transportConfigurationRef, 1);
}
private void addTransformerConfiguration(final FederationConfiguration federationConfiguration, final String address) {
federationConfiguration.addTransformerConfiguration(
new FederationTransformerConfiguration("transformer", new TransformerConfiguration(TestTransformer.class.getName())));
FederationAddressPolicyConfiguration policy = (FederationAddressPolicyConfiguration) federationConfiguration.getFederationPolicyMap().get("AddressPolicy" + address);
policy.setTransformerRef("transformer");
}
private Message createTextMessage(Session session1, String group) throws JMSException {
Message message = session1.createTextMessage("hello");
@ -302,5 +514,15 @@ public class FederatedAddressTest extends FederatedTestBase {
return message;
}
public static class TestTransformer implements Transformer {
static String TEST_PROPERTY = "transformed";
@Override
public org.apache.activemq.artemis.api.core.Message transform(org.apache.activemq.artemis.api.core.Message message) {
message.putBooleanProperty(TEST_PROPERTY, true);
return message;
}
}
}

View File

@ -16,7 +16,6 @@
*/
package org.apache.activemq.artemis.tests.integration.federation;
import java.util.Collections;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
@ -26,12 +25,18 @@ import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import java.util.Collections;
import org.apache.activemq.artemis.api.core.RoutingType;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.postoffice.QueueBinding;
import org.apache.activemq.artemis.core.config.FederationConfiguration;
import org.apache.activemq.artemis.core.config.TransformerConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationDownstreamConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationQueuePolicyConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationTransformerConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationUpstreamConfiguration;
import org.apache.activemq.artemis.core.postoffice.QueueBinding;
import org.apache.activemq.artemis.core.server.transformer.Transformer;
import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
import org.apache.activemq.artemis.tests.util.Wait;
import org.junit.Before;
@ -54,16 +59,136 @@ public class FederatedQueueTest extends FederatedTestBase {
return new ActiveMQConnectionFactory("vm://" + i);
}
@Test
public void testFederatedQueueRemoteConsume() throws Exception {
public void testFederatedQueueRemoteConsumeUpstream() throws Exception {
String queueName = getName();
FederationConfiguration federationConfiguration = createFederationConfiguration("server1", queueName);
FederationConfiguration federationConfiguration = createUpstreamFederationConfiguration("server1", queueName);
getServer(0).getConfiguration().getFederationConfigurations().add(federationConfiguration);
getServer(0).getFederationManager().deploy();
testFederatedQueueRemoteConsume(queueName);
}
@Test
public void testFederatedQueueRemoteConsumeUpstreamPriorityAdjustment() throws Exception {
String queueName = getName();
FederationConfiguration federationConfiguration = createUpstreamFederationConfiguration("server1", queueName);
FederationQueuePolicyConfiguration policy = (FederationQueuePolicyConfiguration) federationConfiguration.getFederationPolicyMap().get("QueuePolicy" + queueName);
//Favor federated broker over local consumers
policy.setPriorityAdjustment(1);
getServer(0).getConfiguration().getFederationConfigurations().add(federationConfiguration);
getServer(0).getFederationManager().deploy();
testFederatedQueueRemoteConsumeUpstreamPriorityAdjustment(queueName);
}
@Test
public void testFederatedQueueRemoteConsumeDownstreamPriorityAdjustment() throws Exception {
String queueName = getName();
FederationConfiguration federationConfiguration = createDownstreamFederationConfiguration("server0", queueName, "server1");
FederationQueuePolicyConfiguration policy = (FederationQueuePolicyConfiguration) federationConfiguration.getFederationPolicyMap().get("QueuePolicy" + queueName);
//Favor federated broker over local consumers
policy.setPriorityAdjustment(1);
getServer(1).getConfiguration().getFederationConfigurations().add(federationConfiguration);
getServer(1).getFederationManager().deploy();
testFederatedQueueRemoteConsumeUpstreamPriorityAdjustment(queueName);
}
private void testFederatedQueueRemoteConsumeUpstreamPriorityAdjustment(final String queueName) throws Exception {
ConnectionFactory cf1 = getCF(1);
ConnectionFactory cf0 = getCF(0);
try (Connection connection1 = cf1.createConnection(); Connection connection0 = cf0.createConnection()) {
connection0.start();
connection1.start();
Session session0 = connection0.createSession();
Session session1 = connection1.createSession();
Queue queue0 = session0.createQueue(queueName);
Queue queue1 = session1.createQueue(queueName);
MessageConsumer consumer0 = session0.createConsumer(queue0);
MessageConsumer consumer1 = session1.createConsumer(queue1);
//Wait for local and federated consumer to be established on Server 1
assertTrue(Wait.waitFor(() -> getServer(1).locateQueue(SimpleString.toSimpleString(queueName)).getConsumerCount() == 2,
5000, 100));
MessageProducer producer1 = session1.createProducer(queue1);
producer1.send(session1.createTextMessage("hello"));
//Consumer 0 should receive the message over consumer because of adjusted priority
//to favor the federated broker
assertNull(consumer1.receive(500));
assertNotNull(consumer0.receive(1000));
consumer0.close();
consumer1.close();
}
}
private void verifyTransformer(String queueName) throws Exception {
ConnectionFactory cf1 = getCF(1);
ConnectionFactory cf0 = getCF(0);
try (Connection connection1 = cf1.createConnection(); Connection connection0 = cf0.createConnection()) {
connection1.start();
Session session1 = connection1.createSession();
Queue queue1 = session1.createQueue(queueName);
MessageProducer producer1 = session1.createProducer(queue1);
producer1.send(session1.createTextMessage("hello"));
connection0.start();
Session session0 = connection0.createSession();
Queue queue0 = session0.createQueue(queueName);
MessageConsumer consumer0 = session0.createConsumer(queue0);
Message message = consumer0.receive(1000);
assertNotNull(message);
assertEquals(message.getBooleanProperty(TestTransformer.TEST_PROPERTY), true);
}
}
@Test
public void testFederatedQueueRemoteConsumeUpstreamTransformer() throws Exception {
String queueName = getName();
FederationConfiguration federationConfiguration = createUpstreamFederationConfiguration("server1", queueName);
addTransformerConfiguration(federationConfiguration, queueName);
getServer(0).getConfiguration().getFederationConfigurations().add(federationConfiguration);
getServer(0).getFederationManager().deploy();
verifyTransformer(queueName);
}
@Test
public void testFederatedQueueRemoteConsumeDownstream() throws Exception {
String queueName = getName();
FederationConfiguration federationConfiguration = createDownstreamFederationConfiguration("server0", queueName, "server1");
getServer(1).getConfiguration().getFederationConfigurations().add(federationConfiguration);
getServer(1).getFederationManager().deploy();
testFederatedQueueRemoteConsume(queueName);
}
@Test
public void testFederatedQueueRemoteConsumeDownstreamTransformer() throws Exception {
String queueName = getName();
FederationConfiguration federationConfiguration = createDownstreamFederationConfiguration("server0", queueName, "server1");
addTransformerConfiguration(federationConfiguration, queueName);
getServer(1).getConfiguration().getFederationConfigurations().add(federationConfiguration);
getServer(1).getFederationManager().deploy();
verifyTransformer(queueName);
}
private void testFederatedQueueRemoteConsume(final String queueName) throws Exception {
ConnectionFactory cf1 = getCF(1);
ConnectionFactory cf0 = getCF(0);
try (Connection connection1 = cf1.createConnection(); Connection connection0 = cf0.createConnection()) {
@ -130,7 +255,7 @@ public class FederatedQueueTest extends FederatedTestBase {
assertNull(consumer0.receive(100));
FederationConfiguration federationConfiguration = createFederationConfiguration("server1", queueName);
FederationConfiguration federationConfiguration = createUpstreamFederationConfiguration("server1", queueName);
getServer(0).getConfiguration().getFederationConfigurations().add(federationConfiguration);
getServer(0).getFederationManager().deploy();
@ -141,20 +266,134 @@ public class FederatedQueueTest extends FederatedTestBase {
}
@Test
public void testFederatedQueueBiDirectional() throws Exception {
public void testFederatedQueueBiDirectionalUpstream() throws Exception {
String queueName = getName();
//Set queue up on both brokers
for (int i = 0; i < 2; i++) {
getServer(i).createQueue(SimpleString.toSimpleString(queueName), RoutingType.ANYCAST, SimpleString.toSimpleString(queueName), null, true, false);
}
FederationConfiguration federationConfiguration0 = createFederationConfiguration("server1", queueName);
FederationConfiguration federationConfiguration0 = createUpstreamFederationConfiguration("server1", queueName);
getServer(0).getConfiguration().getFederationConfigurations().add(federationConfiguration0);
getServer(0).getFederationManager().deploy();
FederationConfiguration federationConfiguration1 = createFederationConfiguration("server0", queueName);
FederationConfiguration federationConfiguration1 = createUpstreamFederationConfiguration("server0", queueName);
getServer(1).getConfiguration().getFederationConfigurations().add(federationConfiguration1);
getServer(1).getFederationManager().deploy();
testFederatedQueueBiDirectional(queueName, false);
}
@Test
public void testFederatedQueueBiDirectionalDownstream() throws Exception {
String queueName = getName();
//Set queue up on both brokers
for (int i = 0; i < 2; i++) {
getServer(i).createQueue(SimpleString.toSimpleString(queueName), RoutingType.ANYCAST, SimpleString.toSimpleString(queueName), null, true, false);
}
FederationConfiguration federationConfiguration0 = createDownstreamFederationConfiguration("server1", queueName, "server0");
getServer(0).getConfiguration().getFederationConfigurations().add(federationConfiguration0);
getServer(0).getFederationManager().deploy();
FederationConfiguration federationConfiguration1 = createDownstreamFederationConfiguration("server0", queueName, "server1");
getServer(1).getConfiguration().getFederationConfigurations().add(federationConfiguration1);
getServer(1).getFederationManager().deploy();
testFederatedQueueBiDirectional(queueName, false);
}
@Test
public void testFederatedQueueBiDirectionalDownstreamUpstream() throws Exception {
String queueName = getName();
//Set queue up on both brokers
for (int i = 0; i < 2; i++) {
getServer(i).createQueue(SimpleString.toSimpleString(queueName), RoutingType.ANYCAST, SimpleString.toSimpleString(queueName), null, true, false);
}
FederationConfiguration federationConfiguration0 = createDownstreamFederationConfiguration("server1-downstream",
"server1", queueName, null, false, "server0");
FederationUpstreamConfiguration upstreamConfig = createFederationUpstream("server1", queueName);
federationConfiguration0.addUpstreamConfiguration(upstreamConfig);
getServer(0).getConfiguration().getFederationConfigurations().add(federationConfiguration0);
getServer(0).getFederationManager().deploy();
testFederatedQueueBiDirectional(queueName, false);
}
@Test
public void testFederatedQueueBiDirectionalDownstreamUpstreamSharedConnection() throws Exception {
String queueName = getName();
//Set queue up on both brokers
for (int i = 0; i < 2; i++) {
getServer(i).createQueue(SimpleString.toSimpleString(queueName), RoutingType.ANYCAST, SimpleString.toSimpleString(queueName), null, true, false);
}
FederationConfiguration federationConfiguration0 = createDownstreamFederationConfiguration("server1-downstream",
"server1", queueName, null, true, "server0");
FederationUpstreamConfiguration upstreamConfig = createFederationUpstream("server1", queueName);
upstreamConfig.getConnectionConfiguration().setShareConnection(true);
federationConfiguration0.addUpstreamConfiguration(upstreamConfig);
getServer(0).getConfiguration().getFederationConfigurations().add(federationConfiguration0);
getServer(0).getFederationManager().deploy();
testFederatedQueueBiDirectional(queueName, true);
}
@Test
public void testFederatedQueueShareUpstreamConnectionFalse() throws Exception {
String queueName = getName();
//Set queue up on both brokers
for (int i = 0; i < 2; i++) {
getServer(i).createQueue(SimpleString.toSimpleString(queueName), RoutingType.ANYCAST, SimpleString.toSimpleString(queueName), null, true, false);
}
FederationConfiguration federationConfiguration0 = createDownstreamFederationConfiguration("server1-downstream",
"server1", queueName, null, false, "server0");
federationConfiguration0.addUpstreamConfiguration(createFederationUpstream("server1", queueName));
getServer(0).getConfiguration().getFederationConfigurations().add(federationConfiguration0);
getServer(0).getFederationManager().deploy();
testFederatedQueueShareUpstreamConnection(queueName, 2, 3);
}
@Test
public void testFederatedQueueShareUpstreamConnectionTrue() throws Exception {
String queueName = getName();
//Set queue up on both brokers
for (int i = 0; i < 2; i++) {
getServer(i).createQueue(SimpleString.toSimpleString(queueName), RoutingType.ANYCAST, SimpleString.toSimpleString(queueName), null, true, false);
}
FederationConfiguration federationConfiguration0 = createDownstreamFederationConfiguration("server1-downstream",
"server1", queueName, null, true, "server0");
FederationUpstreamConfiguration upstreamConfiguration = createFederationUpstream("server1", queueName);
upstreamConfiguration.getConnectionConfiguration().setShareConnection(true);
federationConfiguration0.addUpstreamConfiguration(upstreamConfiguration);
getServer(0).getConfiguration().getFederationConfigurations().add(federationConfiguration0);
getServer(0).getFederationManager().deploy();
testFederatedQueueShareUpstreamConnection(queueName, 2, 2);
}
private void testFederatedQueueShareUpstreamConnection(String queueName, int server0Connections, int server1Connections) throws Exception {
ConnectionFactory cf1 = getCF(1);
ConnectionFactory cf0 = getCF(0);
try (Connection connection1 = cf1.createConnection(); Connection connection0 = cf0.createConnection()) {
connection0.start();
connection1.start();
Session session0 = connection0.createSession();
Session session1 = connection1.createSession();
MessageConsumer consumer0 = session0.createConsumer(session0.createQueue(queueName));
MessageConsumer consumer1 = session1.createConsumer(session1.createQueue(queueName));
assertTrue(Wait.waitFor(() -> getServer(0).getConnectionCount() == server0Connections, 500, 100));
assertTrue(Wait.waitFor(() -> getServer(1).getConnectionCount() == server1Connections, 500, 100));
assertFalse(Wait.waitFor(() -> getServer(0).getConnectionCount() > server0Connections, 500, 100));
assertFalse(Wait.waitFor(() -> getServer(1).getConnectionCount() > server1Connections, 500, 100));
}
}
private void testFederatedQueueBiDirectional(String queueName, boolean shared) throws Exception {
ConnectionFactory cf1 = getCF(1);
ConnectionFactory cf0 = getCF(0);
try (Connection connection1 = cf1.createConnection(); Connection connection0 = cf0.createConnection()) {
@ -169,7 +408,6 @@ public class FederatedQueueTest extends FederatedTestBase {
MessageProducer producer1 = session1.createProducer(queue1);
MessageConsumer consumer0 = session0.createConsumer(queue0);
//Test producers being on broker 0 and broker 1 and consumer on broker 0.
producer0.send(session1.createTextMessage("hello"));
assertNotNull(consumer0.receive(1000));
@ -183,13 +421,21 @@ public class FederatedQueueTest extends FederatedTestBase {
assertFalse(Wait.waitFor(() -> getServer(1).locateQueue(SimpleString.toSimpleString(queueName)).getConsumerCount() > 1, 500, 100));
//Test consumer move from broker 0, to broker 1
final int server1ConsumerCount = getServer(1).getConnectionCount();
consumer0.close();
Wait.waitFor(() -> ((QueueBinding) getServer(0).getPostOffice().getBinding(SimpleString.toSimpleString(queueName))).consumerCount() == 0, 1000);
//Make sure we don't drop connection if shared
if (shared) {
assertFalse(Wait.waitFor(() -> getServer(1).getConnectionCount() == server1ConsumerCount - 1,
500, 100));
assertTrue(server1ConsumerCount == getServer(1).getConnectionCount());
}
MessageConsumer consumer1 = session1.createConsumer(queue1);
producer0.send(session1.createTextMessage("hello"));
assertNotNull(consumer1.receive(10000));
assertNotNull(consumer1.receive(1000));
producer1.send(session1.createTextMessage("hello"));
assertNotNull(consumer1.receive(1000));
@ -210,7 +456,6 @@ public class FederatedQueueTest extends FederatedTestBase {
}
}
@Test
public void testFederatedQueueChainOfBrokers() throws Exception {
String queueName = getName();
@ -221,12 +466,12 @@ public class FederatedQueueTest extends FederatedTestBase {
}
//Connect broker 0 (consumer will be here at end of chain) to broker 1
FederationConfiguration federationConfiguration0 = createFederationConfiguration("server1", queueName, true);
FederationConfiguration federationConfiguration0 = createUpstreamFederationConfiguration("server1", queueName, true);
getServer(0).getConfiguration().getFederationConfigurations().add(federationConfiguration0);
getServer(0).getFederationManager().deploy();
//Connect broker 1 (middle of chain) to broker 2
FederationConfiguration federationConfiguration1 = createFederationConfiguration("server2", queueName, true);
FederationConfiguration federationConfiguration1 = createUpstreamFederationConfiguration("server2", queueName, true);
getServer(1).getConfiguration().getFederationConfigurations().add(federationConfiguration1);
getServer(1).getFederationManager().deploy();
//Broker 2 we dont setup any federation as he is the upstream (head of the chain)
@ -263,7 +508,7 @@ public class FederatedQueueTest extends FederatedTestBase {
getServer(i).createQueue(SimpleString.toSimpleString(queueName), RoutingType.ANYCAST, SimpleString.toSimpleString(queueName), null, true, false);
}
FederationConfiguration federationConfiguration = createFederationConfiguration("server1", queueName);
FederationConfiguration federationConfiguration = createUpstreamFederationConfiguration("server1", queueName);
getServer(0).getConfiguration().getFederationConfigurations().add(federationConfiguration);
getServer(0).getFederationManager().deploy();
@ -307,7 +552,6 @@ public class FederatedQueueTest extends FederatedTestBase {
assertNotNull(consumer0.receive(1000));
}
@Test
public void testFederatedQueueLocalBrokerRestart() throws Exception {
String queueName = getName();
@ -317,7 +561,7 @@ public class FederatedQueueTest extends FederatedTestBase {
getServer(i).createQueue(SimpleString.toSimpleString(queueName), RoutingType.ANYCAST, SimpleString.toSimpleString(queueName), null, true, false);
}
FederationConfiguration federationConfiguration = createFederationConfiguration("server1", queueName);
FederationConfiguration federationConfiguration = createUpstreamFederationConfiguration("server1", queueName);
getServer(0).getConfiguration().getFederationConfigurations().add(federationConfiguration);
getServer(0).getFederationManager().deploy();
@ -365,17 +609,56 @@ public class FederatedQueueTest extends FederatedTestBase {
assertNotNull(consumer0.receive(1000));
}
private FederationConfiguration createFederationConfiguration(String connector, String queueName) {
return createFederationConfiguration(connector, queueName, null);
private FederationConfiguration createDownstreamFederationConfiguration(String connector, String queueName, Boolean includeFederated,
String transportConfigurationRef) {
return createDownstreamFederationConfiguration(null, connector, queueName, includeFederated, false, transportConfigurationRef);
}
private FederationConfiguration createFederationConfiguration(String connector, String queueName, Boolean includeFederated) {
private FederationConfiguration createDownstreamFederationConfiguration(String name, String connector, String queueName, Boolean includeFederated,
boolean shareConnection, String transportConfigurationRef) {
FederationDownstreamConfiguration downstreamConfiguration = new FederationDownstreamConfiguration();
downstreamConfiguration.setName(name != null ? name : connector);
downstreamConfiguration.getConnectionConfiguration().setStaticConnectors(Collections.singletonList(connector));
downstreamConfiguration.getConnectionConfiguration().setCircuitBreakerTimeout(-1);
downstreamConfiguration.getConnectionConfiguration().setShareConnection(shareConnection);
downstreamConfiguration.addPolicyRef("QueuePolicy" + queueName);
downstreamConfiguration.setUpstreamConfigurationRef(transportConfigurationRef);
FederationConfiguration federationConfiguration = createFederationConfiguration(connector, queueName, includeFederated);
federationConfiguration.addDownstreamConfiguration(downstreamConfiguration);
return federationConfiguration;
}
private FederationConfiguration createDownstreamFederationConfiguration(String connector, String queueName, String transportConfigurationRef) {
return createDownstreamFederationConfiguration(null, connector, queueName, null, false, transportConfigurationRef);
}
private FederationConfiguration createUpstreamFederationConfiguration(String connector, String queueName, Boolean includeFederated) {
FederationUpstreamConfiguration upstreamConfiguration = createFederationUpstream(connector, queueName);
FederationConfiguration federationConfiguration = createFederationConfiguration(connector, queueName, includeFederated);
federationConfiguration.addUpstreamConfiguration(upstreamConfiguration);
return federationConfiguration;
}
private FederationUpstreamConfiguration createFederationUpstream(String connector, String queueName) {
FederationUpstreamConfiguration upstreamConfiguration = new FederationUpstreamConfiguration();
upstreamConfiguration.setName(connector);
upstreamConfiguration.setName("server1-upstream");
upstreamConfiguration.getConnectionConfiguration().setStaticConnectors(Collections.singletonList(connector));
upstreamConfiguration.getConnectionConfiguration().setCircuitBreakerTimeout(-1);
upstreamConfiguration.addPolicyRef("QueuePolicy" + queueName);
return upstreamConfiguration;
}
private FederationConfiguration createUpstreamFederationConfiguration(String connector, String queueName) {
return createUpstreamFederationConfiguration(connector, queueName, null);
}
private FederationConfiguration createFederationConfiguration(String connector, String queueName, Boolean includeFederated) {
FederationQueuePolicyConfiguration queuePolicyConfiguration = new FederationQueuePolicyConfiguration();
queuePolicyConfiguration.setName( "QueuePolicy" + queueName);
@ -387,17 +670,33 @@ public class FederatedQueueTest extends FederatedTestBase {
FederationConfiguration federationConfiguration = new FederationConfiguration();
federationConfiguration.setName("default");
federationConfiguration.addUpstreamConfiguration(upstreamConfiguration);
federationConfiguration.addFederationPolicy(queuePolicyConfiguration);
return federationConfiguration;
}
private void addTransformerConfiguration(final FederationConfiguration federationConfiguration, final String queueName) {
federationConfiguration.addTransformerConfiguration(
new FederationTransformerConfiguration("transformer", new TransformerConfiguration(TestTransformer.class.getName())));
FederationQueuePolicyConfiguration policy = (FederationQueuePolicyConfiguration) federationConfiguration.getFederationPolicyMap().get("QueuePolicy" + queueName);
policy.setTransformerRef("transformer");
}
private Message createTextMessage(Session session1, String group) throws JMSException {
Message message = session1.createTextMessage("hello");
message.setStringProperty("JMSXGroupID", group);
return message;
}
public static class TestTransformer implements Transformer {
static String TEST_PROPERTY = "transformed";
@Override
public org.apache.activemq.artemis.api.core.Message transform(org.apache.activemq.artemis.api.core.Message message) {
message.putBooleanProperty(TEST_PROPERTY, true);
return message;
}
}
}

View File

@ -158,6 +158,27 @@ public abstract class ActiveMQBufferTestBase extends ActiveMQTestBase {
Assert.assertFalse(wrapper.readBoolean());
}
@Test
public void testPutNullableTrueBoolean() throws Exception {
wrapper.writeNullableBoolean(true);
Assert.assertTrue(wrapper.readNullableBoolean());
}
@Test
public void testPutNullableFalseBoolean() throws Exception {
wrapper.writeNullableBoolean(false);
Assert.assertFalse(wrapper.readNullableBoolean());
}
@Test
public void testNullBoolean() throws Exception {
wrapper.writeNullableBoolean(null);
Assert.assertNull(wrapper.readNullableBoolean());
}
@Test
public void testChar() throws Exception {
wrapper.writeChar('a');
@ -195,6 +216,21 @@ public abstract class ActiveMQBufferTestBase extends ActiveMQTestBase {
Assert.assertEquals(l, wrapper.readLong());
}
@Test
public void testNullableLong() throws Exception {
Long l = RandomUtil.randomLong();
wrapper.writeNullableLong(l);
Assert.assertEquals(l, wrapper.readNullableLong());
}
@Test
public void testNullLong() throws Exception {
wrapper.writeNullableLong(null);
Assert.assertNull(wrapper.readNullableLong());
}
@Test
public void testUnsignedShort() throws Exception {
short s1 = Short.MAX_VALUE;