diff --git a/activemq-filters/src/main/java/org/apache/activemq/MaxFrameSizeExceededException.java b/activemq-filters/src/main/java/org/apache/activemq/MaxFrameSizeExceededException.java
new file mode 100644
index 00000000..80543728
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/MaxFrameSizeExceededException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq;
+
+import java.io.IOException;
+
+/** An exception thrown when max frame size is exceeded. */
+public class MaxFrameSizeExceededException extends IOException {
+ private static final long serialVersionUID = -7681404582227153308L;
+
+ public MaxFrameSizeExceededException(String message) {
+ super(message);
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/Message.java b/activemq-filters/src/main/java/org/apache/activemq/Message.java
new file mode 100644
index 00000000..c088d330
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/Message.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq;
+
+/** Represents the JMS extension methods in Apache ActiveMQ */
+public interface Message extends jakarta.jms.Message {
+
+ /**
+ * Returns the MIME type of this mesage. This can be used in selectors to filter on the MIME types
+ * of the different JMS messages, or in the case of BlobMessage it allows you to create a selector
+ * on the MIME type of the BLOB body
+ */
+ String getJMSXMimeType();
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/ScheduledMessage.java b/activemq-filters/src/main/java/org/apache/activemq/ScheduledMessage.java
new file mode 100644
index 00000000..6074f3f0
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/ScheduledMessage.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq;
+
+public interface ScheduledMessage {
+ /**
+ * The time in milliseconds that a message will wait before being scheduled to be delivered by the
+ * broker
+ */
+ public static final String AMQ_SCHEDULED_DELAY = "AMQ_SCHEDULED_DELAY";
+ /**
+ * The time in milliseconds to wait after the start time to wait before scheduling the message
+ * again
+ */
+ public static final String AMQ_SCHEDULED_PERIOD = "AMQ_SCHEDULED_PERIOD";
+ /** The number of times to repeat scheduling a message for delivery */
+ public static final String AMQ_SCHEDULED_REPEAT = "AMQ_SCHEDULED_REPEAT";
+ /** Use a Cron tab entry to set the schedule */
+ public static final String AMQ_SCHEDULED_CRON = "AMQ_SCHEDULED_CRON";
+ /**
+ * An Id that is assigned to a Scheduled Message, this value is only available once the Message is
+ * scheduled, Messages sent to the Browse Destination or delivered to the assigned Destination
+ * will have this value set.
+ */
+ public static final String AMQ_SCHEDULED_ID = "scheduledJobId";
+
+ /**
+ * Special destination to send Message's to with an assigned "action" that the Scheduler should
+ * perform such as removing a message.
+ */
+ public static final String AMQ_SCHEDULER_MANAGEMENT_DESTINATION = "ActiveMQ.Scheduler.Management";
+ /**
+ * Used to specify that a some operation should be performed on the Scheduled Message, the Message
+ * must have an assigned Id for this action to be taken.
+ */
+ public static final String AMQ_SCHEDULER_ACTION = "AMQ_SCHEDULER_ACTION";
+
+ /** Indicates that a browse of the Scheduled Messages is being requested. */
+ public static final String AMQ_SCHEDULER_ACTION_BROWSE = "BROWSE";
+ /**
+ * Indicates that a Scheduled Message is to be remove from the Scheduler, the Id of the scheduled
+ * message must be set as a property in order for this action to have any effect.
+ */
+ public static final String AMQ_SCHEDULER_ACTION_REMOVE = "REMOVE";
+ /** Indicates that all scheduled Messages should be removed. */
+ public static final String AMQ_SCHEDULER_ACTION_REMOVEALL = "REMOVEALL";
+
+ /**
+ * A property that holds the beginning of the time interval that the specified action should be
+ * applied within. Maps to a long value that specified time in milliseconds since UTC.
+ */
+ public static final String AMQ_SCHEDULER_ACTION_START_TIME = "ACTION_START_TIME";
+ /**
+ * A property that holds the end of the time interval that the specified action should be applied
+ * within. Maps to a long value that specified time in milliseconds since UTC.
+ */
+ public static final String AMQ_SCHEDULER_ACTION_END_TIME = "ACTION_END_TIME";
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/Service.java b/activemq-filters/src/main/java/org/apache/activemq/Service.java
new file mode 100644
index 00000000..e4a00384
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/Service.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq;
+
+/**
+ * The core lifecyle interface for ActiveMQ components.
+ *
+ * If there was a standard way to do so, it'd be good to register this interface with Spring so
+ * it treats the start/stop methods as those of InitializingBean and DisposableBean
+ */
+public interface Service {
+
+ void start() throws Exception;
+
+ void stop() throws Exception;
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/broker/region/MessageReference.java b/activemq-filters/src/main/java/org/apache/activemq/broker/region/MessageReference.java
new file mode 100644
index 00000000..d32f1557
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/broker/region/MessageReference.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.broker.region;
+
+import org.apache.activemq.command.ConsumerId;
+import org.apache.activemq.command.Message;
+import org.apache.activemq.command.MessageId;
+
+/**
+ * Keeps track of a message that is flowing through the Broker. This object may hold a hard
+ * reference to the message or only hold the id of the message if the message has been persisted on
+ * in a MessageStore.
+ */
+public interface MessageReference {
+
+ MessageId getMessageId();
+
+ Message getMessageHardRef();
+
+ Message getMessage();
+
+ boolean isPersistent();
+
+ Message.MessageDestination getRegionDestination();
+
+ int getRedeliveryCounter();
+
+ void incrementRedeliveryCounter();
+
+ int getReferenceCount();
+
+ int incrementReferenceCount();
+
+ int decrementReferenceCount();
+
+ ConsumerId getTargetConsumerId();
+
+ int getSize();
+
+ long getExpiration();
+
+ String getGroupID();
+
+ int getGroupSequence();
+
+ /** Returns true if this message is expired */
+ boolean isExpired();
+
+ /** Returns true if this message is dropped. */
+ boolean isDropped();
+
+ /** @return true if the message is an advisory */
+ boolean isAdvisory();
+
+ boolean canProcessAsExpired();
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQDestination.java b/activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQDestination.java
new file mode 100644
index 00000000..c4bbc620
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQDestination.java
@@ -0,0 +1,441 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+import jakarta.jms.Destination;
+import jakarta.jms.JMSException;
+import jakarta.jms.Queue;
+import jakarta.jms.TemporaryQueue;
+import jakarta.jms.TemporaryTopic;
+import jakarta.jms.Topic;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+import org.apache.activemq.filter.AnyDestination;
+import org.apache.activemq.filter.DestinationFilter;
+import org.apache.activemq.jndi.JNDIBaseStorable;
+import org.apache.activemq.util.IntrospectionSupport;
+import org.apache.activemq.util.URISupport;
+
+/** ActiveMQDestination */
+public abstract class ActiveMQDestination extends JNDIBaseStorable
+ implements DataStructure, Destination, Externalizable, Comparable {
+
+ public static final String PATH_SEPERATOR = ".";
+ public static final char COMPOSITE_SEPERATOR = ',';
+
+ public static final byte QUEUE_TYPE = 0x01;
+ public static final byte TOPIC_TYPE = 0x02;
+ public static final byte TEMP_MASK = 0x04;
+ public static final byte TEMP_TOPIC_TYPE = TOPIC_TYPE | TEMP_MASK;
+ public static final byte TEMP_QUEUE_TYPE = QUEUE_TYPE | TEMP_MASK;
+
+ public static final String QUEUE_QUALIFIED_PREFIX = "queue://";
+ public static final String TOPIC_QUALIFIED_PREFIX = "topic://";
+ public static final String TEMP_QUEUE_QUALIFED_PREFIX = "temp-queue://";
+ public static final String TEMP_TOPIC_QUALIFED_PREFIX = "temp-topic://";
+ public static final String IS_DLQ = "isDLQ";
+
+ public static final String TEMP_DESTINATION_NAME_PREFIX = "ID:";
+
+ private static final long serialVersionUID = -3885260014960795889L;
+
+ protected String physicalName;
+
+ protected transient ActiveMQDestination[] compositeDestinations;
+ protected transient String[] destinationPaths;
+ protected transient boolean isPattern;
+ protected transient int hashValue;
+ protected Map options;
+
+ protected static UnresolvedDestinationTransformer unresolvableDestinationTransformer =
+ new DefaultUnresolvedDestinationTransformer();
+
+ public ActiveMQDestination() {}
+
+ protected ActiveMQDestination(String name) {
+ setPhysicalName(name);
+ }
+
+ public ActiveMQDestination(ActiveMQDestination composites[]) {
+ setCompositeDestinations(composites);
+ }
+
+ // static helper methods for working with destinations
+ // -------------------------------------------------------------------------
+ public static ActiveMQDestination createDestination(String name, byte defaultType) {
+ if (name.startsWith(QUEUE_QUALIFIED_PREFIX)) {
+ return new ActiveMQQueue(name.substring(QUEUE_QUALIFIED_PREFIX.length()));
+ } else if (name.startsWith(TOPIC_QUALIFIED_PREFIX)) {
+ return new ActiveMQTopic(name.substring(TOPIC_QUALIFIED_PREFIX.length()));
+ } else if (name.startsWith(TEMP_QUEUE_QUALIFED_PREFIX)) {
+ return new ActiveMQTempQueue(name.substring(TEMP_QUEUE_QUALIFED_PREFIX.length()));
+ } else if (name.startsWith(TEMP_TOPIC_QUALIFED_PREFIX)) {
+ return new ActiveMQTempTopic(name.substring(TEMP_TOPIC_QUALIFED_PREFIX.length()));
+ }
+
+ switch (defaultType) {
+ case QUEUE_TYPE:
+ return new ActiveMQQueue(name);
+ case TOPIC_TYPE:
+ return new ActiveMQTopic(name);
+ case TEMP_QUEUE_TYPE:
+ return new ActiveMQTempQueue(name);
+ case TEMP_TOPIC_TYPE:
+ return new ActiveMQTempTopic(name);
+ default:
+ throw new IllegalArgumentException("Invalid default destination type: " + defaultType);
+ }
+ }
+
+ public static ActiveMQDestination transform(Destination dest) throws JMSException {
+ if (dest == null) {
+ return null;
+ }
+ if (dest instanceof ActiveMQDestination) {
+ return (ActiveMQDestination) dest;
+ }
+
+ if (dest instanceof Queue && dest instanceof Topic) {
+ String queueName = ((Queue) dest).getQueueName();
+ String topicName = ((Topic) dest).getTopicName();
+ if (queueName != null && topicName == null) {
+ return new ActiveMQQueue(queueName);
+ } else if (queueName == null && topicName != null) {
+ return new ActiveMQTopic(topicName);
+ } else {
+ return unresolvableDestinationTransformer.transform(dest);
+ }
+ }
+ if (dest instanceof TemporaryQueue) {
+ return new ActiveMQTempQueue(((TemporaryQueue) dest).getQueueName());
+ }
+ if (dest instanceof TemporaryTopic) {
+ return new ActiveMQTempTopic(((TemporaryTopic) dest).getTopicName());
+ }
+ if (dest instanceof Queue) {
+ return new ActiveMQQueue(((Queue) dest).getQueueName());
+ }
+ if (dest instanceof Topic) {
+ return new ActiveMQTopic(((Topic) dest).getTopicName());
+ }
+ throw new JMSException(
+ "Could not transform the destination into a ActiveMQ destination: " + dest);
+ }
+
+ public static int compare(ActiveMQDestination destination, ActiveMQDestination destination2) {
+ if (destination == destination2) {
+ return 0;
+ }
+ if (destination == null || destination2 instanceof AnyDestination) {
+ return -1;
+ } else if (destination2 == null || destination instanceof AnyDestination) {
+ return 1;
+ } else {
+ if (destination.getDestinationType() == destination2.getDestinationType()) {
+
+ if (destination.isPattern() && destination2.isPattern()) {
+ if (destination.getPhysicalName().compareTo(destination2.getPhysicalName()) == 0) {
+ return 0;
+ }
+ }
+ if (destination.isPattern()) {
+ DestinationFilter filter = DestinationFilter.parseFilter(destination);
+ if (filter.matches(destination2)) {
+ return 1;
+ }
+ }
+ if (destination2.isPattern()) {
+ DestinationFilter filter = DestinationFilter.parseFilter(destination2);
+ if (filter.matches(destination)) {
+ return -1;
+ }
+ }
+ return destination.getPhysicalName().compareTo(destination2.getPhysicalName());
+
+ } else {
+ return destination.isQueue() ? -1 : 1;
+ }
+ }
+ }
+
+ @Override
+ public int compareTo(Object that) {
+ if (that instanceof ActiveMQDestination) {
+ return compare(this, (ActiveMQDestination) that);
+ }
+ if (that == null) {
+ return 1;
+ } else {
+ return getClass().getName().compareTo(that.getClass().getName());
+ }
+ }
+
+ public boolean isComposite() {
+ return compositeDestinations != null;
+ }
+
+ public ActiveMQDestination[] getCompositeDestinations() {
+ return compositeDestinations;
+ }
+
+ public void setCompositeDestinations(ActiveMQDestination[] destinations) {
+ this.compositeDestinations = destinations;
+ this.destinationPaths = null;
+ this.hashValue = 0;
+ this.isPattern = false;
+
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < destinations.length; i++) {
+ if (i != 0) {
+ sb.append(COMPOSITE_SEPERATOR);
+ }
+ if (getDestinationType() == destinations[i].getDestinationType()) {
+ sb.append(destinations[i].getPhysicalName());
+ } else {
+ sb.append(destinations[i].getQualifiedName());
+ }
+ }
+ physicalName = sb.toString();
+ }
+
+ public String getQualifiedName() {
+ if (isComposite()) {
+ return physicalName;
+ }
+ return getQualifiedPrefix() + physicalName;
+ }
+
+ protected abstract String getQualifiedPrefix();
+
+ public String getPhysicalName() {
+ return physicalName;
+ }
+
+ public void setPhysicalName(String physicalName) {
+ physicalName = physicalName.trim();
+ final int length = physicalName.length();
+
+ if (physicalName.isEmpty()) {
+ throw new IllegalArgumentException("Invalid destination name: a non-empty name is required");
+ }
+
+ // options offset
+ int p = -1;
+ boolean composite = false;
+ for (int i = 0; i < length; i++) {
+ char c = physicalName.charAt(i);
+ if (c == '?') {
+ p = i;
+ break;
+ }
+ if (c == COMPOSITE_SEPERATOR) {
+ // won't be wild card
+ isPattern = false;
+ composite = true;
+ } else if (!composite && (c == '*' || c == '>')) {
+ isPattern = true;
+ }
+ }
+ // Strip off any options
+ if (p >= 0) {
+ String optstring = physicalName.substring(p + 1);
+ physicalName = physicalName.substring(0, p);
+ try {
+ options = URISupport.parseQuery(optstring);
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException(
+ "Invalid destination name: "
+ + physicalName
+ + ", it's options are not encoded properly: "
+ + e);
+ }
+ }
+ this.physicalName = physicalName;
+ this.destinationPaths = null;
+ this.hashValue = 0;
+ if (composite) {
+ // Check to see if it is a composite.
+ Set l = new HashSet();
+ StringTokenizer iter = new StringTokenizer(physicalName, "" + COMPOSITE_SEPERATOR);
+ while (iter.hasMoreTokens()) {
+ String name = iter.nextToken().trim();
+ if (name.length() == 0) {
+ continue;
+ }
+ l.add(name);
+ }
+ compositeDestinations = new ActiveMQDestination[l.size()];
+ int counter = 0;
+ for (String dest : l) {
+ compositeDestinations[counter++] = createDestination(dest);
+ }
+ }
+ }
+
+ public ActiveMQDestination createDestination(String name) {
+ return createDestination(name, getDestinationType());
+ }
+
+ public String[] getDestinationPaths() {
+
+ if (destinationPaths != null) {
+ return destinationPaths;
+ }
+
+ List l = new ArrayList();
+ StringBuilder level = new StringBuilder();
+ final char separator = PATH_SEPERATOR.charAt(0);
+ for (char c : physicalName.toCharArray()) {
+ if (c == separator) {
+ l.add(level.toString());
+ level.delete(0, level.length());
+ } else {
+ level.append(c);
+ }
+ }
+ l.add(level.toString());
+
+ destinationPaths = new String[l.size()];
+ l.toArray(destinationPaths);
+ return destinationPaths;
+ }
+
+ public abstract byte getDestinationType();
+
+ public boolean isQueue() {
+ return false;
+ }
+
+ public boolean isTopic() {
+ return false;
+ }
+
+ public boolean isTemporary() {
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ ActiveMQDestination d = (ActiveMQDestination) o;
+ return physicalName.equals(d.physicalName);
+ }
+
+ @Override
+ public int hashCode() {
+ if (hashValue == 0) {
+ hashValue = physicalName.hashCode();
+ }
+ return hashValue;
+ }
+
+ @Override
+ public String toString() {
+ return getQualifiedName();
+ }
+
+ @Override
+ public void writeExternal(ObjectOutput out) throws IOException {
+ out.writeUTF(this.getPhysicalName());
+ out.writeObject(options);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ this.setPhysicalName(in.readUTF());
+ this.options = (Map) in.readObject();
+ }
+
+ public String getDestinationTypeAsString() {
+ switch (getDestinationType()) {
+ case QUEUE_TYPE:
+ return "Queue";
+ case TOPIC_TYPE:
+ return "Topic";
+ case TEMP_QUEUE_TYPE:
+ return "TempQueue";
+ case TEMP_TOPIC_TYPE:
+ return "TempTopic";
+ default:
+ throw new IllegalArgumentException("Invalid destination type: " + getDestinationType());
+ }
+ }
+
+ public Map getOptions() {
+ return options;
+ }
+
+ @Override
+ public boolean isMarshallAware() {
+ return false;
+ }
+
+ @Override
+ public void buildFromProperties(Properties properties) {
+ if (properties == null) {
+ properties = new Properties();
+ }
+
+ IntrospectionSupport.setProperties(this, properties);
+ }
+
+ @Override
+ public void populateProperties(Properties props) {
+ props.setProperty("physicalName", getPhysicalName());
+ }
+
+ public boolean isPattern() {
+ return isPattern;
+ }
+
+ public boolean isDLQ() {
+ return options != null && options.containsKey(IS_DLQ);
+ }
+
+ public void setDLQ(boolean val) {
+ if (options == null) {
+ options = new HashMap();
+ }
+ options.put(IS_DLQ, String.valueOf(val));
+ }
+
+ public static UnresolvedDestinationTransformer getUnresolvableDestinationTransformer() {
+ return unresolvableDestinationTransformer;
+ }
+
+ public static void setUnresolvableDestinationTransformer(
+ UnresolvedDestinationTransformer unresolvableDestinationTransformer) {
+ ActiveMQDestination.unresolvableDestinationTransformer = unresolvableDestinationTransformer;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQMessage.java b/activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQMessage.java
new file mode 100644
index 00000000..5f77551b
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQMessage.java
@@ -0,0 +1,840 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+import jakarta.jms.DeliveryMode;
+import jakarta.jms.Destination;
+import jakarta.jms.JMSException;
+import jakarta.jms.MessageFormatException;
+import jakarta.jms.MessageNotWriteableException;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+import org.apache.activemq.ScheduledMessage;
+import org.apache.activemq.filter.PropertyExpression;
+import org.apache.activemq.util.Callback;
+import org.apache.activemq.util.JMSExceptionSupport;
+import org.apache.activemq.util.TypeConversionSupport;
+import org.fusesource.hawtbuf.UTF8Buffer;
+
+public class ActiveMQMessage extends Message
+ implements org.apache.activemq.Message, ScheduledMessage {
+ public static final byte DATA_STRUCTURE_TYPE = CommandTypes.ACTIVEMQ_MESSAGE;
+ public static final String DLQ_DELIVERY_FAILURE_CAUSE_PROPERTY = "dlqDeliveryFailureCause";
+ public static final String BROKER_PATH_PROPERTY = "JMSActiveMQBrokerPath";
+
+ private static final Map JMS_PROPERTY_SETERS =
+ new HashMap();
+
+ protected transient Callback acknowledgeCallback;
+
+ @Override
+ public byte getDataStructureType() {
+ return DATA_STRUCTURE_TYPE;
+ }
+
+ @Override
+ public Message copy() {
+ ActiveMQMessage copy = new ActiveMQMessage();
+ copy(copy);
+ return copy;
+ }
+
+ protected void copy(ActiveMQMessage copy) {
+ super.copy(copy);
+ copy.acknowledgeCallback = acknowledgeCallback;
+ }
+
+ @Override
+ public int hashCode() {
+ MessageId id = getMessageId();
+ if (id != null) {
+ return id.hashCode();
+ } else {
+ return super.hashCode();
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || o.getClass() != getClass()) {
+ return false;
+ }
+
+ ActiveMQMessage msg = (ActiveMQMessage) o;
+ MessageId oMsg = msg.getMessageId();
+ MessageId thisMsg = this.getMessageId();
+ return thisMsg != null && oMsg != null && oMsg.equals(thisMsg);
+ }
+
+ @Override
+ public void acknowledge() throws JMSException {
+ if (acknowledgeCallback != null) {
+ try {
+ acknowledgeCallback.execute();
+ } catch (JMSException e) {
+ throw e;
+ } catch (Throwable e) {
+ throw JMSExceptionSupport.create(e);
+ }
+ }
+ }
+
+ @Override
+ public void clearBody() throws JMSException {
+ setContent(null);
+ readOnlyBody = false;
+ }
+
+ @Override
+ public String getJMSMessageID() {
+ MessageId messageId = this.getMessageId();
+ if (messageId == null) {
+ return null;
+ }
+ return messageId.toString();
+ }
+
+ /**
+ * Seems to be invalid because the parameter doesn't initialize MessageId instance variables
+ * ProducerId and ProducerSequenceId
+ *
+ * @param value
+ * @throws JMSException
+ */
+ @Override
+ public void setJMSMessageID(String value) throws JMSException {
+ if (value != null) {
+ try {
+ MessageId id = new MessageId(value);
+ this.setMessageId(id);
+ } catch (NumberFormatException e) {
+ // we must be some foreign JMS provider or strange user-supplied
+ // String
+ // so lets set the IDs to be 1
+ MessageId id = new MessageId();
+ id.setTextView(value);
+ this.setMessageId(id);
+ }
+ } else {
+ this.setMessageId(null);
+ }
+ }
+
+ /**
+ * This will create an object of MessageId. For it to be valid, the instance variable ProducerId
+ * and producerSequenceId must be initialized.
+ *
+ * @param producerId
+ * @param producerSequenceId
+ * @throws JMSException
+ */
+ public void setJMSMessageID(ProducerId producerId, long producerSequenceId) throws JMSException {
+ MessageId id = null;
+ try {
+ id = new MessageId(producerId, producerSequenceId);
+ this.setMessageId(id);
+ } catch (Throwable e) {
+ throw JMSExceptionSupport.create(
+ "Invalid message id '" + id + "', reason: " + e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public long getJMSTimestamp() {
+ return this.getTimestamp();
+ }
+
+ @Override
+ public void setJMSTimestamp(long timestamp) {
+ this.setTimestamp(timestamp);
+ }
+
+ @Override
+ public String getJMSCorrelationID() {
+ return this.getCorrelationId();
+ }
+
+ @Override
+ public void setJMSCorrelationID(String correlationId) {
+ this.setCorrelationId(correlationId);
+ }
+
+ @Override
+ public byte[] getJMSCorrelationIDAsBytes() throws JMSException {
+ return encodeString(this.getCorrelationId());
+ }
+
+ @Override
+ public void setJMSCorrelationIDAsBytes(byte[] correlationId) throws JMSException {
+ this.setCorrelationId(decodeString(correlationId));
+ }
+
+ @Override
+ public String getJMSXMimeType() {
+ return "jms/message";
+ }
+
+ protected static String decodeString(byte[] data) throws JMSException {
+ try {
+ if (data == null) {
+ return null;
+ }
+ return new String(data, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new JMSException("Invalid UTF-8 encoding: " + e.getMessage());
+ }
+ }
+
+ protected static byte[] encodeString(String data) throws JMSException {
+ try {
+ if (data == null) {
+ return null;
+ }
+ return data.getBytes("UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new JMSException("Invalid UTF-8 encoding: " + e.getMessage());
+ }
+ }
+
+ @Override
+ public Destination getJMSReplyTo() {
+ return this.getReplyTo();
+ }
+
+ @Override
+ public void setJMSReplyTo(Destination destination) throws JMSException {
+ this.setReplyTo(ActiveMQDestination.transform(destination));
+ }
+
+ @Override
+ public Destination getJMSDestination() {
+ return this.getDestination();
+ }
+
+ @Override
+ public void setJMSDestination(Destination destination) throws JMSException {
+ this.setDestination(ActiveMQDestination.transform(destination));
+ }
+
+ @Override
+ public int getJMSDeliveryMode() {
+ return this.isPersistent() ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT;
+ }
+
+ @Override
+ public void setJMSDeliveryMode(int mode) {
+ this.setPersistent(mode == DeliveryMode.PERSISTENT);
+ }
+
+ @Override
+ public boolean getJMSRedelivered() {
+ return this.isRedelivered();
+ }
+
+ @Override
+ public void setJMSRedelivered(boolean redelivered) {
+ this.setRedelivered(redelivered);
+ }
+
+ @Override
+ public String getJMSType() {
+ return this.getType();
+ }
+
+ @Override
+ public void setJMSType(String type) {
+ this.setType(type);
+ }
+
+ @Override
+ public long getJMSExpiration() {
+ return this.getExpiration();
+ }
+
+ @Override
+ public void setJMSExpiration(long expiration) {
+ this.setExpiration(expiration);
+ }
+
+ @Override
+ public int getJMSPriority() {
+ return this.getPriority();
+ }
+
+ @Override
+ public void setJMSPriority(int priority) {
+ this.setPriority((byte) priority);
+ }
+
+ @Override
+ public void clearProperties() {
+ super.clearProperties();
+ readOnlyProperties = false;
+ }
+
+ @Override
+ public boolean propertyExists(String name) throws JMSException {
+ try {
+ return (this.getProperties().containsKey(name) || getObjectProperty(name) != null);
+ } catch (IOException e) {
+ throw JMSExceptionSupport.create(e);
+ }
+ }
+
+ @Override
+ @SuppressWarnings("rawtypes")
+ public Enumeration getPropertyNames() throws JMSException {
+ try {
+ Vector result = new Vector(this.getProperties().keySet());
+ if (getRedeliveryCounter() != 0) {
+ result.add("JMSXDeliveryCount");
+ }
+ if (getGroupID() != null) {
+ result.add("JMSXGroupID");
+ }
+ if (getGroupID() != null) {
+ result.add("JMSXGroupSeq");
+ }
+ if (getUserID() != null) {
+ result.add("JMSXUserID");
+ }
+ return result.elements();
+ } catch (IOException e) {
+ throw JMSExceptionSupport.create(e);
+ }
+ }
+
+ /**
+ * return all property names, including standard JMS properties and JMSX properties
+ *
+ * @return Enumeration of all property names on this message
+ * @throws JMSException
+ */
+ @SuppressWarnings("rawtypes")
+ public Enumeration getAllPropertyNames() throws JMSException {
+ try {
+ Vector result = new Vector(this.getProperties().keySet());
+ result.addAll(JMS_PROPERTY_SETERS.keySet());
+ return result.elements();
+ } catch (IOException e) {
+ throw JMSExceptionSupport.create(e);
+ }
+ }
+
+ interface PropertySetter {
+ void set(Message message, Object value) throws MessageFormatException;
+ }
+
+ static {
+ JMS_PROPERTY_SETERS.put(
+ "JMSXDeliveryCount",
+ new PropertySetter() {
+ @Override
+ public void set(Message message, Object value) throws MessageFormatException {
+ Integer rc = (Integer) TypeConversionSupport.convert(value, Integer.class);
+ if (rc == null) {
+ throw new MessageFormatException(
+ "Property JMSXDeliveryCount cannot be set from a "
+ + value.getClass().getName()
+ + ".");
+ }
+ message.setRedeliveryCounter(rc - 1);
+ }
+ });
+ JMS_PROPERTY_SETERS.put(
+ "JMSXGroupID",
+ new PropertySetter() {
+ @Override
+ public void set(Message message, Object value) throws MessageFormatException {
+ String rc = (String) TypeConversionSupport.convert(value, String.class);
+ if (rc == null) {
+ throw new MessageFormatException(
+ "Property JMSXGroupID cannot be set from a " + value.getClass().getName() + ".");
+ }
+ message.setGroupID(rc);
+ }
+ });
+ JMS_PROPERTY_SETERS.put(
+ "JMSXGroupSeq",
+ new PropertySetter() {
+ @Override
+ public void set(Message message, Object value) throws MessageFormatException {
+ Integer rc = (Integer) TypeConversionSupport.convert(value, Integer.class);
+ if (rc == null) {
+ throw new MessageFormatException(
+ "Property JMSXGroupSeq cannot be set from a " + value.getClass().getName() + ".");
+ }
+ message.setGroupSequence(rc);
+ }
+ });
+ JMS_PROPERTY_SETERS.put(
+ "JMSCorrelationID",
+ new PropertySetter() {
+ @Override
+ public void set(Message message, Object value) throws MessageFormatException {
+ String rc = (String) TypeConversionSupport.convert(value, String.class);
+ if (rc == null) {
+ throw new MessageFormatException(
+ "Property JMSCorrelationID cannot be set from a "
+ + value.getClass().getName()
+ + ".");
+ }
+ ((ActiveMQMessage) message).setJMSCorrelationID(rc);
+ }
+ });
+ JMS_PROPERTY_SETERS.put(
+ "JMSDeliveryMode",
+ new PropertySetter() {
+ @Override
+ public void set(Message message, Object value) throws MessageFormatException {
+ Integer rc = null;
+ try {
+ rc = (Integer) TypeConversionSupport.convert(value, Integer.class);
+ } catch (NumberFormatException nfe) {
+ if (value instanceof String) {
+ if (((String) value).equalsIgnoreCase("PERSISTENT")) {
+ rc = DeliveryMode.PERSISTENT;
+ } else if (((String) value).equalsIgnoreCase("NON_PERSISTENT")) {
+ rc = DeliveryMode.NON_PERSISTENT;
+ } else {
+ throw nfe;
+ }
+ }
+ }
+ if (rc == null) {
+ Boolean bool = (Boolean) TypeConversionSupport.convert(value, Boolean.class);
+ if (bool == null) {
+ throw new MessageFormatException(
+ "Property JMSDeliveryMode cannot be set from a "
+ + value.getClass().getName()
+ + ".");
+ } else {
+ rc = bool ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT;
+ }
+ }
+ ((ActiveMQMessage) message).setJMSDeliveryMode(rc);
+ }
+ });
+ JMS_PROPERTY_SETERS.put(
+ "JMSExpiration",
+ new PropertySetter() {
+ @Override
+ public void set(Message message, Object value) throws MessageFormatException {
+ Long rc = (Long) TypeConversionSupport.convert(value, Long.class);
+ if (rc == null) {
+ throw new MessageFormatException(
+ "Property JMSExpiration cannot be set from a "
+ + value.getClass().getName()
+ + ".");
+ }
+ ((ActiveMQMessage) message).setJMSExpiration(rc);
+ }
+ });
+ JMS_PROPERTY_SETERS.put(
+ "JMSPriority",
+ new PropertySetter() {
+ @Override
+ public void set(Message message, Object value) throws MessageFormatException {
+ Integer rc = (Integer) TypeConversionSupport.convert(value, Integer.class);
+ if (rc == null) {
+ throw new MessageFormatException(
+ "Property JMSPriority cannot be set from a " + value.getClass().getName() + ".");
+ }
+ ((ActiveMQMessage) message).setJMSPriority(rc);
+ }
+ });
+ JMS_PROPERTY_SETERS.put(
+ "JMSRedelivered",
+ new PropertySetter() {
+ @Override
+ public void set(Message message, Object value) throws MessageFormatException {
+ Boolean rc = (Boolean) TypeConversionSupport.convert(value, Boolean.class);
+ if (rc == null) {
+ throw new MessageFormatException(
+ "Property JMSRedelivered cannot be set from a "
+ + value.getClass().getName()
+ + ".");
+ }
+ ((ActiveMQMessage) message).setJMSRedelivered(rc);
+ }
+ });
+ JMS_PROPERTY_SETERS.put(
+ "JMSReplyTo",
+ new PropertySetter() {
+ @Override
+ public void set(Message message, Object value) throws MessageFormatException {
+ ActiveMQDestination rc =
+ (ActiveMQDestination)
+ TypeConversionSupport.convert(value, ActiveMQDestination.class);
+ if (rc == null) {
+ throw new MessageFormatException(
+ "Property JMSReplyTo cannot be set from a " + value.getClass().getName() + ".");
+ }
+ ((ActiveMQMessage) message).setReplyTo(rc);
+ }
+ });
+ JMS_PROPERTY_SETERS.put(
+ "JMSTimestamp",
+ new PropertySetter() {
+ @Override
+ public void set(Message message, Object value) throws MessageFormatException {
+ Long rc = (Long) TypeConversionSupport.convert(value, Long.class);
+ if (rc == null) {
+ throw new MessageFormatException(
+ "Property JMSTimestamp cannot be set from a " + value.getClass().getName() + ".");
+ }
+ ((ActiveMQMessage) message).setJMSTimestamp(rc);
+ }
+ });
+ JMS_PROPERTY_SETERS.put(
+ "JMSType",
+ new PropertySetter() {
+ @Override
+ public void set(Message message, Object value) throws MessageFormatException {
+ String rc = (String) TypeConversionSupport.convert(value, String.class);
+ if (rc == null) {
+ throw new MessageFormatException(
+ "Property JMSType cannot be set from a " + value.getClass().getName() + ".");
+ }
+ ((ActiveMQMessage) message).setJMSType(rc);
+ }
+ });
+ }
+
+ @Override
+ public void setObjectProperty(String name, Object value) throws JMSException {
+ setObjectProperty(name, value, true);
+ }
+
+ public void setObjectProperty(String name, Object value, boolean checkReadOnly)
+ throws JMSException {
+
+ if (checkReadOnly) {
+ checkReadOnlyProperties();
+ }
+ if (name == null || name.equals("")) {
+ throw new IllegalArgumentException("Property name cannot be empty or null");
+ }
+
+ if (value instanceof UTF8Buffer) {
+ value = value.toString();
+ }
+
+ PropertySetter setter = JMS_PROPERTY_SETERS.get(name);
+
+ if (setter != null && value != null) {
+ setter.set(this, value);
+ } else {
+ try {
+ this.setProperty(name, value);
+ } catch (IOException e) {
+ throw JMSExceptionSupport.create(e);
+ }
+ }
+ }
+
+ public void setProperties(Map properties) throws JMSException {
+ for (Map.Entry entry : properties.entrySet()) {
+ // Lets use the object property method as we may contain standard
+ // extension headers like JMSXGroupID
+ setObjectProperty(entry.getKey(), entry.getValue());
+ }
+ }
+
+ @Override
+ public Object getObjectProperty(String name) throws JMSException {
+ if (name == null) {
+ throw new NullPointerException("Property name cannot be null");
+ }
+
+ // PropertyExpression handles converting message headers to properties.
+ PropertyExpression expression = new PropertyExpression(name);
+ return expression.evaluate(this);
+ }
+
+ @Override
+ public boolean getBooleanProperty(String name) throws JMSException {
+ Object value = getObjectProperty(name);
+ if (value == null) {
+ return false;
+ }
+ Boolean rc = (Boolean) TypeConversionSupport.convert(value, Boolean.class);
+ if (rc == null) {
+ throw new MessageFormatException(
+ "Property "
+ + name
+ + " was a "
+ + value.getClass().getName()
+ + " and cannot be read as a boolean");
+ }
+ return rc;
+ }
+
+ @Override
+ public byte getByteProperty(String name) throws JMSException {
+ Object value = getObjectProperty(name);
+ if (value == null) {
+ throw new NumberFormatException("property " + name + " was null");
+ }
+ Byte rc = (Byte) TypeConversionSupport.convert(value, Byte.class);
+ if (rc == null) {
+ throw new MessageFormatException(
+ "Property "
+ + name
+ + " was a "
+ + value.getClass().getName()
+ + " and cannot be read as a byte");
+ }
+ return rc;
+ }
+
+ @Override
+ public short getShortProperty(String name) throws JMSException {
+ Object value = getObjectProperty(name);
+ if (value == null) {
+ throw new NumberFormatException("property " + name + " was null");
+ }
+ Short rc = (Short) TypeConversionSupport.convert(value, Short.class);
+ if (rc == null) {
+ throw new MessageFormatException(
+ "Property "
+ + name
+ + " was a "
+ + value.getClass().getName()
+ + " and cannot be read as a short");
+ }
+ return rc;
+ }
+
+ @Override
+ public int getIntProperty(String name) throws JMSException {
+ Object value = getObjectProperty(name);
+ if (value == null) {
+ throw new NumberFormatException("property " + name + " was null");
+ }
+ Integer rc = (Integer) TypeConversionSupport.convert(value, Integer.class);
+ if (rc == null) {
+ throw new MessageFormatException(
+ "Property "
+ + name
+ + " was a "
+ + value.getClass().getName()
+ + " and cannot be read as an integer");
+ }
+ return rc;
+ }
+
+ @Override
+ public long getLongProperty(String name) throws JMSException {
+ Object value = getObjectProperty(name);
+ if (value == null) {
+ throw new NumberFormatException("property " + name + " was null");
+ }
+ Long rc = (Long) TypeConversionSupport.convert(value, Long.class);
+ if (rc == null) {
+ throw new MessageFormatException(
+ "Property "
+ + name
+ + " was a "
+ + value.getClass().getName()
+ + " and cannot be read as a long");
+ }
+ return rc;
+ }
+
+ @Override
+ public float getFloatProperty(String name) throws JMSException {
+ Object value = getObjectProperty(name);
+ if (value == null) {
+ throw new NullPointerException("property " + name + " was null");
+ }
+ Float rc = (Float) TypeConversionSupport.convert(value, Float.class);
+ if (rc == null) {
+ throw new MessageFormatException(
+ "Property "
+ + name
+ + " was a "
+ + value.getClass().getName()
+ + " and cannot be read as a float");
+ }
+ return rc;
+ }
+
+ @Override
+ public double getDoubleProperty(String name) throws JMSException {
+ Object value = getObjectProperty(name);
+ if (value == null) {
+ throw new NullPointerException("property " + name + " was null");
+ }
+ Double rc = (Double) TypeConversionSupport.convert(value, Double.class);
+ if (rc == null) {
+ throw new MessageFormatException(
+ "Property "
+ + name
+ + " was a "
+ + value.getClass().getName()
+ + " and cannot be read as a double");
+ }
+ return rc;
+ }
+
+ @Override
+ public String getStringProperty(String name) throws JMSException {
+ Object value = null;
+ if ("JMSXUserID".equals(name)) {
+ value = getUserID();
+ if (value == null) {
+ value = getObjectProperty(name);
+ }
+ } else {
+ value = getObjectProperty(name);
+ }
+ if (value == null) {
+ return null;
+ }
+ String rc = (String) TypeConversionSupport.convert(value, String.class);
+ if (rc == null) {
+ throw new MessageFormatException(
+ "Property "
+ + name
+ + " was a "
+ + value.getClass().getName()
+ + " and cannot be read as a String");
+ }
+ return rc;
+ }
+
+ @Override
+ public void setBooleanProperty(String name, boolean value) throws JMSException {
+ setBooleanProperty(name, value, true);
+ }
+
+ public void setBooleanProperty(String name, boolean value, boolean checkReadOnly)
+ throws JMSException {
+ setObjectProperty(name, value, checkReadOnly);
+ }
+
+ @Override
+ public void setByteProperty(String name, byte value) throws JMSException {
+ setObjectProperty(name, value);
+ }
+
+ @Override
+ public void setShortProperty(String name, short value) throws JMSException {
+ setObjectProperty(name, value);
+ }
+
+ @Override
+ public void setIntProperty(String name, int value) throws JMSException {
+ setObjectProperty(name, value);
+ }
+
+ @Override
+ public void setLongProperty(String name, long value) throws JMSException {
+ setObjectProperty(name, value);
+ }
+
+ @Override
+ public void setFloatProperty(String name, float value) throws JMSException {
+ setObjectProperty(name, value);
+ }
+
+ @Override
+ public void setDoubleProperty(String name, double value) throws JMSException {
+ setObjectProperty(name, value);
+ }
+
+ @Override
+ public void setStringProperty(String name, String value) throws JMSException {
+ setObjectProperty(name, value);
+ }
+
+ protected void checkReadOnlyProperties() throws MessageNotWriteableException {
+ if (readOnlyProperties) {
+ throw new MessageNotWriteableException("Message properties are read-only");
+ }
+ }
+
+ protected void checkReadOnlyBody() throws MessageNotWriteableException {
+ if (readOnlyBody) {
+ throw new MessageNotWriteableException("Message body is read-only");
+ }
+ }
+
+ public Callback getAcknowledgeCallback() {
+ return acknowledgeCallback;
+ }
+
+ public void setAcknowledgeCallback(Callback acknowledgeCallback) {
+ this.acknowledgeCallback = acknowledgeCallback;
+ }
+
+ /** Send operation event listener. Used to get the message ready to be sent. */
+ public void onSend() throws JMSException {
+ setReadOnlyBody(true);
+ setReadOnlyProperties(true);
+ }
+
+ @Override
+ public void storeContent() {}
+
+ @Override
+ public void storeContentAndClear() {
+ storeContent();
+ }
+
+ @Override
+ protected boolean isContentMarshalled() {
+ // Always return true because ActiveMQMessage only has a content field
+ // which is already marshalled
+ return true;
+ }
+
+ @Override
+ public long getJMSDeliveryTime() throws JMSException {
+ return deliveryTime;
+ }
+
+ @Override
+ public void setJMSDeliveryTime(long deliveryTime) throws JMSException {
+ this.deliveryTime = deliveryTime;
+ }
+
+ @Override
+ public final T getBody(Class asType) throws JMSException {
+ if (isBodyAssignableTo(asType)) {
+ return doGetBody(asType);
+ }
+
+ throw new MessageFormatException("Message body cannot be read as type: " + asType);
+ }
+
+ @Override
+ public boolean isBodyAssignableTo(Class c) throws JMSException {
+ return true;
+ }
+
+ protected T doGetBody(Class asType) throws JMSException {
+ return null;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQQueue.java b/activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQQueue.java
new file mode 100644
index 00000000..2c2aeb73
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQQueue.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+import jakarta.jms.JMSException;
+import jakarta.jms.Queue;
+
+/** element="queue" description="An ActiveMQ Queue Destination" */
+public class ActiveMQQueue extends ActiveMQDestination implements Queue {
+
+ public static final byte DATA_STRUCTURE_TYPE = CommandTypes.ACTIVEMQ_QUEUE;
+ private static final long serialVersionUID = -3885260014960795889L;
+
+ public ActiveMQQueue() {}
+
+ public ActiveMQQueue(String name) {
+ super(name);
+ }
+
+ public byte getDataStructureType() {
+ return DATA_STRUCTURE_TYPE;
+ }
+
+ public boolean isQueue() {
+ return true;
+ }
+
+ public String getQueueName() throws JMSException {
+ return getPhysicalName();
+ }
+
+ public byte getDestinationType() {
+ return QUEUE_TYPE;
+ }
+
+ protected String getQualifiedPrefix() {
+ return QUEUE_QUALIFIED_PREFIX;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQTempDestination.java b/activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQTempDestination.java
new file mode 100644
index 00000000..3442d38b
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQTempDestination.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+import jakarta.jms.JMSException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class ActiveMQTempDestination extends ActiveMQDestination {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ActiveMQTempDestination.class);
+ protected transient String connectionId;
+ protected transient int sequenceId;
+
+ public ActiveMQTempDestination() {}
+
+ public ActiveMQTempDestination(String name) {
+ super(name);
+ }
+
+ public ActiveMQTempDestination(String connectionId, long sequenceId) {
+ super(connectionId + ":" + sequenceId);
+ }
+
+ public boolean isTemporary() {
+ return true;
+ }
+
+ public void delete() throws JMSException {}
+
+ public void setPhysicalName(String physicalName) {
+ super.setPhysicalName(physicalName);
+ if (!isComposite()) {
+ // Parse off the sequenceId off the end.
+ // this can fail if the temp destination is
+ // generated by another JMS system via the JMS<->JMS Bridge
+ int p = this.physicalName.lastIndexOf(":");
+ if (p >= 0) {
+ String seqStr = this.physicalName.substring(p + 1).trim();
+ if (seqStr != null && seqStr.length() > 0) {
+ try {
+ sequenceId = Integer.parseInt(seqStr);
+ } catch (NumberFormatException e) {
+ LOG.debug("Did not parse sequence Id from " + physicalName);
+ }
+ // The rest should be the connection id.
+ connectionId = this.physicalName.substring(0, p);
+ }
+ }
+ }
+ }
+
+ public String getConnectionId() {
+ return connectionId;
+ }
+
+ public void setConnectionId(String connectionId) {
+ this.connectionId = connectionId;
+ }
+
+ public int getSequenceId() {
+ return sequenceId;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQTempQueue.java b/activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQTempQueue.java
new file mode 100644
index 00000000..7890e196
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQTempQueue.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+import jakarta.jms.JMSException;
+import jakarta.jms.TemporaryQueue;
+
+/** element="tempQueue" description="An ActiveMQ Temporary Queue Destination" */
+public class ActiveMQTempQueue extends ActiveMQTempDestination implements TemporaryQueue {
+
+ public static final byte DATA_STRUCTURE_TYPE = CommandTypes.ACTIVEMQ_TEMP_QUEUE;
+ private static final long serialVersionUID = 6683049467527633867L;
+
+ public ActiveMQTempQueue() {}
+
+ public ActiveMQTempQueue(String name) {
+ super(name);
+ }
+
+ public ActiveMQTempQueue(ConnectionId connectionId, long sequenceId) {
+ super(connectionId.getValue(), sequenceId);
+ }
+
+ public byte getDataStructureType() {
+ return DATA_STRUCTURE_TYPE;
+ }
+
+ public boolean isQueue() {
+ return true;
+ }
+
+ public String getQueueName() throws JMSException {
+ return getPhysicalName();
+ }
+
+ public byte getDestinationType() {
+ return TEMP_QUEUE_TYPE;
+ }
+
+ protected String getQualifiedPrefix() {
+ return TEMP_QUEUE_QUALIFED_PREFIX;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQTempTopic.java b/activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQTempTopic.java
new file mode 100644
index 00000000..6c950ede
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQTempTopic.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+import jakarta.jms.JMSException;
+import jakarta.jms.TemporaryTopic;
+
+/** element="tempTopic" description="An ActiveMQ Temporary Topic Destination" */
+public class ActiveMQTempTopic extends ActiveMQTempDestination implements TemporaryTopic {
+
+ public static final byte DATA_STRUCTURE_TYPE = CommandTypes.ACTIVEMQ_TEMP_TOPIC;
+ private static final long serialVersionUID = -4325596784597300253L;
+
+ public ActiveMQTempTopic() {}
+
+ public ActiveMQTempTopic(String name) {
+ super(name);
+ }
+
+ public ActiveMQTempTopic(ConnectionId connectionId, long sequenceId) {
+ super(connectionId.getValue(), sequenceId);
+ }
+
+ public byte getDataStructureType() {
+ return DATA_STRUCTURE_TYPE;
+ }
+
+ public boolean isTopic() {
+ return true;
+ }
+
+ public String getTopicName() throws JMSException {
+ return getPhysicalName();
+ }
+
+ public byte getDestinationType() {
+ return TEMP_TOPIC_TYPE;
+ }
+
+ protected String getQualifiedPrefix() {
+ return TEMP_TOPIC_QUALIFED_PREFIX;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQTopic.java b/activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQTopic.java
new file mode 100644
index 00000000..a1acc835
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQTopic.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+import jakarta.jms.JMSException;
+import jakarta.jms.Topic;
+
+/** element="topic" description="An ActiveMQ Topic Destination" code="101" */
+public class ActiveMQTopic extends ActiveMQDestination implements Topic {
+
+ public static final byte DATA_STRUCTURE_TYPE = CommandTypes.ACTIVEMQ_TOPIC;
+ private static final long serialVersionUID = 7300307405896488588L;
+
+ public ActiveMQTopic() {}
+
+ public ActiveMQTopic(String name) {
+ super(name);
+ }
+
+ public byte getDataStructureType() {
+ return DATA_STRUCTURE_TYPE;
+ }
+
+ public boolean isTopic() {
+ return true;
+ }
+
+ public String getTopicName() throws JMSException {
+ return getPhysicalName();
+ }
+
+ public byte getDestinationType() {
+ return TOPIC_TYPE;
+ }
+
+ protected String getQualifiedPrefix() {
+ return TOPIC_QUALIFIED_PREFIX;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/BaseCommand.java b/activemq-filters/src/main/java/org/apache/activemq/command/BaseCommand.java
new file mode 100644
index 00000000..bc74ba04
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/BaseCommand.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+import java.util.Map;
+import org.apache.activemq.util.IntrospectionSupport;
+
+public abstract class BaseCommand implements Command {
+
+ protected int commandId;
+ protected boolean responseRequired;
+
+ private transient Endpoint from;
+ private transient Endpoint to;
+
+ public void copy(BaseCommand copy) {
+ copy.commandId = commandId;
+ copy.responseRequired = responseRequired;
+ }
+
+ /** @openwire:property version=1 */
+ @Override
+ public int getCommandId() {
+ return commandId;
+ }
+
+ @Override
+ public void setCommandId(int commandId) {
+ this.commandId = commandId;
+ }
+
+ /** @openwire:property version=1 */
+ @Override
+ public boolean isResponseRequired() {
+ return responseRequired;
+ }
+
+ @Override
+ public void setResponseRequired(boolean responseRequired) {
+ this.responseRequired = responseRequired;
+ }
+
+ @Override
+ public String toString() {
+ return toString(null);
+ }
+
+ public String toString(Map overrideFields) {
+ return IntrospectionSupport.toString(this, BaseCommand.class, overrideFields);
+ }
+
+ @Override
+ public boolean isWireFormatInfo() {
+ return false;
+ }
+
+ @Override
+ public boolean isBrokerInfo() {
+ return false;
+ }
+
+ @Override
+ public boolean isResponse() {
+ return false;
+ }
+
+ @Override
+ public boolean isMessageDispatch() {
+ return false;
+ }
+
+ @Override
+ public boolean isMessage() {
+ return false;
+ }
+
+ @Override
+ public boolean isMarshallAware() {
+ return false;
+ }
+
+ @Override
+ public boolean isMessageAck() {
+ return false;
+ }
+
+ @Override
+ public boolean isMessageDispatchNotification() {
+ return false;
+ }
+
+ @Override
+ public boolean isShutdownInfo() {
+ return false;
+ }
+
+ @Override
+ public boolean isConnectionControl() {
+ return false;
+ }
+
+ @Override
+ public boolean isConsumerControl() {
+ return false;
+ }
+
+ /** The endpoint within the transport where this message came from. */
+ @Override
+ public Endpoint getFrom() {
+ return from;
+ }
+
+ @Override
+ public void setFrom(Endpoint from) {
+ this.from = from;
+ }
+
+ /**
+ * The endpoint within the transport where this message is going to - null means all endpoints.
+ */
+ @Override
+ public Endpoint getTo() {
+ return to;
+ }
+
+ @Override
+ public void setTo(Endpoint to) {
+ this.to = to;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/BrokerId.java b/activemq-filters/src/main/java/org/apache/activemq/command/BrokerId.java
new file mode 100644
index 00000000..886e5aa9
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/BrokerId.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+public class BrokerId implements DataStructure {
+
+ public static final byte DATA_STRUCTURE_TYPE = CommandTypes.BROKER_ID;
+ protected String value;
+
+ public BrokerId() {}
+
+ public BrokerId(String brokerId) {
+ this.value = brokerId;
+ }
+
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || o.getClass() != BrokerId.class) {
+ return false;
+ }
+ BrokerId id = (BrokerId) o;
+ return value.equals(id.value);
+ }
+
+ public byte getDataStructureType() {
+ return DATA_STRUCTURE_TYPE;
+ }
+
+ public String toString() {
+ return value;
+ }
+
+ /** @openwire:property version=1 */
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String brokerId) {
+ this.value = brokerId;
+ }
+
+ public boolean isMarshallAware() {
+ return false;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/BrokerInfo.java b/activemq-filters/src/main/java/org/apache/activemq/command/BrokerInfo.java
new file mode 100644
index 00000000..e23cef13
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/BrokerInfo.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+import java.io.IOException;
+import java.util.Properties;
+import org.apache.activemq.util.MarshallingSupport;
+
+/**
+ * When a client connects to a broker, the broker send the client a BrokerInfo so that the client
+ * knows which broker node he's talking to and also any peers that the node has in his cluster. This
+ * is the broker helping the client out in discovering other nodes in the cluster.
+ */
+public class BrokerInfo extends BaseCommand {
+ private static final String PASSIVE_SLAVE_KEY = "passiveSlave";
+ public static final byte DATA_STRUCTURE_TYPE = CommandTypes.BROKER_INFO;
+ BrokerId brokerId;
+ String brokerURL;
+ boolean slaveBroker;
+ boolean masterBroker;
+ boolean faultTolerantConfiguration;
+ boolean networkConnection;
+ boolean duplexConnection;
+ BrokerInfo peerBrokerInfos[];
+ String brokerName;
+ long connectionId;
+ String brokerUploadUrl;
+ String networkProperties;
+ transient int refCount = 0;
+
+ public BrokerInfo copy() {
+ BrokerInfo copy = new BrokerInfo();
+ copy(copy);
+ return copy;
+ }
+
+ private void copy(BrokerInfo copy) {
+ super.copy(copy);
+ copy.brokerId = this.brokerId;
+ copy.brokerURL = this.brokerURL;
+ copy.slaveBroker = this.slaveBroker;
+ copy.masterBroker = this.masterBroker;
+ copy.faultTolerantConfiguration = this.faultTolerantConfiguration;
+ copy.networkConnection = this.networkConnection;
+ copy.duplexConnection = this.duplexConnection;
+ copy.peerBrokerInfos = this.peerBrokerInfos;
+ copy.brokerName = this.brokerName;
+ copy.connectionId = this.connectionId;
+ copy.brokerUploadUrl = this.brokerUploadUrl;
+ copy.networkProperties = this.networkProperties;
+ }
+
+ @Override
+ public boolean isBrokerInfo() {
+ return true;
+ }
+
+ @Override
+ public byte getDataStructureType() {
+ return DATA_STRUCTURE_TYPE;
+ }
+
+ /** @openwire:property version=1 cache=true */
+ public BrokerId getBrokerId() {
+ return brokerId;
+ }
+
+ public void setBrokerId(BrokerId brokerId) {
+ this.brokerId = brokerId;
+ }
+
+ /** @openwire:property version=1 */
+ public String getBrokerURL() {
+ return brokerURL;
+ }
+
+ public void setBrokerURL(String brokerURL) {
+ this.brokerURL = brokerURL;
+ }
+
+ /** @openwire:property version=1 testSize=0 */
+ public BrokerInfo[] getPeerBrokerInfos() {
+ return peerBrokerInfos;
+ }
+
+ public void setPeerBrokerInfos(BrokerInfo[] peerBrokerInfos) {
+ this.peerBrokerInfos = peerBrokerInfos;
+ }
+
+ /** @openwire:property version=1 */
+ public String getBrokerName() {
+ return brokerName;
+ }
+
+ public void setBrokerName(String brokerName) {
+ this.brokerName = brokerName;
+ }
+
+ /** @openwire:property version=1 */
+ public boolean isSlaveBroker() {
+ return slaveBroker;
+ }
+
+ public void setSlaveBroker(boolean slaveBroker) {
+ this.slaveBroker = slaveBroker;
+ }
+
+ /** @openwire:property version=1 */
+ public boolean isMasterBroker() {
+ return masterBroker;
+ }
+
+ /** @param masterBroker The masterBroker to set. */
+ public void setMasterBroker(boolean masterBroker) {
+ this.masterBroker = masterBroker;
+ }
+
+ /**
+ * @openwire:property version=1
+ * @return Returns the faultTolerantConfiguration.
+ */
+ public boolean isFaultTolerantConfiguration() {
+ return faultTolerantConfiguration;
+ }
+
+ /** @param faultTolerantConfiguration The faultTolerantConfiguration to set. */
+ public void setFaultTolerantConfiguration(boolean faultTolerantConfiguration) {
+ this.faultTolerantConfiguration = faultTolerantConfiguration;
+ }
+
+ /**
+ * @openwire:property version=2
+ * @return the duplexConnection
+ */
+ public boolean isDuplexConnection() {
+ return this.duplexConnection;
+ }
+
+ /** @param duplexConnection the duplexConnection to set */
+ public void setDuplexConnection(boolean duplexConnection) {
+ this.duplexConnection = duplexConnection;
+ }
+
+ /**
+ * @openwire:property version=2
+ * @return the networkConnection
+ */
+ public boolean isNetworkConnection() {
+ return this.networkConnection;
+ }
+
+ /** @param networkConnection the networkConnection to set */
+ public void setNetworkConnection(boolean networkConnection) {
+ this.networkConnection = networkConnection;
+ }
+
+ /**
+ * The broker assigns a each connection it accepts a connection id.
+ *
+ * @openwire:property version=2
+ */
+ public long getConnectionId() {
+ return connectionId;
+ }
+
+ public void setConnectionId(long connectionId) {
+ this.connectionId = connectionId;
+ }
+
+ /**
+ * The URL to use when uploading BLOBs to the broker or some other external file/http server
+ *
+ * @openwire:property version=3
+ */
+ public String getBrokerUploadUrl() {
+ return brokerUploadUrl;
+ }
+
+ public void setBrokerUploadUrl(String brokerUploadUrl) {
+ this.brokerUploadUrl = brokerUploadUrl;
+ }
+
+ /**
+ * @openwire:property version=3 cache=false
+ * @return the networkProperties
+ */
+ public String getNetworkProperties() {
+ return this.networkProperties;
+ }
+
+ /** @param networkProperties the networkProperties to set */
+ public void setNetworkProperties(String networkProperties) {
+ this.networkProperties = networkProperties;
+ }
+
+ public boolean isPassiveSlave() {
+ boolean result = false;
+ Properties props = getProperties();
+ if (props != null) {
+ result = Boolean.parseBoolean(props.getProperty(PASSIVE_SLAVE_KEY, "false"));
+ }
+ return result;
+ }
+
+ public void setPassiveSlave(boolean value) {
+ Properties props = new Properties();
+ props.put(PASSIVE_SLAVE_KEY, Boolean.toString(value));
+ try {
+ this.networkProperties = MarshallingSupport.propertiesToString(props);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public Properties getProperties() {
+ Properties result = null;
+ try {
+ result = MarshallingSupport.stringToProperties(getNetworkProperties());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return result;
+ }
+
+ public int getRefCount() {
+ return refCount;
+ }
+
+ public void incrementRefCount() {
+ refCount++;
+ }
+
+ public int decrementRefCount() {
+ return --refCount;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/Command.java b/activemq-filters/src/main/java/org/apache/activemq/command/Command.java
new file mode 100644
index 00000000..fbace03e
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/Command.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+/** The Command Pattern so that we can send and receive commands on the different transports */
+public interface Command extends DataStructure {
+
+ void setCommandId(int value);
+
+ /** @return the unique ID of this request used to map responses to requests */
+ int getCommandId();
+
+ void setResponseRequired(boolean responseRequired);
+
+ boolean isResponseRequired();
+
+ boolean isResponse();
+
+ boolean isMessageDispatch();
+
+ boolean isBrokerInfo();
+
+ boolean isWireFormatInfo();
+
+ boolean isMessage();
+
+ boolean isMessageAck();
+
+ boolean isMessageDispatchNotification();
+
+ boolean isShutdownInfo();
+
+ boolean isConnectionControl();
+
+ boolean isConsumerControl();
+
+ /**
+ * The endpoint within the transport where this message came from which could be null if the
+ * transport only supports a single endpoint.
+ */
+ Endpoint getFrom();
+
+ void setFrom(Endpoint from);
+
+ /**
+ * The endpoint within the transport where this message is going to - null means all endpoints.
+ */
+ Endpoint getTo();
+
+ void setTo(Endpoint to);
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/CommandTypes.java b/activemq-filters/src/main/java/org/apache/activemq/command/CommandTypes.java
new file mode 100644
index 00000000..275e93bc
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/CommandTypes.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+/** Holds the command id constants used by the command objects. */
+public interface CommandTypes {
+
+ // What is the latest version of the openwire protocol
+ byte PROTOCOL_VERSION = 12;
+
+ // What is the latest version of the openwire protocol used in the stores
+ byte PROTOCOL_STORE_VERSION = 11;
+
+ // What is the legacy version that old KahaDB store's most commonly used
+ byte PROTOCOL_LEGACY_STORE_VERSION = 6;
+
+ // What is the first version that BROKER_SUBSCRIPTION_INFO is supported
+ byte PROTOCOL_VERSION_DURABLE_SYNC = 12;
+
+ // A marshaling layer can use this type to specify a null object.
+ byte NULL = 0;
+
+ // /////////////////////////////////////////////////
+ //
+ // Info objects sent back and forth client/server when
+ // setting up a client connection.
+ //
+ // /////////////////////////////////////////////////
+ byte WIREFORMAT_INFO = 1;
+ byte BROKER_INFO = 2;
+ byte CONNECTION_INFO = 3;
+ byte SESSION_INFO = 4;
+ byte CONSUMER_INFO = 5;
+ byte PRODUCER_INFO = 6;
+ byte TRANSACTION_INFO = 7;
+ byte DESTINATION_INFO = 8;
+ byte REMOVE_SUBSCRIPTION_INFO = 9;
+ byte KEEP_ALIVE_INFO = 10;
+ byte SHUTDOWN_INFO = 11;
+ byte REMOVE_INFO = 12;
+ byte CONTROL_COMMAND = 14;
+ byte FLUSH_COMMAND = 15;
+ byte CONNECTION_ERROR = 16;
+ byte CONSUMER_CONTROL = 17;
+ byte CONNECTION_CONTROL = 18;
+
+ // /////////////////////////////////////////////////
+ //
+ // Messages that go back and forth between the client
+ // and the server.
+ //
+ // /////////////////////////////////////////////////
+ byte PRODUCER_ACK = 19;
+ byte MESSAGE_PULL = 20;
+ byte MESSAGE_DISPATCH = 21;
+ byte MESSAGE_ACK = 22;
+
+ byte ACTIVEMQ_MESSAGE = 23;
+ byte ACTIVEMQ_BYTES_MESSAGE = 24;
+ byte ACTIVEMQ_MAP_MESSAGE = 25;
+ byte ACTIVEMQ_OBJECT_MESSAGE = 26;
+ byte ACTIVEMQ_STREAM_MESSAGE = 27;
+ byte ACTIVEMQ_TEXT_MESSAGE = 28;
+ byte ACTIVEMQ_BLOB_MESSAGE = 29;
+
+ // /////////////////////////////////////////////////
+ //
+ // Command Response messages
+ //
+ // /////////////////////////////////////////////////
+ byte RESPONSE = 30;
+ byte EXCEPTION_RESPONSE = 31;
+ byte DATA_RESPONSE = 32;
+ byte DATA_ARRAY_RESPONSE = 33;
+ byte INTEGER_RESPONSE = 34;
+
+ // /////////////////////////////////////////////////
+ //
+ // Used by discovery
+ //
+ // /////////////////////////////////////////////////BROKER_SUBSCRIPTION_INFO
+ byte DISCOVERY_EVENT = 40;
+
+ // /////////////////////////////////////////////////
+ //
+ // Command object used by the Journal
+ //
+ // /////////////////////////////////////////////////
+ byte JOURNAL_ACK = 50;
+ byte JOURNAL_REMOVE = 52;
+ byte JOURNAL_TRACE = 53;
+ byte JOURNAL_TRANSACTION = 54;
+ byte DURABLE_SUBSCRIPTION_INFO = 55;
+
+ // /////////////////////////////////////////////////
+ //
+ // Reliability and fragmentation
+ //
+ // /////////////////////////////////////////////////
+ byte PARTIAL_COMMAND = 60;
+ byte PARTIAL_LAST_COMMAND = 61;
+
+ byte REPLAY = 65;
+
+ // /////////////////////////////////////////////////
+ //
+ // Types used represent basic Java types.
+ //
+ // /////////////////////////////////////////////////
+ byte BYTE_TYPE = 70;
+ byte CHAR_TYPE = 71;
+ byte SHORT_TYPE = 72;
+ byte INTEGER_TYPE = 73;
+ byte LONG_TYPE = 74;
+ byte DOUBLE_TYPE = 75;
+ byte FLOAT_TYPE = 76;
+ byte STRING_TYPE = 77;
+ byte BOOLEAN_TYPE = 78;
+ byte BYTE_ARRAY_TYPE = 79;
+
+ // /////////////////////////////////////////////////
+ //
+ // Broker to Broker command objects
+ //
+ // /////////////////////////////////////////////////
+
+ byte MESSAGE_DISPATCH_NOTIFICATION = 90;
+ byte NETWORK_BRIDGE_FILTER = 91;
+ byte BROKER_SUBSCRIPTION_INFO = 92;
+
+ // /////////////////////////////////////////////////
+ //
+ // Data structures contained in the command objects.
+ //
+ // /////////////////////////////////////////////////
+ byte ACTIVEMQ_QUEUE = 100;
+ byte ACTIVEMQ_TOPIC = 101;
+ byte ACTIVEMQ_TEMP_QUEUE = 102;
+ byte ACTIVEMQ_TEMP_TOPIC = 103;
+
+ byte MESSAGE_ID = 110;
+ byte ACTIVEMQ_LOCAL_TRANSACTION_ID = 111;
+ byte ACTIVEMQ_XA_TRANSACTION_ID = 112;
+
+ byte CONNECTION_ID = 120;
+ byte SESSION_ID = 121;
+ byte CONSUMER_ID = 122;
+ byte PRODUCER_ID = 123;
+ byte BROKER_ID = 124;
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/ConnectionId.java b/activemq-filters/src/main/java/org/apache/activemq/command/ConnectionId.java
new file mode 100644
index 00000000..2639c913
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/ConnectionId.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+public class ConnectionId implements DataStructure, Comparable {
+
+ public static final byte DATA_STRUCTURE_TYPE = CommandTypes.CONNECTION_ID;
+
+ protected String value;
+
+ public ConnectionId() {}
+
+ public ConnectionId(String connectionId) {
+ this.value = connectionId;
+ }
+
+ public ConnectionId(ConnectionId id) {
+ this.value = id.getValue();
+ }
+
+ public ConnectionId(SessionId id) {
+ this.value = id.getConnectionId();
+ }
+
+ public ConnectionId(ProducerId id) {
+ this.value = id.getConnectionId();
+ }
+
+ public ConnectionId(ConsumerId id) {
+ this.value = id.getConnectionId();
+ }
+
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || o.getClass() != ConnectionId.class) {
+ return false;
+ }
+ ConnectionId id = (ConnectionId) o;
+ return value.equals(id.value);
+ }
+
+ public byte getDataStructureType() {
+ return DATA_STRUCTURE_TYPE;
+ }
+
+ public String toString() {
+ return value;
+ }
+
+ /** @openwire:property version=1 */
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String connectionId) {
+ this.value = connectionId;
+ }
+
+ public boolean isMarshallAware() {
+ return false;
+ }
+
+ public int compareTo(ConnectionId o) {
+ return value.compareTo(o.value);
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/ConsumerId.java b/activemq-filters/src/main/java/org/apache/activemq/command/ConsumerId.java
new file mode 100644
index 00000000..15d84ec0
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/ConsumerId.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+public class ConsumerId implements DataStructure {
+
+ public static final byte DATA_STRUCTURE_TYPE = CommandTypes.CONSUMER_ID;
+
+ protected String connectionId;
+ protected long sessionId;
+ protected long value;
+
+ protected transient int hashCode;
+ protected transient String key;
+ protected transient SessionId parentId;
+
+ public ConsumerId() {}
+
+ public ConsumerId(String str) {
+ if (str != null) {
+ String[] splits = str.split(":");
+ if (splits != null && splits.length >= 3) {
+ this.connectionId = splits[0];
+ this.sessionId = Long.parseLong(splits[1]);
+ this.value = Long.parseLong(splits[2]);
+ }
+ }
+ }
+
+ public ConsumerId(SessionId sessionId, long consumerId) {
+ this.connectionId = sessionId.getConnectionId();
+ this.sessionId = sessionId.getValue();
+ this.value = consumerId;
+ }
+
+ public ConsumerId(ConsumerId id) {
+ this.connectionId = id.getConnectionId();
+ this.sessionId = id.getSessionId();
+ this.value = id.getValue();
+ }
+
+ public SessionId getParentId() {
+ if (parentId == null) {
+ parentId = new SessionId(this);
+ }
+ return parentId;
+ }
+
+ public int hashCode() {
+ if (hashCode == 0) {
+ hashCode = connectionId.hashCode() ^ (int) sessionId ^ (int) value;
+ }
+ return hashCode;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || o.getClass() != ConsumerId.class) {
+ return false;
+ }
+ ConsumerId id = (ConsumerId) o;
+ return sessionId == id.sessionId && value == id.value && connectionId.equals(id.connectionId);
+ }
+
+ public byte getDataStructureType() {
+ return DATA_STRUCTURE_TYPE;
+ }
+
+ public String toString() {
+ if (key == null) {
+ key = connectionId + ":" + sessionId + ":" + value;
+ }
+ return key;
+ }
+
+ /** @openwire:property version=1 */
+ public String getConnectionId() {
+ return connectionId;
+ }
+
+ public void setConnectionId(String connectionId) {
+ this.connectionId = connectionId;
+ }
+
+ /** @openwire:property version=1 */
+ public long getSessionId() {
+ return sessionId;
+ }
+
+ public void setSessionId(long sessionId) {
+ this.sessionId = sessionId;
+ }
+
+ /** @openwire:property version=1 */
+ public long getValue() {
+ return value;
+ }
+
+ public void setValue(long consumerId) {
+ this.value = consumerId;
+ }
+
+ public boolean isMarshallAware() {
+ return false;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/DataStructure.java b/activemq-filters/src/main/java/org/apache/activemq/command/DataStructure.java
new file mode 100644
index 00000000..0777a52c
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/DataStructure.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+/** */
+public interface DataStructure {
+
+ /** @return The type of the data structure */
+ byte getDataStructureType();
+
+ boolean isMarshallAware();
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/DefaultUnresolvedDestinationTransformer.java b/activemq-filters/src/main/java/org/apache/activemq/command/DefaultUnresolvedDestinationTransformer.java
new file mode 100644
index 00000000..0b431dc1
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/DefaultUnresolvedDestinationTransformer.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+import jakarta.jms.Destination;
+import jakarta.jms.JMSException;
+import jakarta.jms.Queue;
+import jakarta.jms.Topic;
+import java.lang.reflect.Method;
+
+public class DefaultUnresolvedDestinationTransformer implements UnresolvedDestinationTransformer {
+
+ @Override
+ public ActiveMQDestination transform(Destination dest) throws JMSException {
+ String queueName = ((Queue) dest).getQueueName();
+ String topicName = ((Topic) dest).getTopicName();
+
+ if (queueName == null && topicName == null) {
+ throw new JMSException(
+ "Unresolvable destination: Both queue and topic names are null: " + dest);
+ }
+ try {
+ Method isQueueMethod = dest.getClass().getMethod("isQueue");
+ Method isTopicMethod = dest.getClass().getMethod("isTopic");
+ Boolean isQueue = (Boolean) isQueueMethod.invoke(dest);
+ Boolean isTopic = (Boolean) isTopicMethod.invoke(dest);
+ if (isQueue) {
+ return new ActiveMQQueue(queueName);
+ } else if (isTopic) {
+ return new ActiveMQTopic(topicName);
+ } else {
+ throw new JMSException("Unresolvable destination: Neither Queue nor Topic: " + dest);
+ }
+ } catch (Exception e) {
+ throw new JMSException("Unresolvable destination: " + e.getMessage() + ": " + dest);
+ }
+ }
+
+ @Override
+ public ActiveMQDestination transform(String dest) throws JMSException {
+ return new ActiveMQQueue(dest);
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/Endpoint.java b/activemq-filters/src/main/java/org/apache/activemq/command/Endpoint.java
new file mode 100644
index 00000000..1193b8bb
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/Endpoint.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+/**
+ * Represents the logical endpoint where commands come from or are sent to.
+ *
+ * For connection based transports like TCP / VM then there is a single endpoint for all
+ * commands. For transports like multicast there could be different endpoints being used on the same
+ * transport.
+ */
+public interface Endpoint {
+
+ /** Returns the name of the endpoint. */
+ String getName();
+
+ /** Returns the broker ID for this endpoint, if the endpoint is a broker or null */
+ BrokerId getBrokerId();
+
+ /** Returns the broker information for this endpoint, if the endpoint is a broker or null */
+ BrokerInfo getBrokerInfo();
+
+ void setBrokerInfo(BrokerInfo brokerInfo);
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/MarshallAware.java b/activemq-filters/src/main/java/org/apache/activemq/command/MarshallAware.java
new file mode 100644
index 00000000..0e6d00aa
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/MarshallAware.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+import java.io.IOException;
+import org.apache.activemq.wireformat.WireFormat;
+
+public interface MarshallAware {
+
+ void beforeMarshall(WireFormat wireFormat) throws IOException;
+
+ void afterMarshall(WireFormat wireFormat) throws IOException;
+
+ void beforeUnmarshall(WireFormat wireFormat) throws IOException;
+
+ void afterUnmarshall(WireFormat wireFormat) throws IOException;
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/Message.java b/activemq-filters/src/main/java/org/apache/activemq/command/Message.java
new file mode 100644
index 00000000..ce4fcccf
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/Message.java
@@ -0,0 +1,807 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+import jakarta.jms.JMSException;
+import java.beans.Transient;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.ObjectStreamException;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.zip.DeflaterOutputStream;
+import org.apache.activemq.broker.region.MessageReference;
+import org.apache.activemq.usage.MemoryUsage;
+import org.apache.activemq.util.ByteArrayInputStream;
+import org.apache.activemq.util.ByteArrayOutputStream;
+import org.apache.activemq.util.ByteSequence;
+import org.apache.activemq.util.MarshallingSupport;
+import org.apache.activemq.wireformat.WireFormat;
+import org.fusesource.hawtbuf.UTF8Buffer;
+
+/** Represents an ActiveMQ message */
+public abstract class Message extends BaseCommand implements MarshallAware, MessageReference {
+ public static final String ORIGINAL_EXPIRATION = "originalExpiration";
+ public static final String ADIVSORY_MESSAGE_TYPE = "Advisory";
+
+ /** The default minimum amount of memory a message is assumed to use */
+ public static final int DEFAULT_MINIMUM_MESSAGE_SIZE = 1024;
+
+ protected MessageId messageId;
+ protected ActiveMQDestination originalDestination;
+ protected TransactionId originalTransactionId;
+
+ protected ProducerId producerId;
+ protected ActiveMQDestination destination;
+ protected TransactionId transactionId;
+
+ protected long deliveryTime;
+ protected long expiration;
+ protected long timestamp;
+ protected long arrival;
+ protected long brokerInTime;
+ protected long brokerOutTime;
+ protected String correlationId;
+ protected ActiveMQDestination replyTo;
+ protected boolean persistent;
+ protected String type;
+ protected byte priority;
+ protected String groupID;
+ protected int groupSequence;
+ protected ConsumerId targetConsumerId;
+ protected boolean compressed;
+ protected String userID;
+
+ protected ByteSequence content;
+ protected volatile ByteSequence marshalledProperties;
+ protected DataStructure dataStructure;
+ protected int redeliveryCounter;
+
+ protected int size;
+ protected Map properties;
+ protected boolean readOnlyProperties;
+ protected boolean readOnlyBody;
+ protected transient boolean recievedByDFBridge;
+ protected boolean droppable;
+ protected boolean jmsXGroupFirstForConsumer;
+
+ private transient short referenceCount;
+ transient MessageDestination regionDestination;
+ transient MemoryUsage memoryUsage;
+ transient AtomicBoolean processAsExpired = new AtomicBoolean(false);
+
+ private BrokerId[] brokerPath;
+ private BrokerId[] cluster;
+
+ public static interface MessageDestination {
+ int getMinimumMessageSize();
+
+ MemoryUsage getMemoryUsage();
+ }
+
+ public abstract Message copy();
+
+ public abstract void clearBody() throws JMSException;
+
+ public abstract void storeContent();
+
+ public abstract void storeContentAndClear();
+
+ // useful to reduce the memory footprint of a persisted message
+ public void clearUnMarshalledState() throws JMSException {
+ properties = null;
+ }
+
+ public boolean isMarshalled() {
+ return isContentMarshalled() && isPropertiesMarshalled();
+ }
+
+ protected boolean isPropertiesMarshalled() {
+ return marshalledProperties != null || properties == null;
+ }
+
+ protected boolean isContentMarshalled() {
+ return content != null;
+ }
+
+ protected void copy(Message copy) {
+ super.copy(copy);
+ copy.producerId = producerId;
+ copy.transactionId = transactionId;
+ copy.destination = destination;
+ copy.messageId = messageId != null ? messageId.copy() : null;
+ copy.originalDestination = originalDestination;
+ copy.originalTransactionId = originalTransactionId;
+ copy.deliveryTime = deliveryTime;
+ copy.expiration = expiration;
+ copy.timestamp = timestamp;
+ copy.correlationId = correlationId;
+ copy.replyTo = replyTo;
+ copy.persistent = persistent;
+ copy.redeliveryCounter = redeliveryCounter;
+ copy.type = type;
+ copy.priority = priority;
+ copy.size = size;
+ copy.groupID = groupID;
+ copy.userID = userID;
+ copy.groupSequence = groupSequence;
+
+ if (properties != null) {
+ copy.properties = new HashMap(properties);
+
+ // The new message hasn't expired, so remove this feild.
+ copy.properties.remove(ORIGINAL_EXPIRATION);
+ } else {
+ copy.properties = properties;
+ }
+
+ copy.content = copyByteSequence(content);
+ copy.marshalledProperties = copyByteSequence(marshalledProperties);
+ copy.dataStructure = dataStructure;
+ copy.readOnlyProperties = readOnlyProperties;
+ copy.readOnlyBody = readOnlyBody;
+ copy.compressed = compressed;
+ copy.recievedByDFBridge = recievedByDFBridge;
+
+ copy.arrival = arrival;
+ copy.regionDestination = regionDestination;
+ copy.brokerInTime = brokerInTime;
+ copy.brokerOutTime = brokerOutTime;
+ copy.memoryUsage = this.memoryUsage;
+ copy.brokerPath = brokerPath;
+ copy.jmsXGroupFirstForConsumer = jmsXGroupFirstForConsumer;
+
+ // lets not copy the following fields
+ // copy.targetConsumerId = targetConsumerId;
+ // copy.referenceCount = referenceCount;
+ }
+
+ private ByteSequence copyByteSequence(ByteSequence content) {
+ if (content != null) {
+ return new ByteSequence(content.getData(), content.getOffset(), content.getLength());
+ }
+ return null;
+ }
+
+ public Object getProperty(String name) throws IOException {
+ if (properties == null) {
+ if (marshalledProperties == null) {
+ return null;
+ }
+ properties = unmarsallProperties(marshalledProperties);
+ }
+ Object result = properties.get(name);
+ if (result instanceof UTF8Buffer) {
+ result = result.toString();
+ }
+
+ return result;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map getProperties() throws IOException {
+ if (properties == null) {
+ if (marshalledProperties == null) {
+ return Collections.EMPTY_MAP;
+ }
+ properties = unmarsallProperties(marshalledProperties);
+ }
+ return Collections.unmodifiableMap(properties);
+ }
+
+ public void clearProperties() {
+ marshalledProperties = null;
+ properties = null;
+ }
+
+ public void setProperty(String name, Object value) throws IOException {
+ lazyCreateProperties();
+ properties.put(name, value);
+ }
+
+ public void removeProperty(String name) throws IOException {
+ lazyCreateProperties();
+ properties.remove(name);
+ }
+
+ protected void lazyCreateProperties() throws IOException {
+ if (properties == null) {
+ if (marshalledProperties == null) {
+ properties = new HashMap();
+ } else {
+ properties = unmarsallProperties(marshalledProperties);
+ marshalledProperties = null;
+ }
+ } else {
+ marshalledProperties = null;
+ }
+ }
+
+ private Map unmarsallProperties(ByteSequence marshalledProperties)
+ throws IOException {
+ return MarshallingSupport.unmarshalPrimitiveMap(
+ new DataInputStream(new ByteArrayInputStream(marshalledProperties)));
+ }
+
+ @Override
+ public void beforeMarshall(WireFormat wireFormat) throws IOException {
+ // Need to marshal the properties.
+ if (marshalledProperties == null && properties != null) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream os = new DataOutputStream(baos);
+ MarshallingSupport.marshalPrimitiveMap(properties, os);
+ os.close();
+ marshalledProperties = baos.toByteSequence();
+ }
+ }
+
+ @Override
+ public void afterMarshall(WireFormat wireFormat) throws IOException {}
+
+ @Override
+ public void beforeUnmarshall(WireFormat wireFormat) throws IOException {}
+
+ @Override
+ public void afterUnmarshall(WireFormat wireFormat) throws IOException {}
+
+ // /////////////////////////////////////////////////////////////////
+ //
+ // Simple Field accessors
+ //
+ // /////////////////////////////////////////////////////////////////
+
+ /** @openwire:property version=1 cache=true */
+ public ProducerId getProducerId() {
+ return producerId;
+ }
+
+ public void setProducerId(ProducerId producerId) {
+ this.producerId = producerId;
+ }
+
+ /** @openwire:property version=1 cache=true */
+ public ActiveMQDestination getDestination() {
+ return destination;
+ }
+
+ public void setDestination(ActiveMQDestination destination) {
+ this.destination = destination;
+ }
+
+ /** @openwire:property version=1 cache=true */
+ public TransactionId getTransactionId() {
+ return transactionId;
+ }
+
+ public void setTransactionId(TransactionId transactionId) {
+ this.transactionId = transactionId;
+ }
+
+ public boolean isInTransaction() {
+ return transactionId != null;
+ }
+
+ /** @openwire:property version=1 cache=true */
+ public ActiveMQDestination getOriginalDestination() {
+ return originalDestination;
+ }
+
+ public void setOriginalDestination(ActiveMQDestination destination) {
+ this.originalDestination = destination;
+ }
+
+ /** @openwire:property version=1 */
+ @Override
+ public MessageId getMessageId() {
+ return messageId;
+ }
+
+ public void setMessageId(MessageId messageId) {
+ this.messageId = messageId;
+ }
+
+ /** @openwire:property version=1 cache=true */
+ public TransactionId getOriginalTransactionId() {
+ return originalTransactionId;
+ }
+
+ public void setOriginalTransactionId(TransactionId transactionId) {
+ this.originalTransactionId = transactionId;
+ }
+
+ /** @openwire:property version=1 */
+ @Override
+ public String getGroupID() {
+ return groupID;
+ }
+
+ public void setGroupID(String groupID) {
+ this.groupID = groupID;
+ }
+
+ /** @openwire:property version=1 */
+ @Override
+ public int getGroupSequence() {
+ return groupSequence;
+ }
+
+ public void setGroupSequence(int groupSequence) {
+ this.groupSequence = groupSequence;
+ }
+
+ /** @openwire:property version=1 */
+ public String getCorrelationId() {
+ return correlationId;
+ }
+
+ public void setCorrelationId(String correlationId) {
+ this.correlationId = correlationId;
+ }
+
+ /** @openwire:property version=1 */
+ @Override
+ public boolean isPersistent() {
+ return persistent;
+ }
+
+ public void setPersistent(boolean deliveryMode) {
+ this.persistent = deliveryMode;
+ }
+
+ /** @openwire:property version=1 */
+ @Override
+ public long getExpiration() {
+ return expiration;
+ }
+
+ public void setExpiration(long expiration) {
+ this.expiration = expiration;
+ }
+
+ /** @openwire:property version=1 */
+ public byte getPriority() {
+ return priority;
+ }
+
+ public void setPriority(byte priority) {
+ if (priority < 0) {
+ this.priority = 0;
+ } else if (priority > 9) {
+ this.priority = 9;
+ } else {
+ this.priority = priority;
+ }
+ }
+
+ /** @openwire:property version=1 */
+ public ActiveMQDestination getReplyTo() {
+ return replyTo;
+ }
+
+ public void setReplyTo(ActiveMQDestination replyTo) {
+ this.replyTo = replyTo;
+ }
+
+ /** @openwire:property version=1 */
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(long timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ /** @openwire:property version=1 */
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ /** @openwire:property version=1 */
+ public ByteSequence getContent() {
+ return content;
+ }
+
+ public void setContent(ByteSequence content) {
+ this.content = content;
+ }
+
+ /** @openwire:property version=1 */
+ public ByteSequence getMarshalledProperties() {
+ return marshalledProperties;
+ }
+
+ public void setMarshalledProperties(ByteSequence marshalledProperties) {
+ this.marshalledProperties = marshalledProperties;
+ }
+
+ /** @openwire:property version=1 */
+ public DataStructure getDataStructure() {
+ return dataStructure;
+ }
+
+ public void setDataStructure(DataStructure data) {
+ this.dataStructure = data;
+ }
+
+ /**
+ * Can be used to route the message to a specific consumer. Should be null to allow the broker use
+ * normal JMS routing semantics. If the target consumer id is an active consumer on the broker,
+ * the message is dropped. Used by the AdvisoryBroker to replay advisory messages to a specific
+ * consumer.
+ *
+ * @openwire:property version=1 cache=true
+ */
+ @Override
+ public ConsumerId getTargetConsumerId() {
+ return targetConsumerId;
+ }
+
+ public void setTargetConsumerId(ConsumerId targetConsumerId) {
+ this.targetConsumerId = targetConsumerId;
+ }
+
+ @Override
+ public boolean isExpired() {
+ long expireTime = getExpiration();
+ return expireTime > 0 && System.currentTimeMillis() > expireTime;
+ }
+
+ @Override
+ public boolean isAdvisory() {
+ return type != null && type.equals(ADIVSORY_MESSAGE_TYPE);
+ }
+
+ /** @openwire:property version=1 */
+ public boolean isCompressed() {
+ return compressed;
+ }
+
+ public void setCompressed(boolean compressed) {
+ this.compressed = compressed;
+ }
+
+ public boolean isRedelivered() {
+ return redeliveryCounter > 0;
+ }
+
+ public void setRedelivered(boolean redelivered) {
+ if (redelivered) {
+ if (!isRedelivered()) {
+ setRedeliveryCounter(1);
+ }
+ } else {
+ if (isRedelivered()) {
+ setRedeliveryCounter(0);
+ }
+ }
+ }
+
+ @Override
+ public void incrementRedeliveryCounter() {
+ redeliveryCounter++;
+ }
+
+ /** @openwire:property version=1 */
+ @Override
+ public int getRedeliveryCounter() {
+ return redeliveryCounter;
+ }
+
+ public void setRedeliveryCounter(int deliveryCounter) {
+ this.redeliveryCounter = deliveryCounter;
+ }
+
+ /**
+ * The route of brokers the command has moved through.
+ *
+ * @openwire:property version=1 cache=true
+ */
+ public BrokerId[] getBrokerPath() {
+ return brokerPath;
+ }
+
+ public void setBrokerPath(BrokerId[] brokerPath) {
+ this.brokerPath = brokerPath;
+ }
+
+ public boolean isReadOnlyProperties() {
+ return readOnlyProperties;
+ }
+
+ public void setReadOnlyProperties(boolean readOnlyProperties) {
+ this.readOnlyProperties = readOnlyProperties;
+ }
+
+ public boolean isReadOnlyBody() {
+ return readOnlyBody;
+ }
+
+ public void setReadOnlyBody(boolean readOnlyBody) {
+ this.readOnlyBody = readOnlyBody;
+ }
+
+ /**
+ * Used to schedule the arrival time of a message to a broker. The broker will not dispatch a
+ * message to a consumer until it's arrival time has elapsed.
+ *
+ * @openwire:property version=1
+ */
+ public long getArrival() {
+ return arrival;
+ }
+
+ public void setArrival(long arrival) {
+ this.arrival = arrival;
+ }
+
+ /**
+ * Only set by the broker and defines the userID of the producer connection who sent this message.
+ * This is an optional field, it needs to be enabled on the broker to have this field populated.
+ *
+ * @openwire:property version=1
+ */
+ public String getUserID() {
+ return userID;
+ }
+
+ public void setUserID(String jmsxUserID) {
+ this.userID = jmsxUserID;
+ }
+
+ @Override
+ public int getReferenceCount() {
+ return referenceCount;
+ }
+
+ @Override
+ public Message getMessageHardRef() {
+ return this;
+ }
+
+ @Override
+ public Message getMessage() {
+ return this;
+ }
+
+ public void setRegionDestination(MessageDestination destination) {
+ this.regionDestination = destination;
+ if (this.memoryUsage == null) {
+ this.memoryUsage = destination.getMemoryUsage();
+ }
+ }
+
+ @Override
+ @Transient
+ public MessageDestination getRegionDestination() {
+ return regionDestination;
+ }
+
+ public MemoryUsage getMemoryUsage() {
+ return this.memoryUsage;
+ }
+
+ public void setMemoryUsage(MemoryUsage usage) {
+ this.memoryUsage = usage;
+ }
+
+ @Override
+ public boolean isMarshallAware() {
+ return true;
+ }
+
+ @Override
+ public int incrementReferenceCount() {
+ int rc;
+ int size;
+ synchronized (this) {
+ rc = ++referenceCount;
+ size = getSize();
+ }
+
+ if (rc == 1 && getMemoryUsage() != null) {
+ getMemoryUsage().increaseUsage(size);
+ // System.err.println("INCREASE USAGE " + System.identityHashCode(getMemoryUsage()) + "
+ // PERCENT = " + getMemoryUsage().getPercentUsage());
+
+ }
+
+ // System.out.println(" + "+getMemoryUsage().getName()+" :::: "+getMessageId()+"rc="+rc);
+ return rc;
+ }
+
+ @Override
+ public int decrementReferenceCount() {
+ int rc;
+ int size;
+ synchronized (this) {
+ rc = --referenceCount;
+ size = getSize();
+ }
+
+ if (rc == 0 && getMemoryUsage() != null) {
+ getMemoryUsage().decreaseUsage(size);
+ // Thread.dumpStack();
+ // System.err.println("DECREADED USAGE " + System.identityHashCode(getMemoryUsage()) + "
+ // PERCENT = " + getMemoryUsage().getPercentUsage());
+ }
+
+ // System.out.println(" - "+getMemoryUsage().getName()+" :::: "+getMessageId()+"rc="+rc);
+
+ return rc;
+ }
+
+ @Override
+ public int getSize() {
+ int minimumMessageSize = getMinimumMessageSize();
+ if (size < minimumMessageSize || size == 0) {
+ size = minimumMessageSize;
+ if (marshalledProperties != null) {
+ size += marshalledProperties.getLength();
+ }
+ if (content != null) {
+ size += content.getLength();
+ }
+ }
+ return size;
+ }
+
+ protected int getMinimumMessageSize() {
+ int result = DEFAULT_MINIMUM_MESSAGE_SIZE;
+ // let destination override
+ MessageDestination dest = regionDestination;
+ if (dest != null) {
+ result = dest.getMinimumMessageSize();
+ }
+ return result;
+ }
+
+ /**
+ * @openwire:property version=1
+ * @return Returns the recievedByDFBridge.
+ */
+ public boolean isRecievedByDFBridge() {
+ return recievedByDFBridge;
+ }
+
+ /** @param recievedByDFBridge The recievedByDFBridge to set. */
+ public void setRecievedByDFBridge(boolean recievedByDFBridge) {
+ this.recievedByDFBridge = recievedByDFBridge;
+ }
+
+ public void onMessageRolledBack() {
+ incrementRedeliveryCounter();
+ }
+
+ /** @openwire:property version=2 cache=true */
+ public boolean isDroppable() {
+ return droppable;
+ }
+
+ public void setDroppable(boolean droppable) {
+ this.droppable = droppable;
+ }
+
+ /**
+ * If a message is stored in multiple nodes on a cluster, all the cluster members will be listed
+ * here. Otherwise, it will be null.
+ *
+ * @openwire:property version=3 cache=true
+ */
+ public BrokerId[] getCluster() {
+ return cluster;
+ }
+
+ public void setCluster(BrokerId[] cluster) {
+ this.cluster = cluster;
+ }
+
+ @Override
+ public boolean isMessage() {
+ return true;
+ }
+
+ /** @openwire:property version=3 */
+ public long getBrokerInTime() {
+ return this.brokerInTime;
+ }
+
+ public void setBrokerInTime(long brokerInTime) {
+ this.brokerInTime = brokerInTime;
+ }
+
+ /** @openwire:property version=3 */
+ public long getBrokerOutTime() {
+ return this.brokerOutTime;
+ }
+
+ public void setBrokerOutTime(long brokerOutTime) {
+ this.brokerOutTime = brokerOutTime;
+ }
+
+ @Override
+ public boolean isDropped() {
+ return false;
+ }
+
+ /** @openwire:property version=10 */
+ public boolean isJMSXGroupFirstForConsumer() {
+ return jmsXGroupFirstForConsumer;
+ }
+
+ public void setJMSXGroupFirstForConsumer(boolean val) {
+ jmsXGroupFirstForConsumer = val;
+ }
+
+ public void compress() throws IOException {
+ if (!isCompressed()) {
+ storeContent();
+ if (!isCompressed() && getContent() != null) {
+ doCompress();
+ }
+ }
+ }
+
+ protected void doCompress() throws IOException {
+ compressed = true;
+ ByteSequence bytes = getContent();
+ ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
+ OutputStream os = new DeflaterOutputStream(bytesOut);
+ os.write(bytes.data, bytes.offset, bytes.length);
+ os.close();
+ setContent(bytesOut.toByteSequence());
+ }
+
+ @Override
+ public String toString() {
+ return toString(null);
+ }
+
+ @Override
+ public String toString(Map overrideFields) {
+ try {
+ getProperties();
+ } catch (IOException e) {
+ }
+ return super.toString(overrideFields);
+ }
+
+ @Override
+ public boolean canProcessAsExpired() {
+ return processAsExpired.compareAndSet(false, true);
+ }
+
+ /**
+ * Initialize the transient fields at deserialization to get a normal state.
+ *
+ * @see Serializable
+ * Javadoc
+ */
+ protected Object readResolve() throws ObjectStreamException {
+ if (this.processAsExpired == null) {
+ this.processAsExpired = new AtomicBoolean();
+ }
+ return this;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/MessageId.java b/activemq-filters/src/main/java/org/apache/activemq/command/MessageId.java
new file mode 100644
index 00000000..723ed70a
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/MessageId.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+public class MessageId implements DataStructure, Comparable {
+
+ public static final byte DATA_STRUCTURE_TYPE = CommandTypes.MESSAGE_ID;
+
+ protected String textView;
+ protected ProducerId producerId;
+ protected long producerSequenceId;
+ protected long brokerSequenceId;
+
+ private transient String key;
+ private transient int hashCode;
+
+ private transient AtomicReference dataLocator = new AtomicReference();
+ private transient Object entryLocator;
+ private transient Object plistLocator;
+ private transient Object futureOrSequenceLong;
+
+ public MessageId() {
+ this.producerId = new ProducerId();
+ }
+
+ public MessageId(ProducerInfo producerInfo, long producerSequenceId) {
+ this.producerId = producerInfo.getProducerId();
+ this.producerSequenceId = producerSequenceId;
+ }
+
+ public MessageId(String messageKey) {
+ setValue(messageKey);
+ }
+
+ public MessageId(String producerId, long producerSequenceId) {
+ this(new ProducerId(producerId), producerSequenceId);
+ }
+
+ public MessageId(ProducerId producerId, long producerSequenceId) {
+ this.producerId = producerId;
+ this.producerSequenceId = producerSequenceId;
+ }
+
+ /** Sets the value as a String */
+ public void setValue(String messageKey) {
+ key = messageKey;
+ // Parse off the sequenceId
+ int p = messageKey.lastIndexOf(":");
+ if (p >= 0) {
+ producerSequenceId = Long.parseLong(messageKey.substring(p + 1));
+ messageKey = messageKey.substring(0, p);
+ } else {
+ throw new NumberFormatException();
+ }
+ producerId = new ProducerId(messageKey);
+ }
+
+ /**
+ * Sets the transient text view of the message which will be ignored if the message is marshaled
+ * on a transport; so is only for in-JVM changes to accommodate foreign JMS message IDs
+ */
+ public void setTextView(String key) {
+ this.textView = key;
+ }
+
+ /**
+ * @openwire:property version=10
+ * @return
+ */
+ public String getTextView() {
+ return textView;
+ }
+
+ @Override
+ public byte getDataStructureType() {
+ return DATA_STRUCTURE_TYPE;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || o.getClass() != getClass()) {
+ return false;
+ }
+
+ MessageId id = (MessageId) o;
+ return producerSequenceId == id.producerSequenceId && producerId.equals(id.producerId);
+ }
+
+ @Override
+ public int hashCode() {
+ if (hashCode == 0) {
+ hashCode = producerId.hashCode() ^ (int) producerSequenceId;
+ }
+ return hashCode;
+ }
+
+ public String toProducerKey() {
+ if (textView == null) {
+ return toString();
+ } else {
+ return producerId.toString() + ":" + producerSequenceId;
+ }
+ }
+
+ @Override
+ public String toString() {
+ if (key == null) {
+ if (textView != null) {
+ if (textView.startsWith("ID:")) {
+ key = textView;
+ } else {
+ key = "ID:" + textView;
+ }
+ } else {
+ key = producerId.toString() + ":" + producerSequenceId;
+ }
+ }
+ return key;
+ }
+
+ /** @openwire:property version=1 cache=true */
+ public ProducerId getProducerId() {
+ return producerId;
+ }
+
+ public void setProducerId(ProducerId producerId) {
+ this.producerId = producerId;
+ }
+
+ /** @openwire:property version=1 */
+ public long getProducerSequenceId() {
+ return producerSequenceId;
+ }
+
+ public void setProducerSequenceId(long producerSequenceId) {
+ this.producerSequenceId = producerSequenceId;
+ }
+
+ /** @openwire:property version=1 */
+ public long getBrokerSequenceId() {
+ return brokerSequenceId;
+ }
+
+ public void setBrokerSequenceId(long brokerSequenceId) {
+ this.brokerSequenceId = brokerSequenceId;
+ }
+
+ @Override
+ public boolean isMarshallAware() {
+ return false;
+ }
+
+ public MessageId copy() {
+ MessageId copy = new MessageId(producerId, producerSequenceId);
+ copy.key = key;
+ copy.brokerSequenceId = brokerSequenceId;
+ copy.dataLocator = dataLocator;
+ copy.entryLocator = entryLocator;
+ copy.futureOrSequenceLong = futureOrSequenceLong;
+ copy.plistLocator = plistLocator;
+ copy.textView = textView;
+ return copy;
+ }
+
+ /**
+ * @param
+ * @return
+ * @see Comparable#compareTo(Object)
+ */
+ @Override
+ public int compareTo(MessageId other) {
+ int result = -1;
+ if (other != null) {
+ result = this.toString().compareTo(other.toString());
+ }
+ return result;
+ }
+
+ /**
+ * @return a locator which aids a message store in loading a message faster. Only used by the
+ * message stores.
+ */
+ public Object getDataLocator() {
+ return dataLocator.get();
+ }
+
+ /**
+ * Sets a locator which aids a message store in loading a message faster. Only used by the message
+ * stores.
+ */
+ public void setDataLocator(Object value) {
+ this.dataLocator.set(value);
+ }
+
+ public Object getFutureOrSequenceLong() {
+ return futureOrSequenceLong;
+ }
+
+ public void setFutureOrSequenceLong(Object futureOrSequenceLong) {
+ this.futureOrSequenceLong = futureOrSequenceLong;
+ }
+
+ public Object getEntryLocator() {
+ return entryLocator;
+ }
+
+ public void setEntryLocator(Object entryLocator) {
+ this.entryLocator = entryLocator;
+ }
+
+ public Object getPlistLocator() {
+ return plistLocator;
+ }
+
+ public void setPlistLocator(Object plistLocator) {
+ this.plistLocator = plistLocator;
+ }
+
+ private Object readResolve() {
+ dataLocator = new AtomicReference();
+ return this;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/ProducerId.java b/activemq-filters/src/main/java/org/apache/activemq/command/ProducerId.java
new file mode 100644
index 00000000..0ee55e9f
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/ProducerId.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+public class ProducerId implements DataStructure {
+
+ public static final byte DATA_STRUCTURE_TYPE = CommandTypes.PRODUCER_ID;
+
+ protected String connectionId;
+ protected long sessionId;
+ protected long value;
+
+ protected transient int hashCode;
+ protected transient String key;
+ protected transient SessionId parentId;
+
+ public ProducerId() {}
+
+ public ProducerId(SessionId sessionId, long producerId) {
+ this.connectionId = sessionId.getConnectionId();
+ this.sessionId = sessionId.getValue();
+ this.value = producerId;
+ }
+
+ public ProducerId(ProducerId id) {
+ this.connectionId = id.getConnectionId();
+ this.sessionId = id.getSessionId();
+ this.value = id.getValue();
+ }
+
+ public ProducerId(String producerKey) {
+ // Parse off the producerId
+ int p = producerKey.lastIndexOf(":");
+ if (p >= 0) {
+ value = Long.parseLong(producerKey.substring(p + 1));
+ producerKey = producerKey.substring(0, p);
+ }
+ setProducerSessionKey(producerKey);
+ }
+
+ public SessionId getParentId() {
+ if (parentId == null) {
+ parentId = new SessionId(this);
+ }
+ return parentId;
+ }
+
+ public int hashCode() {
+ if (hashCode == 0) {
+ hashCode = connectionId.hashCode() ^ (int) sessionId ^ (int) value;
+ }
+ return hashCode;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || o.getClass() != ProducerId.class) {
+ return false;
+ }
+ ProducerId id = (ProducerId) o;
+ return sessionId == id.sessionId && value == id.value && connectionId.equals(id.connectionId);
+ }
+
+ /** @param sessionKey */
+ private void setProducerSessionKey(String sessionKey) {
+ // Parse off the value
+ int p = sessionKey.lastIndexOf(":");
+ if (p >= 0) {
+ sessionId = Long.parseLong(sessionKey.substring(p + 1));
+ sessionKey = sessionKey.substring(0, p);
+ }
+ // The rest is the value
+ connectionId = sessionKey;
+ }
+
+ public String toString() {
+ if (key == null) {
+ key = connectionId + ":" + sessionId + ":" + value;
+ }
+ return key;
+ }
+
+ public byte getDataStructureType() {
+ return DATA_STRUCTURE_TYPE;
+ }
+
+ /** @openwire:property version=1 cache=true */
+ public String getConnectionId() {
+ return connectionId;
+ }
+
+ public void setConnectionId(String connectionId) {
+ this.connectionId = connectionId;
+ }
+
+ /** @openwire:property version=1 */
+ public long getValue() {
+ return value;
+ }
+
+ public void setValue(long producerId) {
+ this.value = producerId;
+ }
+
+ /** @openwire:property version=1 */
+ public long getSessionId() {
+ return sessionId;
+ }
+
+ public void setSessionId(long sessionId) {
+ this.sessionId = sessionId;
+ }
+
+ public boolean isMarshallAware() {
+ return false;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/ProducerInfo.java b/activemq-filters/src/main/java/org/apache/activemq/command/ProducerInfo.java
new file mode 100644
index 00000000..068a650b
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/ProducerInfo.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+public class ProducerInfo extends BaseCommand {
+
+ public static final byte DATA_STRUCTURE_TYPE = CommandTypes.PRODUCER_INFO;
+
+ protected ProducerId producerId;
+ protected ActiveMQDestination destination;
+ protected BrokerId[] brokerPath;
+ protected boolean dispatchAsync;
+ protected int windowSize;
+ protected AtomicLong sentCount = new AtomicLong();
+
+ public ProducerInfo() {}
+
+ public ProducerInfo(ProducerId producerId) {
+ this.producerId = producerId;
+ }
+
+ public ProducerInfo(SessionInfo sessionInfo, long producerId) {
+ this.producerId = new ProducerId(sessionInfo.getSessionId(), producerId);
+ }
+
+ public ProducerInfo copy() {
+ ProducerInfo info = new ProducerInfo();
+ copy(info);
+ return info;
+ }
+
+ public void copy(ProducerInfo info) {
+ super.copy(info);
+ info.producerId = producerId;
+ info.destination = destination;
+ }
+
+ public byte getDataStructureType() {
+ return DATA_STRUCTURE_TYPE;
+ }
+
+ /** @openwire:property version=1 cache=true */
+ public ProducerId getProducerId() {
+ return producerId;
+ }
+
+ public void setProducerId(ProducerId producerId) {
+ this.producerId = producerId;
+ }
+
+ /** @openwire:property version=1 cache=true */
+ public ActiveMQDestination getDestination() {
+ return destination;
+ }
+
+ public void setDestination(ActiveMQDestination destination) {
+ this.destination = destination;
+ }
+
+ public RemoveInfo createRemoveCommand() {
+ RemoveInfo command = new RemoveInfo(getProducerId());
+ command.setResponseRequired(isResponseRequired());
+ return command;
+ }
+
+ /**
+ * The route of brokers the command has moved through.
+ *
+ * @openwire:property version=1 cache=true
+ */
+ public BrokerId[] getBrokerPath() {
+ return brokerPath;
+ }
+
+ public void setBrokerPath(BrokerId[] brokerPath) {
+ this.brokerPath = brokerPath;
+ }
+
+ /**
+ * If the broker should dispatch messages from this producer async. Since sync dispatch could
+ * potentally block the producer thread, this could be an important setting for the producer.
+ *
+ * @openwire:property version=2
+ */
+ public boolean isDispatchAsync() {
+ return dispatchAsync;
+ }
+
+ public void setDispatchAsync(boolean dispatchAsync) {
+ this.dispatchAsync = dispatchAsync;
+ }
+
+ /**
+ * Used to configure the producer window size. A producer will send up to the configured window
+ * size worth of payload data to the broker before waiting for an Ack that allows him to send
+ * more.
+ *
+ * @openwire:property version=3
+ */
+ public int getWindowSize() {
+ return windowSize;
+ }
+
+ public void setWindowSize(int windowSize) {
+ this.windowSize = windowSize;
+ }
+
+ public long getSentCount() {
+ return sentCount.get();
+ }
+
+ public void incrementSentCount() {
+ sentCount.incrementAndGet();
+ }
+
+ public void resetSentCount() {
+ sentCount.set(0);
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/RemoveInfo.java b/activemq-filters/src/main/java/org/apache/activemq/command/RemoveInfo.java
new file mode 100644
index 00000000..684d625f
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/RemoveInfo.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+/** Removes a consumer, producer, session or connection. */
+public class RemoveInfo extends BaseCommand {
+
+ public static final byte DATA_STRUCTURE_TYPE = CommandTypes.REMOVE_INFO;
+ public static final int LAST_DELIVERED_UNSET = -1;
+ public static final int LAST_DELIVERED_UNKNOWN = -2;
+ protected DataStructure objectId;
+ protected long lastDeliveredSequenceId = LAST_DELIVERED_UNKNOWN;
+
+ public RemoveInfo() {}
+
+ public RemoveInfo(DataStructure objectId) {
+ this.objectId = objectId;
+ }
+
+ public byte getDataStructureType() {
+ return DATA_STRUCTURE_TYPE;
+ }
+
+ /** @openwire:property version=1 cache=true */
+ public DataStructure getObjectId() {
+ return objectId;
+ }
+
+ public void setObjectId(DataStructure objectId) {
+ this.objectId = objectId;
+ }
+
+ /** @openwire:property version=5 cache=false */
+ public long getLastDeliveredSequenceId() {
+ return lastDeliveredSequenceId;
+ }
+
+ public void setLastDeliveredSequenceId(long lastDeliveredSequenceId) {
+ this.lastDeliveredSequenceId = lastDeliveredSequenceId;
+ }
+
+ /** Returns true if this event is for a removed connection */
+ public boolean isConnectionRemove() {
+ return objectId.getDataStructureType() == ConnectionId.DATA_STRUCTURE_TYPE;
+ }
+
+ /** Returns true if this event is for a removed session */
+ public boolean isSessionRemove() {
+ return objectId.getDataStructureType() == SessionId.DATA_STRUCTURE_TYPE;
+ }
+
+ /** Returns true if this event is for a removed consumer */
+ public boolean isConsumerRemove() {
+ return objectId.getDataStructureType() == ConsumerId.DATA_STRUCTURE_TYPE;
+ }
+
+ /** Returns true if this event is for a removed producer */
+ public boolean isProducerRemove() {
+ return objectId.getDataStructureType() == ProducerId.DATA_STRUCTURE_TYPE;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/Response.java b/activemq-filters/src/main/java/org/apache/activemq/command/Response.java
new file mode 100644
index 00000000..fbabe170
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/Response.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+public class Response extends BaseCommand {
+
+ public static final byte DATA_STRUCTURE_TYPE = CommandTypes.RESPONSE;
+ int correlationId;
+
+ public byte getDataStructureType() {
+ return DATA_STRUCTURE_TYPE;
+ }
+
+ /** @openwire:property version=1 */
+ public int getCorrelationId() {
+ return correlationId;
+ }
+
+ public void setCorrelationId(int responseId) {
+ this.correlationId = responseId;
+ }
+
+ public boolean isResponse() {
+ return true;
+ }
+
+ public boolean isException() {
+ return false;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/SessionId.java b/activemq-filters/src/main/java/org/apache/activemq/command/SessionId.java
new file mode 100644
index 00000000..4941302c
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/SessionId.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+public class SessionId implements DataStructure {
+
+ public static final byte DATA_STRUCTURE_TYPE = CommandTypes.SESSION_ID;
+
+ protected String connectionId;
+ protected long value;
+
+ protected transient int hashCode;
+ protected transient String key;
+ protected transient ConnectionId parentId;
+
+ public SessionId() {}
+
+ public SessionId(ConnectionId connectionId, long sessionId) {
+ this.connectionId = connectionId.getValue();
+ this.value = sessionId;
+ }
+
+ public SessionId(SessionId id) {
+ this.connectionId = id.getConnectionId();
+ this.value = id.getValue();
+ }
+
+ public SessionId(ProducerId id) {
+ this.connectionId = id.getConnectionId();
+ this.value = id.getSessionId();
+ }
+
+ public SessionId(ConsumerId id) {
+ this.connectionId = id.getConnectionId();
+ this.value = id.getSessionId();
+ }
+
+ public ConnectionId getParentId() {
+ if (parentId == null) {
+ parentId = new ConnectionId(this);
+ }
+ return parentId;
+ }
+
+ public int hashCode() {
+ if (hashCode == 0) {
+ hashCode = connectionId.hashCode() ^ (int) value;
+ }
+ return hashCode;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || o.getClass() != SessionId.class) {
+ return false;
+ }
+ SessionId id = (SessionId) o;
+ return value == id.value && connectionId.equals(id.connectionId);
+ }
+
+ public byte getDataStructureType() {
+ return DATA_STRUCTURE_TYPE;
+ }
+
+ /** @openwire:property version=1 cache=true */
+ public String getConnectionId() {
+ return connectionId;
+ }
+
+ public void setConnectionId(String connectionId) {
+ this.connectionId = connectionId;
+ }
+
+ /** @openwire:property version=1 */
+ public long getValue() {
+ return value;
+ }
+
+ public void setValue(long sessionId) {
+ this.value = sessionId;
+ }
+
+ public String toString() {
+ if (key == null) {
+ key = connectionId + ":" + value;
+ }
+ return key;
+ }
+
+ public boolean isMarshallAware() {
+ return false;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/SessionInfo.java b/activemq-filters/src/main/java/org/apache/activemq/command/SessionInfo.java
new file mode 100644
index 00000000..5780a586
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/SessionInfo.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+public class SessionInfo extends BaseCommand {
+
+ public static final byte DATA_STRUCTURE_TYPE = CommandTypes.SESSION_INFO;
+
+ protected SessionId sessionId;
+
+ public SessionInfo() {
+ sessionId = new SessionId();
+ }
+
+ public SessionInfo(SessionId sessionId) {
+ this.sessionId = sessionId;
+ }
+
+ public byte getDataStructureType() {
+ return DATA_STRUCTURE_TYPE;
+ }
+
+ /** @openwire:property version=1 cache=true */
+ public SessionId getSessionId() {
+ return sessionId;
+ }
+
+ public void setSessionId(SessionId sessionId) {
+ this.sessionId = sessionId;
+ }
+
+ public RemoveInfo createRemoveCommand() {
+ RemoveInfo command = new RemoveInfo(getSessionId());
+ command.setResponseRequired(isResponseRequired());
+ return command;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/TransactionId.java b/activemq-filters/src/main/java/org/apache/activemq/command/TransactionId.java
new file mode 100644
index 00000000..cb378a26
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/TransactionId.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+public abstract class TransactionId implements DataStructure {
+
+ public abstract boolean isXATransaction();
+
+ public abstract boolean isLocalTransaction();
+
+ public abstract String getTransactionKey();
+
+ public boolean isMarshallAware() {
+ return false;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/command/UnresolvedDestinationTransformer.java b/activemq-filters/src/main/java/org/apache/activemq/command/UnresolvedDestinationTransformer.java
new file mode 100644
index 00000000..95369df0
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/command/UnresolvedDestinationTransformer.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.command;
+
+import jakarta.jms.Destination;
+import jakarta.jms.JMSException;
+
+public interface UnresolvedDestinationTransformer {
+
+ public ActiveMQDestination transform(Destination dest) throws JMSException;
+
+ public ActiveMQDestination transform(String dest) throws JMSException;
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/filter/function/inListFunction.java b/activemq-filters/src/main/java/org/apache/activemq/filter/function/inListFunction.java
index de01efd2..1c2f53e3 100644
--- a/activemq-filters/src/main/java/org/apache/activemq/filter/function/inListFunction.java
+++ b/activemq-filters/src/main/java/org/apache/activemq/filter/function/inListFunction.java
@@ -22,8 +22,6 @@
* Filter function that matches a value against a list of values and evaluates to an indicator of
* membership in the list. For example:
*
- *
- *
*
INLIST( SPLIT('1,2,3', ',') , '2' )
*
*
Note that the first argument must be a List. Strings containing lists are not acceptable; for
diff --git a/activemq-filters/src/main/java/org/apache/activemq/filter/function/makeListFunction.java b/activemq-filters/src/main/java/org/apache/activemq/filter/function/makeListFunction.java
index bfd86172..cbfcd89c 100644
--- a/activemq-filters/src/main/java/org/apache/activemq/filter/function/makeListFunction.java
+++ b/activemq-filters/src/main/java/org/apache/activemq/filter/function/makeListFunction.java
@@ -22,8 +22,6 @@
* Filter function that creates a list with each argument being one element in the list. For
* example:
*
- *
- *
*
MAKELIST( '1', '2', '3' )
*/
public class makeListFunction implements FilterFunction {
diff --git a/activemq-filters/src/main/java/org/apache/activemq/filter/function/regexMatchFunction.java b/activemq-filters/src/main/java/org/apache/activemq/filter/function/regexMatchFunction.java
index 36d01301..76cd6cc7 100644
--- a/activemq-filters/src/main/java/org/apache/activemq/filter/function/regexMatchFunction.java
+++ b/activemq-filters/src/main/java/org/apache/activemq/filter/function/regexMatchFunction.java
@@ -24,8 +24,6 @@
/**
* Filter function that matches a value against a regular expression.
*
- *
- *
*
REGEX( 'A.B', 'A-B' )
*
*
Note that the regular expression is not anchored; use the anchor characters, ^ and $,
diff --git a/activemq-filters/src/main/java/org/apache/activemq/jndi/JNDIBaseStorable.java b/activemq-filters/src/main/java/org/apache/activemq/jndi/JNDIBaseStorable.java
new file mode 100644
index 00000000..988dc7ef
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/jndi/JNDIBaseStorable.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.jndi;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Properties;
+import javax.naming.NamingException;
+import javax.naming.Reference;
+
+/** Facilitates objects to be stored in JNDI as properties */
+public abstract class JNDIBaseStorable implements JNDIStorableInterface, Externalizable {
+
+ private Properties properties;
+
+ /**
+ * Set the properties that will represent the instance in JNDI
+ *
+ * @param props properties
+ */
+ protected abstract void buildFromProperties(Properties props);
+
+ /**
+ * Initialize the instance from properties stored in JNDI
+ *
+ * @param props properties
+ */
+ protected abstract void populateProperties(Properties props);
+
+ /**
+ * set the properties for this instance as retrieved from JNDI
+ *
+ * @param props properties
+ */
+ public synchronized void setProperties(Properties props) {
+ this.properties = props;
+ buildFromProperties(props);
+ }
+
+ /**
+ * Get the properties from this instance for storing in JNDI
+ *
+ * @return the properties
+ */
+ public synchronized Properties getProperties() {
+ if (this.properties == null) {
+ this.properties = new Properties();
+ }
+ populateProperties(this.properties);
+ return this.properties;
+ }
+
+ /**
+ * Retrive a Reference for this instance to store in JNDI
+ *
+ * @return the built Reference
+ * @throws NamingException if error on building Reference
+ */
+ public Reference getReference() throws NamingException {
+ return JNDIReferenceFactory.createReference(this.getClass().getName(), this);
+ }
+
+ /**
+ * @param in in
+ * @throws IOException IOException
+ * @throws ClassNotFoundException ClassNotFoundException
+ * @see Externalizable#readExternal(ObjectInput)
+ */
+ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ Properties props = (Properties) in.readObject();
+ if (props != null) {
+ setProperties(props);
+ }
+ }
+
+ /**
+ * @param out out
+ * @throws IOException IOException
+ * @see Externalizable#writeExternal(ObjectOutput)
+ */
+ public void writeExternal(ObjectOutput out) throws IOException {
+ out.writeObject(getProperties());
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/jndi/JNDIReferenceFactory.java b/activemq-filters/src/main/java/org/apache/activemq/jndi/JNDIReferenceFactory.java
new file mode 100644
index 00000000..4808fd51
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/jndi/JNDIReferenceFactory.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.jndi;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import javax.naming.StringRefAddr;
+import javax.naming.spi.ObjectFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Converts objects implementing JNDIStorable into a property fields so they can be stored and
+ * regenerated from JNDI
+ */
+public class JNDIReferenceFactory implements ObjectFactory {
+
+ static Logger log = LoggerFactory.getLogger(JNDIReferenceFactory.class);
+
+ /**
+ * This will be called by a JNDIprovider when a Reference is retrieved from a JNDI store - and
+ * generates the original instance
+ *
+ * @param object the Reference object
+ * @param name the JNDI name
+ * @param nameCtx the context
+ * @param environment the environment settings used by JNDI
+ * @return the instance built from the Reference object
+ * @throws Exception if building the instance from Reference fails (usually class not found)
+ */
+ public Object getObjectInstance(Object object, Name name, Context nameCtx, Hashtable environment)
+ throws Exception {
+ Object result = null;
+ if (object instanceof Reference) {
+ Reference reference = (Reference) object;
+
+ if (log.isTraceEnabled()) {
+ log.trace("Getting instance of " + reference.getClassName());
+ }
+
+ Class theClass = loadClass(this, reference.getClassName());
+ if (JNDIStorableInterface.class.isAssignableFrom(theClass)) {
+
+ JNDIStorableInterface store =
+ JNDIStorableInterface.class.cast(theClass.getConstructor().newInstance());
+ Properties properties = new Properties();
+ for (Enumeration iter = reference.getAll(); iter.hasMoreElements(); ) {
+
+ StringRefAddr addr = (StringRefAddr) iter.nextElement();
+ properties.put(addr.getType(), (addr.getContent() == null) ? "" : addr.getContent());
+ }
+ store.setProperties(properties);
+ result = store;
+ }
+ } else {
+ log.error("Object " + object + " is not a reference - cannot load");
+ throw new RuntimeException("Object " + object + " is not a reference");
+ }
+ return result;
+ }
+
+ /**
+ * Create a Reference instance from a JNDIStorable object
+ *
+ * @param instanceClassName
+ * @param po
+ * @return
+ * @throws NamingException
+ */
+ public static Reference createReference(String instanceClassName, JNDIStorableInterface po)
+ throws NamingException {
+ if (log.isTraceEnabled()) {
+ log.trace("Creating reference: " + instanceClassName + "," + po);
+ }
+ Reference result = new Reference(instanceClassName, JNDIReferenceFactory.class.getName(), null);
+ try {
+ Properties props = po.getProperties();
+ for (Enumeration iter = props.propertyNames(); iter.hasMoreElements(); ) {
+ String key = (String) iter.nextElement();
+ String value = props.getProperty(key);
+ StringRefAddr addr = new StringRefAddr(key, value);
+ result.add(addr);
+ }
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ throw new NamingException(e.getMessage());
+ }
+ return result;
+ }
+
+ /**
+ * Retrieve the class loader for a named class
+ *
+ * @param thisObj
+ * @param className
+ * @return
+ * @throws ClassNotFoundException
+ */
+ public static Class loadClass(Object thisObj, String className) throws ClassNotFoundException {
+ // tryu local ClassLoader first.
+ ClassLoader loader = thisObj.getClass().getClassLoader();
+ Class theClass;
+ if (loader != null) {
+ theClass = loader.loadClass(className);
+ } else {
+ // Will be null in jdk1.1.8
+ // use default classLoader
+ theClass = Class.forName(className);
+ }
+ return theClass;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/jndi/JNDIStorableInterface.java b/activemq-filters/src/main/java/org/apache/activemq/jndi/JNDIStorableInterface.java
new file mode 100644
index 00000000..972e2d85
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/jndi/JNDIStorableInterface.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.jndi;
+
+import java.util.Properties;
+import javax.naming.Referenceable;
+
+/** Facilitates objects to be stored in JNDI as properties */
+public interface JNDIStorableInterface extends Referenceable {
+
+ /**
+ * set the properties for this instance as retrieved from JNDI
+ *
+ * @param properties properties
+ */
+ void setProperties(Properties properties);
+
+ /**
+ * Get the properties from this instance for storing in JNDI
+ *
+ * @return the properties that should be stored in JNDI
+ */
+ Properties getProperties();
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/selector/ParseException.java b/activemq-filters/src/main/java/org/apache/activemq/selector/ParseException.java
new file mode 100644
index 00000000..0edb382e
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/selector/ParseException.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 7.0 */
+/* JavaCCOptions:KEEP_LINE_COLUMN=true */
+/**
+ * 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.selector;
+
+/**
+ * This exception is thrown when parse errors are encountered. You can explicitly create objects of
+ * this exception type by calling the method generateParseException in the generated parser.
+ *
+ *
You can modify this class to customize your error reporting mechanisms so long as you retain
+ * the public fields.
+ */
+public class ParseException extends Exception {
+
+ /**
+ * The version identifier for this Serializable class. Increment only if the serialized
+ * form of the class changes.
+ */
+ private static final long serialVersionUID = 1L;
+
+ /** The end of line string for this machine. */
+ protected static String EOL = System.getProperty("line.separator", "\n");
+
+ /**
+ * This constructor is used by the method "generateParseException" in the generated parser.
+ * Calling this constructor generates a new object of this type with the fields "currentToken",
+ * "expectedTokenSequences", and "tokenImage" set.
+ */
+ public ParseException(
+ Token currentTokenVal, int[][] expectedTokenSequencesVal, String[] tokenImageVal) {
+ super(initialise(currentTokenVal, expectedTokenSequencesVal, tokenImageVal));
+ currentToken = currentTokenVal;
+ expectedTokenSequences = expectedTokenSequencesVal;
+ tokenImage = tokenImageVal;
+ }
+
+ /**
+ * The following constructors are for use by you for whatever purpose you can think of.
+ * Constructing the exception in this manner makes the exception behave in the normal way - i.e.,
+ * as documented in the class "Throwable". The fields "errorToken", "expectedTokenSequences", and
+ * "tokenImage" do not contain relevant information. The JavaCC generated code does not use these
+ * constructors.
+ */
+ public ParseException() {
+ super();
+ }
+
+ /** Constructor with message. */
+ public ParseException(String message) {
+ super(message);
+ }
+
+ /**
+ * This is the last token that has been consumed successfully. If this object has been created due
+ * to a parse error, the token following this token will (therefore) be the first error token.
+ */
+ public Token currentToken;
+
+ /**
+ * Each entry in this array is an array of integers. Each array of integers represents a sequence
+ * of tokens (by their ordinal values) that is expected at this point of the parse.
+ */
+ public int[][] expectedTokenSequences;
+
+ /**
+ * This is a reference to the "tokenImage" array of the generated parser within which the parse
+ * error occurred. This array is defined in the generated ...Constants interface.
+ */
+ public String[] tokenImage;
+
+ /**
+ * It uses "currentToken" and "expectedTokenSequences" to generate a parse error message and
+ * returns it. If this object has been created due to a parse error, and you do not catch it (it
+ * gets thrown from the parser) the correct error message gets displayed.
+ */
+ private static String initialise(
+ Token currentToken, int[][] expectedTokenSequences, String[] tokenImage) {
+
+ StringBuilder expected = new StringBuilder();
+ int maxSize = 0;
+ for (int i = 0; i < expectedTokenSequences.length; i++) {
+ if (maxSize < expectedTokenSequences[i].length) {
+ maxSize = expectedTokenSequences[i].length;
+ }
+ for (int j = 0; j < expectedTokenSequences[i].length; j++) {
+ expected.append(tokenImage[expectedTokenSequences[i][j]]).append(' ');
+ }
+ if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) {
+ expected.append("...");
+ }
+ expected.append(EOL).append(" ");
+ }
+ String retval = "Encountered \"";
+ Token tok = currentToken.next;
+ for (int i = 0; i < maxSize; i++) {
+ if (i != 0) retval += " ";
+ if (tok.kind == 0) {
+ retval += tokenImage[0];
+ break;
+ }
+ retval += " " + tokenImage[tok.kind];
+ retval += " \"";
+ retval += add_escapes(tok.image);
+ retval += " \"";
+ tok = tok.next;
+ }
+ if (currentToken.next != null) {
+ retval +=
+ "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
+ }
+ retval += "." + EOL;
+
+ if (expectedTokenSequences.length == 0) {
+ // Nothing to add here
+ } else {
+ if (expectedTokenSequences.length == 1) {
+ retval += "Was expecting:" + EOL + " ";
+ } else {
+ retval += "Was expecting one of:" + EOL + " ";
+ }
+ retval += expected.toString();
+ }
+
+ return retval;
+ }
+
+ /**
+ * Used to convert raw characters to their escaped version when these raw version cannot be used
+ * as part of an ASCII string literal.
+ */
+ static String add_escapes(String str) {
+ StringBuilder retval = new StringBuilder();
+ char ch;
+ for (int i = 0; i < str.length(); i++) {
+ switch (str.charAt(i)) {
+ case '\b':
+ retval.append("\\b");
+ continue;
+ case '\t':
+ retval.append("\\t");
+ continue;
+ case '\n':
+ retval.append("\\n");
+ continue;
+ case '\f':
+ retval.append("\\f");
+ continue;
+ case '\r':
+ retval.append("\\r");
+ continue;
+ case '\"':
+ retval.append("\\\"");
+ continue;
+ case '\'':
+ retval.append("\\\'");
+ continue;
+ case '\\':
+ retval.append("\\\\");
+ continue;
+ default:
+ if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+ String s = "0000" + Integer.toString(ch, 16);
+ retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+ } else {
+ retval.append(ch);
+ }
+ continue;
+ }
+ }
+ return retval.toString();
+ }
+}
+/* JavaCC - OriginalChecksum=5f5fc62f1257792aed58e813cd9fbff9 (do not edit this line) */
diff --git a/activemq-filters/src/main/java/org/apache/activemq/selector/SelectorParser.java b/activemq-filters/src/main/java/org/apache/activemq/selector/SelectorParser.java
new file mode 100644
index 00000000..647eda2a
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/selector/SelectorParser.java
@@ -0,0 +1,1527 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* SelectorParser.java */
+/* Generated By:JavaCC: Do not edit this line. SelectorParser.java */
+/**
+ * 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.selector;
+
+import jakarta.jms.InvalidSelectorException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Map;
+import org.apache.activemq.filter.ArithmeticExpression;
+import org.apache.activemq.filter.BooleanExpression;
+import org.apache.activemq.filter.ComparisonExpression;
+import org.apache.activemq.filter.ConstantExpression;
+import org.apache.activemq.filter.Expression;
+import org.apache.activemq.filter.FunctionCallExpression;
+import org.apache.activemq.filter.FunctionCallExpression.invalidFunctionExpressionException;
+import org.apache.activemq.filter.LogicExpression;
+import org.apache.activemq.filter.PropertyExpression;
+import org.apache.activemq.filter.UnaryExpression;
+import org.apache.activemq.util.LRUCache;
+
+/**
+ * JMS Selector Parser generated by JavaCC
+ *
+ *
Do not edit this .java file directly - it is autogenerated from SelectorParser.jj
+ */
+public class SelectorParser implements SelectorParserConstants {
+
+ private static final Map cache = Collections.synchronizedMap(new LRUCache(100));
+ private static final String CONVERT_STRING_EXPRESSIONS_PREFIX = "convert_string_expressions:";
+
+ public static BooleanExpression parse(String sql) throws InvalidSelectorException {
+ Object result = cache.get(sql);
+ if (result instanceof InvalidSelectorException) {
+ throw (InvalidSelectorException) result;
+ } else if (result instanceof BooleanExpression) {
+ return (BooleanExpression) result;
+ } else {
+
+ boolean convertStringExpressions = false;
+ if (sql.startsWith(CONVERT_STRING_EXPRESSIONS_PREFIX)) {
+ convertStringExpressions = true;
+ sql = sql.substring(CONVERT_STRING_EXPRESSIONS_PREFIX.length());
+ }
+
+ if (convertStringExpressions) {
+ ComparisonExpression.CONVERT_STRING_EXPRESSIONS.set(true);
+ }
+ try {
+ BooleanExpression e = new SelectorParser(sql).parse();
+ cache.put(sql, e);
+ return e;
+ } catch (InvalidSelectorException t) {
+ cache.put(sql, t);
+ throw t;
+ } finally {
+ if (convertStringExpressions) {
+ ComparisonExpression.CONVERT_STRING_EXPRESSIONS.remove();
+ }
+ }
+ }
+ }
+
+ public static void clearCache() {
+ cache.clear();
+ }
+
+ private String sql;
+
+ protected SelectorParser(String sql) {
+ this(new StringReader(sql));
+ this.sql = sql;
+ }
+
+ protected BooleanExpression parse() throws InvalidSelectorException {
+ try {
+ return this.JmsSelector();
+ } catch (Throwable e) {
+ throw (InvalidSelectorException) new InvalidSelectorException(sql).initCause(e);
+ }
+ }
+
+ private static BooleanExpression asBooleanExpression(Expression value) throws ParseException {
+ if (value instanceof BooleanExpression) {
+ return (BooleanExpression) value;
+ }
+ if (value instanceof PropertyExpression) {
+ return UnaryExpression.createBooleanCast(value);
+ }
+ throw new ParseException("Expression will not result in a boolean value: " + value);
+ }
+
+ // ----------------------------------------------------------------------------
+ // Grammer
+ // ----------------------------------------------------------------------------
+ public final BooleanExpression JmsSelector() throws ParseException {
+ Expression left = null;
+ left = orExpression();
+ jj_consume_token(0);
+ {
+ if ("" != null) return asBooleanExpression(left);
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ public final Expression orExpression() throws ParseException {
+ Expression left;
+ Expression right;
+ left = andExpression();
+ label_1:
+ while (true) {
+ switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) {
+ case OR:
+ {;
+ break;
+ }
+ default:
+ break label_1;
+ }
+ jj_consume_token(OR);
+ right = andExpression();
+ left = LogicExpression.createOR(asBooleanExpression(left), asBooleanExpression(right));
+ }
+ {
+ if ("" != null) return left;
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ public final Expression andExpression() throws ParseException {
+ Expression left;
+ Expression right;
+ left = equalityExpression();
+ label_2:
+ while (true) {
+ switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) {
+ case AND:
+ {;
+ break;
+ }
+ default:
+ break label_2;
+ }
+ jj_consume_token(AND);
+ right = equalityExpression();
+ left = LogicExpression.createAND(asBooleanExpression(left), asBooleanExpression(right));
+ }
+ {
+ if ("" != null) return left;
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ public final Expression equalityExpression() throws ParseException {
+ Expression left;
+ Expression right;
+ left = comparisonExpression();
+ label_3:
+ while (true) {
+ switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) {
+ case IS:
+ case 28:
+ case 29:
+ {;
+ break;
+ }
+ default:
+ break label_3;
+ }
+ switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) {
+ case 28:
+ {
+ jj_consume_token(28);
+ right = comparisonExpression();
+ left = ComparisonExpression.createEqual(left, right);
+ break;
+ }
+ case 29:
+ {
+ jj_consume_token(29);
+ right = comparisonExpression();
+ left = ComparisonExpression.createNotEqual(left, right);
+ break;
+ }
+ default:
+ if (jj_2_1(2)) {
+ jj_consume_token(IS);
+ jj_consume_token(NULL);
+ left = ComparisonExpression.createIsNull(left);
+ } else {
+ switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) {
+ case IS:
+ {
+ jj_consume_token(IS);
+ jj_consume_token(NOT);
+ jj_consume_token(NULL);
+ left = ComparisonExpression.createIsNotNull(left);
+ break;
+ }
+ default:
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ }
+ }
+ }
+ {
+ if ("" != null) return left;
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ public final Expression comparisonExpression() throws ParseException {
+ Expression left;
+ Expression right;
+ Expression low;
+ Expression high;
+ String t, u;
+ boolean not;
+ ArrayList list;
+ left = addExpression();
+ label_4:
+ while (true) {
+ switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) {
+ case NOT:
+ case BETWEEN:
+ case LIKE:
+ case IN:
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ {;
+ break;
+ }
+ default:
+ break label_4;
+ }
+ switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) {
+ case 30:
+ {
+ jj_consume_token(30);
+ right = addExpression();
+ left = ComparisonExpression.createGreaterThan(left, right);
+ break;
+ }
+ case 31:
+ {
+ jj_consume_token(31);
+ right = addExpression();
+ left = ComparisonExpression.createGreaterThanEqual(left, right);
+ break;
+ }
+ case 32:
+ {
+ jj_consume_token(32);
+ right = addExpression();
+ left = ComparisonExpression.createLessThan(left, right);
+ break;
+ }
+ case 33:
+ {
+ jj_consume_token(33);
+ right = addExpression();
+ left = ComparisonExpression.createLessThanEqual(left, right);
+ break;
+ }
+ case LIKE:
+ {
+ u = null;
+ jj_consume_token(LIKE);
+ t = stringLitteral();
+ switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) {
+ case ESCAPE:
+ {
+ jj_consume_token(ESCAPE);
+ u = stringLitteral();
+ break;
+ }
+ default:;
+ }
+ left = ComparisonExpression.createLike(left, t, u);
+ break;
+ }
+ default:
+ if (jj_2_2(2)) {
+ u = null;
+ jj_consume_token(NOT);
+ jj_consume_token(LIKE);
+ t = stringLitteral();
+ switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) {
+ case ESCAPE:
+ {
+ jj_consume_token(ESCAPE);
+ u = stringLitteral();
+ break;
+ }
+ default:;
+ }
+ left = ComparisonExpression.createNotLike(left, t, u);
+ } else {
+ switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) {
+ case BETWEEN:
+ {
+ jj_consume_token(BETWEEN);
+ low = addExpression();
+ jj_consume_token(AND);
+ high = addExpression();
+ left = ComparisonExpression.createBetween(left, low, high);
+ break;
+ }
+ default:
+ if (jj_2_3(2)) {
+ jj_consume_token(NOT);
+ jj_consume_token(BETWEEN);
+ low = addExpression();
+ jj_consume_token(AND);
+ high = addExpression();
+ left = ComparisonExpression.createNotBetween(left, low, high);
+ } else {
+ switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) {
+ case IN:
+ {
+ jj_consume_token(IN);
+ jj_consume_token(34);
+ t = stringLitteral();
+ list = new ArrayList();
+ list.add(t);
+ label_5:
+ while (true) {
+ switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) {
+ case 35:
+ {;
+ break;
+ }
+ default:
+ break label_5;
+ }
+ jj_consume_token(35);
+ t = stringLitteral();
+ list.add(t);
+ }
+ jj_consume_token(36);
+ left = ComparisonExpression.createInFilter(left, list);
+ break;
+ }
+ default:
+ if (jj_2_4(2)) {
+ jj_consume_token(NOT);
+ jj_consume_token(IN);
+ jj_consume_token(34);
+ t = stringLitteral();
+ list = new ArrayList();
+ list.add(t);
+ label_6:
+ while (true) {
+ switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) {
+ case 35:
+ {;
+ break;
+ }
+ default:
+ break label_6;
+ }
+ jj_consume_token(35);
+ t = stringLitteral();
+ list.add(t);
+ }
+ jj_consume_token(36);
+ left = ComparisonExpression.createNotInFilter(left, list);
+ } else {
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ {
+ if ("" != null) return left;
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ public final Expression addExpression() throws ParseException {
+ Expression left;
+ Expression right;
+ left = multExpr();
+ label_7:
+ while (true) {
+ if (jj_2_5(2147483647)) {;
+ } else {
+ break label_7;
+ }
+ switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) {
+ case 37:
+ {
+ jj_consume_token(37);
+ right = multExpr();
+ left = ArithmeticExpression.createPlus(left, right);
+ break;
+ }
+ case 38:
+ {
+ jj_consume_token(38);
+ right = multExpr();
+ left = ArithmeticExpression.createMinus(left, right);
+ break;
+ }
+ default:
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ }
+ {
+ if ("" != null) return left;
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ public final Expression multExpr() throws ParseException {
+ Expression left;
+ Expression right;
+ left = unaryExpr();
+ label_8:
+ while (true) {
+ switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) {
+ case 39:
+ case 40:
+ case 41:
+ {;
+ break;
+ }
+ default:
+ break label_8;
+ }
+ switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) {
+ case 39:
+ {
+ jj_consume_token(39);
+ right = unaryExpr();
+ left = ArithmeticExpression.createMultiply(left, right);
+ break;
+ }
+ case 40:
+ {
+ jj_consume_token(40);
+ right = unaryExpr();
+ left = ArithmeticExpression.createDivide(left, right);
+ break;
+ }
+ case 41:
+ {
+ jj_consume_token(41);
+ right = unaryExpr();
+ left = ArithmeticExpression.createMod(left, right);
+ break;
+ }
+ default:
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ }
+ {
+ if ("" != null) return left;
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ public final Expression unaryExpr() throws ParseException {
+ String s = null;
+ Expression left = null;
+ if (jj_2_6(2147483647)) {
+ jj_consume_token(37);
+ left = unaryExpr();
+ } else {
+ switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) {
+ case 38:
+ {
+ jj_consume_token(38);
+ left = unaryExpr();
+ left = UnaryExpression.createNegate(left);
+ break;
+ }
+ case NOT:
+ {
+ jj_consume_token(NOT);
+ left = unaryExpr();
+ left = UnaryExpression.createNOT(asBooleanExpression(left));
+ break;
+ }
+ case XPATH:
+ {
+ jj_consume_token(XPATH);
+ s = stringLitteral();
+ left = UnaryExpression.createXPath(s);
+ break;
+ }
+ case XQUERY:
+ {
+ jj_consume_token(XQUERY);
+ s = stringLitteral();
+ left = UnaryExpression.createXQuery(s);
+ break;
+ }
+ default:
+ if (jj_2_7(2147483647)) {
+ left = functionCallExpr();
+ } else {
+ switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) {
+ case TRUE:
+ case FALSE:
+ case NULL:
+ case DECIMAL_LITERAL:
+ case HEX_LITERAL:
+ case OCTAL_LITERAL:
+ case FLOATING_POINT_LITERAL:
+ case STRING_LITERAL:
+ case ID:
+ case 34:
+ {
+ left = primaryExpr();
+ break;
+ }
+ default:
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ }
+ }
+ }
+ {
+ if ("" != null) return left;
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ public final Expression functionCallExpr() throws ParseException {
+ Token func_name;
+ FunctionCallExpression func_call = null;
+ Expression arg = null;
+ ArrayList arg_list = new ArrayList();
+ func_name = jj_consume_token(ID);
+ jj_consume_token(34);
+ arg = unaryExpr();
+ arg_list.add(arg);
+ label_9:
+ while (true) {
+ switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) {
+ case 35:
+ {;
+ break;
+ }
+ default:
+ break label_9;
+ }
+ jj_consume_token(35);
+ arg = unaryExpr();
+ arg_list.add(arg);
+ }
+ jj_consume_token(36);
+ try {
+ {
+ if ("" != null) return FunctionCallExpression.createFunctionCall(func_name.image, arg_list);
+ }
+ } catch (invalidFunctionExpressionException inv_exc) {
+ // Re-throw as an error to avoid the need to propogate the throws declaration.
+ {
+ if (true) throw new Error("invalid function call expression", inv_exc);
+ }
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ public final Expression primaryExpr() throws ParseException {
+ Expression left = null;
+ switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) {
+ case TRUE:
+ case FALSE:
+ case NULL:
+ case DECIMAL_LITERAL:
+ case HEX_LITERAL:
+ case OCTAL_LITERAL:
+ case FLOATING_POINT_LITERAL:
+ case STRING_LITERAL:
+ {
+ left = literal();
+ break;
+ }
+ case ID:
+ {
+ left = variable();
+ break;
+ }
+ case 34:
+ {
+ jj_consume_token(34);
+ left = orExpression();
+ jj_consume_token(36);
+ break;
+ }
+ default:
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ {
+ if ("" != null) return left;
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ public final ConstantExpression literal() throws ParseException {
+ Token t;
+ String s;
+ ConstantExpression left = null;
+ switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) {
+ case STRING_LITERAL:
+ {
+ s = stringLitteral();
+ left = new ConstantExpression(s);
+ break;
+ }
+ case DECIMAL_LITERAL:
+ {
+ t = jj_consume_token(DECIMAL_LITERAL);
+ left = ConstantExpression.createFromDecimal(t.image);
+ break;
+ }
+ case HEX_LITERAL:
+ {
+ t = jj_consume_token(HEX_LITERAL);
+ left = ConstantExpression.createFromHex(t.image);
+ break;
+ }
+ case OCTAL_LITERAL:
+ {
+ t = jj_consume_token(OCTAL_LITERAL);
+ left = ConstantExpression.createFromOctal(t.image);
+ break;
+ }
+ case FLOATING_POINT_LITERAL:
+ {
+ t = jj_consume_token(FLOATING_POINT_LITERAL);
+ left = ConstantExpression.createFloat(t.image);
+ break;
+ }
+ case TRUE:
+ {
+ jj_consume_token(TRUE);
+ left = ConstantExpression.TRUE;
+ break;
+ }
+ case FALSE:
+ {
+ jj_consume_token(FALSE);
+ left = ConstantExpression.FALSE;
+ break;
+ }
+ case NULL:
+ {
+ jj_consume_token(NULL);
+ left = ConstantExpression.NULL;
+ break;
+ }
+ default:
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ {
+ if ("" != null) return left;
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ public final String stringLitteral() throws ParseException {
+ Token t;
+ StringBuffer rc = new StringBuffer();
+ boolean first = true;
+ t = jj_consume_token(STRING_LITERAL);
+ // Decode the sting value.
+ String image = t.image;
+ for (int i = 1; i < image.length() - 1; i++) {
+ char c = image.charAt(i);
+ if (c == '\'') i++;
+ rc.append(c);
+ }
+ {
+ if ("" != null) return rc.toString();
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ public final PropertyExpression variable() throws ParseException {
+ Token t;
+ PropertyExpression left = null;
+ t = jj_consume_token(ID);
+ left = new PropertyExpression(t.image);
+ {
+ if ("" != null) return left;
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ private boolean jj_2_1(int xla) {
+ jj_la = xla;
+ jj_lastpos = jj_scanpos = token;
+ try {
+ return (!jj_3_1());
+ } catch (LookaheadSuccess ls) {
+ return true;
+ }
+ }
+
+ private boolean jj_2_2(int xla) {
+ jj_la = xla;
+ jj_lastpos = jj_scanpos = token;
+ try {
+ return (!jj_3_2());
+ } catch (LookaheadSuccess ls) {
+ return true;
+ }
+ }
+
+ private boolean jj_2_3(int xla) {
+ jj_la = xla;
+ jj_lastpos = jj_scanpos = token;
+ try {
+ return (!jj_3_3());
+ } catch (LookaheadSuccess ls) {
+ return true;
+ }
+ }
+
+ private boolean jj_2_4(int xla) {
+ jj_la = xla;
+ jj_lastpos = jj_scanpos = token;
+ try {
+ return (!jj_3_4());
+ } catch (LookaheadSuccess ls) {
+ return true;
+ }
+ }
+
+ private boolean jj_2_5(int xla) {
+ jj_la = xla;
+ jj_lastpos = jj_scanpos = token;
+ try {
+ return (!jj_3_5());
+ } catch (LookaheadSuccess ls) {
+ return true;
+ }
+ }
+
+ private boolean jj_2_6(int xla) {
+ jj_la = xla;
+ jj_lastpos = jj_scanpos = token;
+ try {
+ return (!jj_3_6());
+ } catch (LookaheadSuccess ls) {
+ return true;
+ }
+ }
+
+ private boolean jj_2_7(int xla) {
+ jj_la = xla;
+ jj_lastpos = jj_scanpos = token;
+ try {
+ return (!jj_3_7());
+ } catch (LookaheadSuccess ls) {
+ return true;
+ }
+ }
+
+ private boolean jj_3R_unaryExpr_469_5_11() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_unaryExpr_470_9_13()) {
+ jj_scanpos = xsp;
+ if (jj_3R_unaryExpr_473_9_14()) {
+ jj_scanpos = xsp;
+ if (jj_3R_unaryExpr_478_9_15()) {
+ jj_scanpos = xsp;
+ if (jj_3R_unaryExpr_483_9_16()) {
+ jj_scanpos = xsp;
+ if (jj_3R_unaryExpr_488_9_17()) {
+ jj_scanpos = xsp;
+ if (jj_3R_unaryExpr_493_13_18()) {
+ jj_scanpos = xsp;
+ if (jj_3R_unaryExpr_496_9_19()) return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_comparisonExpression_358_17_59() {
+ if (jj_scan_token(IN)) return true;
+ if (jj_scan_token(34)) return true;
+ if (jj_3R_stringLitteral_635_5_23()) return true;
+ Token xsp;
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_comparisonExpression_366_25_64()) {
+ jj_scanpos = xsp;
+ break;
+ }
+ }
+ if (jj_scan_token(36)) return true;
+ return false;
+ }
+
+ private boolean jj_3R_equalityExpression_267_13_49() {
+ if (jj_scan_token(28)) return true;
+ if (jj_3R_comparisonExpression_305_5_45()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_equalityExpression_267_13_46() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_equalityExpression_267_13_49()) {
+ jj_scanpos = xsp;
+ if (jj_3R_equalityExpression_272_13_50()) {
+ jj_scanpos = xsp;
+ if (jj_3_1()) {
+ jj_scanpos = xsp;
+ if (jj_3R_equalityExpression_283_13_51()) return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_variable_655_5_31() {
+ if (jj_scan_token(ID)) return true;
+ return false;
+ }
+
+ private boolean jj_3_3() {
+ if (jj_scan_token(NOT)) return true;
+ if (jj_scan_token(BETWEEN)) return true;
+ if (jj_3R_addExpression_412_5_47()) return true;
+ if (jj_scan_token(AND)) return true;
+ if (jj_3R_addExpression_412_5_47()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_primaryExpr_550_9_29() {
+ if (jj_scan_token(34)) return true;
+ if (jj_3R_orExpression_221_5_32()) return true;
+ if (jj_scan_token(36)) return true;
+ return false;
+ }
+
+ private boolean jj_3R_primaryExpr_548_9_28() {
+ if (jj_3R_variable_655_5_31()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_multExpr_451_9_22() {
+ if (jj_scan_token(41)) return true;
+ if (jj_3R_unaryExpr_469_5_11()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_primaryExpr_546_9_27() {
+ if (jj_3R_literal_566_5_30()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_comparisonExpression_347_17_58() {
+ if (jj_scan_token(BETWEEN)) return true;
+ if (jj_3R_addExpression_412_5_47()) return true;
+ if (jj_scan_token(AND)) return true;
+ if (jj_3R_addExpression_412_5_47()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_equalityExpression_263_5_43() {
+ if (jj_3R_comparisonExpression_305_5_45()) return true;
+ Token xsp;
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_equalityExpression_267_13_46()) {
+ jj_scanpos = xsp;
+ break;
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_multExpr_446_9_21() {
+ if (jj_scan_token(40)) return true;
+ if (jj_3R_unaryExpr_469_5_11()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_primaryExpr_545_5_25() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_primaryExpr_546_9_27()) {
+ jj_scanpos = xsp;
+ if (jj_3R_primaryExpr_548_9_28()) {
+ jj_scanpos = xsp;
+ if (jj_3R_primaryExpr_550_9_29()) return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_comparisonExpression_333_23_62() {
+ if (jj_scan_token(ESCAPE)) return true;
+ if (jj_3R_stringLitteral_635_5_23()) return true;
+ return false;
+ }
+
+ private boolean jj_3_2() {
+ if (jj_scan_token(NOT)) return true;
+ if (jj_scan_token(LIKE)) return true;
+ if (jj_3R_stringLitteral_635_5_23()) return true;
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_comparisonExpression_342_53_63()) jj_scanpos = xsp;
+ return false;
+ }
+
+ private boolean jj_3R_multExpr_441_9_12() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_multExpr_441_9_20()) {
+ jj_scanpos = xsp;
+ if (jj_3R_multExpr_446_9_21()) {
+ jj_scanpos = xsp;
+ if (jj_3R_multExpr_451_9_22()) return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_multExpr_441_9_20() {
+ if (jj_scan_token(39)) return true;
+ if (jj_3R_unaryExpr_469_5_11()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_stringLitteral_635_5_23() {
+ if (jj_scan_token(STRING_LITERAL)) return true;
+ return false;
+ }
+
+ private boolean jj_3R_andExpression_246_13_44() {
+ if (jj_scan_token(AND)) return true;
+ if (jj_3R_equalityExpression_263_5_43()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_multExpr_439_5_10() {
+ if (jj_3R_unaryExpr_469_5_11()) return true;
+ Token xsp;
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_multExpr_441_9_12()) {
+ jj_scanpos = xsp;
+ break;
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_comparisonExpression_329_17_57() {
+ if (jj_scan_token(LIKE)) return true;
+ if (jj_3R_stringLitteral_635_5_23()) return true;
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_comparisonExpression_333_23_62()) jj_scanpos = xsp;
+ return false;
+ }
+
+ private boolean jj_3R_andExpression_243_5_41() {
+ if (jj_3R_equalityExpression_263_5_43()) return true;
+ Token xsp;
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_andExpression_246_13_44()) {
+ jj_scanpos = xsp;
+ break;
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_functionCallExpr_519_13_26() {
+ if (jj_scan_token(35)) return true;
+ if (jj_3R_unaryExpr_469_5_11()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_comparisonExpression_324_17_56() {
+ if (jj_scan_token(33)) return true;
+ if (jj_3R_addExpression_412_5_47()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_literal_616_9_40() {
+ if (jj_scan_token(NULL)) return true;
+ return false;
+ }
+
+ private boolean jj_3R_addExpression_421_13_61() {
+ if (jj_scan_token(38)) return true;
+ if (jj_3R_multExpr_439_5_10()) return true;
+ return false;
+ }
+
+ private boolean jj_3_5() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_scan_token(37)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(38)) return true;
+ }
+ if (jj_3R_multExpr_439_5_10()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_comparisonExpression_319_17_55() {
+ if (jj_scan_token(32)) return true;
+ if (jj_3R_addExpression_412_5_47()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_addExpression_416_13_60() {
+ if (jj_scan_token(37)) return true;
+ if (jj_3R_multExpr_439_5_10()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_literal_609_9_39() {
+ if (jj_scan_token(FALSE)) return true;
+ return false;
+ }
+
+ private boolean jj_3R_orExpression_224_13_42() {
+ if (jj_scan_token(OR)) return true;
+ if (jj_3R_andExpression_243_5_41()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_comparisonExpression_314_17_54() {
+ if (jj_scan_token(31)) return true;
+ if (jj_3R_addExpression_412_5_47()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_addExpression_414_9_52() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_addExpression_416_13_60()) {
+ jj_scanpos = xsp;
+ if (jj_3R_addExpression_421_13_61()) return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3_7() {
+ if (jj_scan_token(ID)) return true;
+ if (jj_scan_token(34)) return true;
+ return false;
+ }
+
+ private boolean jj_3R_functionCallExpr_512_5_24() {
+ if (jj_scan_token(ID)) return true;
+ if (jj_scan_token(34)) return true;
+ if (jj_3R_unaryExpr_469_5_11()) return true;
+ Token xsp;
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_functionCallExpr_519_13_26()) {
+ jj_scanpos = xsp;
+ break;
+ }
+ }
+ if (jj_scan_token(36)) return true;
+ return false;
+ }
+
+ private boolean jj_3R_comparisonExpression_309_17_53() {
+ if (jj_scan_token(30)) return true;
+ if (jj_3R_addExpression_412_5_47()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_comparisonExpression_309_17_48() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_comparisonExpression_309_17_53()) {
+ jj_scanpos = xsp;
+ if (jj_3R_comparisonExpression_314_17_54()) {
+ jj_scanpos = xsp;
+ if (jj_3R_comparisonExpression_319_17_55()) {
+ jj_scanpos = xsp;
+ if (jj_3R_comparisonExpression_324_17_56()) {
+ jj_scanpos = xsp;
+ if (jj_3R_comparisonExpression_329_17_57()) {
+ jj_scanpos = xsp;
+ if (jj_3_2()) {
+ jj_scanpos = xsp;
+ if (jj_3R_comparisonExpression_347_17_58()) {
+ jj_scanpos = xsp;
+ if (jj_3_3()) {
+ jj_scanpos = xsp;
+ if (jj_3R_comparisonExpression_358_17_59()) {
+ jj_scanpos = xsp;
+ if (jj_3_4()) return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_literal_602_9_38() {
+ if (jj_scan_token(TRUE)) return true;
+ return false;
+ }
+
+ private boolean jj_3R_addExpression_412_5_47() {
+ if (jj_3R_multExpr_439_5_10()) return true;
+ Token xsp;
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_addExpression_414_9_52()) {
+ jj_scanpos = xsp;
+ break;
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_orExpression_221_5_32() {
+ if (jj_3R_andExpression_243_5_41()) return true;
+ Token xsp;
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_orExpression_224_13_42()) {
+ jj_scanpos = xsp;
+ break;
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_literal_595_9_37() {
+ if (jj_scan_token(FLOATING_POINT_LITERAL)) return true;
+ return false;
+ }
+
+ private boolean jj_3R_comparisonExpression_387_25_65() {
+ if (jj_scan_token(35)) return true;
+ if (jj_3R_stringLitteral_635_5_23()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_unaryExpr_493_13_18() {
+ if (jj_3R_functionCallExpr_512_5_24()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_unaryExpr_496_9_19() {
+ if (jj_3R_primaryExpr_545_5_25()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_literal_588_9_36() {
+ if (jj_scan_token(OCTAL_LITERAL)) return true;
+ return false;
+ }
+
+ private boolean jj_3R_comparisonExpression_305_5_45() {
+ if (jj_3R_addExpression_412_5_47()) return true;
+ Token xsp;
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_comparisonExpression_309_17_48()) {
+ jj_scanpos = xsp;
+ break;
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_unaryExpr_488_9_17() {
+ if (jj_scan_token(XQUERY)) return true;
+ if (jj_3R_stringLitteral_635_5_23()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_literal_581_9_35() {
+ if (jj_scan_token(HEX_LITERAL)) return true;
+ return false;
+ }
+
+ private boolean jj_3R_unaryExpr_483_9_16() {
+ if (jj_scan_token(XPATH)) return true;
+ if (jj_3R_stringLitteral_635_5_23()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_comparisonExpression_342_53_63() {
+ if (jj_scan_token(ESCAPE)) return true;
+ if (jj_3R_stringLitteral_635_5_23()) return true;
+ return false;
+ }
+
+ private boolean jj_3_4() {
+ if (jj_scan_token(NOT)) return true;
+ if (jj_scan_token(IN)) return true;
+ if (jj_scan_token(34)) return true;
+ if (jj_3R_stringLitteral_635_5_23()) return true;
+ Token xsp;
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_comparisonExpression_387_25_65()) {
+ jj_scanpos = xsp;
+ break;
+ }
+ }
+ if (jj_scan_token(36)) return true;
+ return false;
+ }
+
+ private boolean jj_3_6() {
+ if (jj_scan_token(37)) return true;
+ if (jj_3R_unaryExpr_469_5_11()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_literal_574_9_34() {
+ if (jj_scan_token(DECIMAL_LITERAL)) return true;
+ return false;
+ }
+
+ private boolean jj_3R_unaryExpr_478_9_15() {
+ if (jj_scan_token(NOT)) return true;
+ if (jj_3R_unaryExpr_469_5_11()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_comparisonExpression_366_25_64() {
+ if (jj_scan_token(35)) return true;
+ if (jj_3R_stringLitteral_635_5_23()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_equalityExpression_283_13_51() {
+ if (jj_scan_token(IS)) return true;
+ if (jj_scan_token(NOT)) return true;
+ if (jj_scan_token(NULL)) return true;
+ return false;
+ }
+
+ private boolean jj_3R_unaryExpr_473_9_14() {
+ if (jj_scan_token(38)) return true;
+ if (jj_3R_unaryExpr_469_5_11()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_literal_567_9_33() {
+ if (jj_3R_stringLitteral_635_5_23()) return true;
+ return false;
+ }
+
+ private boolean jj_3_1() {
+ if (jj_scan_token(IS)) return true;
+ if (jj_scan_token(NULL)) return true;
+ return false;
+ }
+
+ private boolean jj_3R_unaryExpr_470_9_13() {
+ if (jj_scan_token(37)) return true;
+ if (jj_3R_unaryExpr_469_5_11()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_literal_566_5_30() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_literal_567_9_33()) {
+ jj_scanpos = xsp;
+ if (jj_3R_literal_574_9_34()) {
+ jj_scanpos = xsp;
+ if (jj_3R_literal_581_9_35()) {
+ jj_scanpos = xsp;
+ if (jj_3R_literal_588_9_36()) {
+ jj_scanpos = xsp;
+ if (jj_3R_literal_595_9_37()) {
+ jj_scanpos = xsp;
+ if (jj_3R_literal_602_9_38()) {
+ jj_scanpos = xsp;
+ if (jj_3R_literal_609_9_39()) {
+ jj_scanpos = xsp;
+ if (jj_3R_literal_616_9_40()) return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_equalityExpression_272_13_50() {
+ if (jj_scan_token(29)) return true;
+ if (jj_3R_comparisonExpression_305_5_45()) return true;
+ return false;
+ }
+
+ /** Generated Token Manager. */
+ public SelectorParserTokenManager token_source;
+
+ SimpleCharStream jj_input_stream;
+ /** Current token. */
+ public Token token;
+ /** Next token. */
+ public Token jj_nt;
+
+ private int jj_ntk;
+ private Token jj_scanpos, jj_lastpos;
+ private int jj_la;
+
+ /** Constructor with InputStream. */
+ public SelectorParser(InputStream stream) {
+ this(stream, null);
+ }
+ /** Constructor with InputStream and supplied encoding */
+ public SelectorParser(InputStream stream, String encoding) {
+ try {
+ jj_input_stream = new SimpleCharStream(stream, encoding, 1, 1);
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ token_source = new SelectorParserTokenManager(jj_input_stream);
+ token = new Token();
+ jj_ntk = -1;
+ }
+
+ /** Reinitialise. */
+ public void ReInit(InputStream stream) {
+ ReInit(stream, null);
+ }
+ /** Reinitialise. */
+ public void ReInit(InputStream stream, String encoding) {
+ try {
+ jj_input_stream.ReInit(stream, encoding, 1, 1);
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ token_source.ReInit(jj_input_stream);
+ token = new Token();
+ jj_ntk = -1;
+ }
+
+ /** Constructor. */
+ public SelectorParser(Reader stream) {
+ jj_input_stream = new SimpleCharStream(stream, 1, 1);
+ token_source = new SelectorParserTokenManager(jj_input_stream);
+ token = new Token();
+ jj_ntk = -1;
+ }
+
+ /** Reinitialise. */
+ public void ReInit(Reader stream) {
+ if (jj_input_stream == null) {
+ jj_input_stream = new SimpleCharStream(stream, 1, 1);
+ } else {
+ jj_input_stream.ReInit(stream, 1, 1);
+ }
+ if (token_source == null) {
+ token_source = new SelectorParserTokenManager(jj_input_stream);
+ }
+
+ token_source.ReInit(jj_input_stream);
+ token = new Token();
+ jj_ntk = -1;
+ }
+
+ /** Constructor with generated Token Manager. */
+ public SelectorParser(SelectorParserTokenManager tm) {
+ token_source = tm;
+ token = new Token();
+ jj_ntk = -1;
+ }
+
+ /** Reinitialise. */
+ public void ReInit(SelectorParserTokenManager tm) {
+ token_source = tm;
+ token = new Token();
+ jj_ntk = -1;
+ }
+
+ private Token jj_consume_token(int kind) throws ParseException {
+ Token oldToken;
+ if ((oldToken = token).next != null) token = token.next;
+ else token = token.next = token_source.getNextToken();
+ jj_ntk = -1;
+ if (token.kind == kind) {
+ return token;
+ }
+ token = oldToken;
+ throw generateParseException();
+ }
+
+ @SuppressWarnings("serial")
+ private static final class LookaheadSuccess extends Error {
+ @Override
+ public Throwable fillInStackTrace() {
+ return this;
+ }
+ }
+
+ private static final LookaheadSuccess jj_ls = new LookaheadSuccess();
+
+ private boolean jj_scan_token(int kind) {
+ if (jj_scanpos == jj_lastpos) {
+ jj_la--;
+ if (jj_scanpos.next == null) {
+ jj_lastpos = jj_scanpos = jj_scanpos.next = token_source.getNextToken();
+ } else {
+ jj_lastpos = jj_scanpos = jj_scanpos.next;
+ }
+ } else {
+ jj_scanpos = jj_scanpos.next;
+ }
+ if (jj_scanpos.kind != kind) return true;
+ if (jj_la == 0 && jj_scanpos == jj_lastpos) throw jj_ls;
+ return false;
+ }
+
+ /** Get the next Token. */
+ public final Token getNextToken() {
+ if (token.next != null) token = token.next;
+ else token = token.next = token_source.getNextToken();
+ jj_ntk = -1;
+ return token;
+ }
+
+ /** Get the specific Token. */
+ public final Token getToken(int index) {
+ Token t = token;
+ for (int i = 0; i < index; i++) {
+ if (t.next != null) t = t.next;
+ else t = t.next = token_source.getNextToken();
+ }
+ return t;
+ }
+
+ private int jj_ntk_f() {
+ if ((jj_nt = token.next) == null)
+ return (jj_ntk = (token.next = token_source.getNextToken()).kind);
+ else return (jj_ntk = jj_nt.kind);
+ }
+
+ /** Generate ParseException. */
+ public ParseException generateParseException() {
+ Token errortok = token.next;
+ int line = errortok.beginLine, column = errortok.beginColumn;
+ String mess = (errortok.kind == 0) ? tokenImage[0] : errortok.image;
+ return new ParseException(
+ "Parse error at line " + line + ", column " + column + ". Encountered: " + mess);
+ }
+
+ private boolean trace_enabled;
+
+ /** Trace enabled. */
+ public final boolean trace_enabled() {
+ return trace_enabled;
+ }
+
+ /** Enable tracing. */
+ public final void enable_tracing() {}
+
+ /** Disable tracing. */
+ public final void disable_tracing() {}
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/selector/SelectorParserConstants.java b/activemq-filters/src/main/java/org/apache/activemq/selector/SelectorParserConstants.java
new file mode 100644
index 00000000..2a88a6b5
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/selector/SelectorParserConstants.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* Generated By:JavaCC: Do not edit this line. SelectorParserConstants.java */
+/**
+ * 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.selector;
+
+/** Token literal values and constants. Generated by org.javacc.parser.OtherFilesGen#start() */
+public interface SelectorParserConstants {
+
+ /** End of File. */
+ int EOF = 0;
+ /** RegularExpression Id. */
+ int LINE_COMMENT = 6;
+ /** RegularExpression Id. */
+ int BLOCK_COMMENT = 7;
+ /** RegularExpression Id. */
+ int NOT = 8;
+ /** RegularExpression Id. */
+ int AND = 9;
+ /** RegularExpression Id. */
+ int OR = 10;
+ /** RegularExpression Id. */
+ int BETWEEN = 11;
+ /** RegularExpression Id. */
+ int LIKE = 12;
+ /** RegularExpression Id. */
+ int ESCAPE = 13;
+ /** RegularExpression Id. */
+ int IN = 14;
+ /** RegularExpression Id. */
+ int IS = 15;
+ /** RegularExpression Id. */
+ int TRUE = 16;
+ /** RegularExpression Id. */
+ int FALSE = 17;
+ /** RegularExpression Id. */
+ int NULL = 18;
+ /** RegularExpression Id. */
+ int XPATH = 19;
+ /** RegularExpression Id. */
+ int XQUERY = 20;
+ /** RegularExpression Id. */
+ int DECIMAL_LITERAL = 21;
+ /** RegularExpression Id. */
+ int HEX_LITERAL = 22;
+ /** RegularExpression Id. */
+ int OCTAL_LITERAL = 23;
+ /** RegularExpression Id. */
+ int FLOATING_POINT_LITERAL = 24;
+ /** RegularExpression Id. */
+ int EXPONENT = 25;
+ /** RegularExpression Id. */
+ int STRING_LITERAL = 26;
+ /** RegularExpression Id. */
+ int ID = 27;
+
+ /** Lexical state. */
+ int DEFAULT = 0;
+
+ /** Literal token values. */
+ String[] tokenImage = {
+ "",
+ "\" \"",
+ "\"\\t\"",
+ "\"\\n\"",
+ "\"\\r\"",
+ "\"\\f\"",
+ "",
+ "",
+ "\"NOT\"",
+ "\"AND\"",
+ "\"OR\"",
+ "\"BETWEEN\"",
+ "\"LIKE\"",
+ "\"ESCAPE\"",
+ "\"IN\"",
+ "\"IS\"",
+ "\"TRUE\"",
+ "\"FALSE\"",
+ "\"NULL\"",
+ "\"XPATH\"",
+ "\"XQUERY\"",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "\"=\"",
+ "\"<>\"",
+ "\">\"",
+ "\">=\"",
+ "\"<\"",
+ "\"<=\"",
+ "\"(\"",
+ "\",\"",
+ "\")\"",
+ "\"+\"",
+ "\"-\"",
+ "\"*\"",
+ "\"/\"",
+ "\"%\"",
+ };
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/selector/SelectorParserTokenManager.java b/activemq-filters/src/main/java/org/apache/activemq/selector/SelectorParserTokenManager.java
new file mode 100644
index 00000000..c699bcfc
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/selector/SelectorParserTokenManager.java
@@ -0,0 +1,1097 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* SelectorParserTokenManager.java */
+/* Generated By:JavaCC: Do not edit this line. SelectorParserTokenManager.java */
+/**
+ * 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.selector;
+
+import java.io.IOException;
+import java.io.PrintStream;
+
+/** Token Manager. */
+@SuppressWarnings("unused")
+public class SelectorParserTokenManager implements SelectorParserConstants {
+
+ /** Debug output. */
+ public PrintStream debugStream = System.out;
+ /** Set debug output. */
+ public void setDebugStream(PrintStream ds) {
+ debugStream = ds;
+ }
+
+ private int jjStopAtPos(int pos, int kind) {
+ jjmatchedKind = kind;
+ jjmatchedPos = pos;
+ return pos + 1;
+ }
+
+ private int jjMoveStringLiteralDfa0_0() {
+ switch (curChar) {
+ case 9:
+ jjmatchedKind = 2;
+ return jjMoveNfa_0(5, 0);
+ case 10:
+ jjmatchedKind = 3;
+ return jjMoveNfa_0(5, 0);
+ case 12:
+ jjmatchedKind = 5;
+ return jjMoveNfa_0(5, 0);
+ case 13:
+ jjmatchedKind = 4;
+ return jjMoveNfa_0(5, 0);
+ case 32:
+ jjmatchedKind = 1;
+ return jjMoveNfa_0(5, 0);
+ case 37:
+ jjmatchedKind = 41;
+ return jjMoveNfa_0(5, 0);
+ case 40:
+ jjmatchedKind = 34;
+ return jjMoveNfa_0(5, 0);
+ case 41:
+ jjmatchedKind = 36;
+ return jjMoveNfa_0(5, 0);
+ case 42:
+ jjmatchedKind = 39;
+ return jjMoveNfa_0(5, 0);
+ case 43:
+ jjmatchedKind = 37;
+ return jjMoveNfa_0(5, 0);
+ case 44:
+ jjmatchedKind = 35;
+ return jjMoveNfa_0(5, 0);
+ case 45:
+ jjmatchedKind = 38;
+ return jjMoveNfa_0(5, 0);
+ case 47:
+ jjmatchedKind = 40;
+ return jjMoveNfa_0(5, 0);
+ case 60:
+ jjmatchedKind = 32;
+ return jjMoveStringLiteralDfa1_0(0x220000000L);
+ case 61:
+ jjmatchedKind = 28;
+ return jjMoveNfa_0(5, 0);
+ case 62:
+ jjmatchedKind = 30;
+ return jjMoveStringLiteralDfa1_0(0x80000000L);
+ case 65:
+ return jjMoveStringLiteralDfa1_0(0x200L);
+ case 66:
+ return jjMoveStringLiteralDfa1_0(0x800L);
+ case 69:
+ return jjMoveStringLiteralDfa1_0(0x2000L);
+ case 70:
+ return jjMoveStringLiteralDfa1_0(0x20000L);
+ case 73:
+ return jjMoveStringLiteralDfa1_0(0xc000L);
+ case 76:
+ return jjMoveStringLiteralDfa1_0(0x1000L);
+ case 78:
+ return jjMoveStringLiteralDfa1_0(0x40100L);
+ case 79:
+ return jjMoveStringLiteralDfa1_0(0x400L);
+ case 84:
+ return jjMoveStringLiteralDfa1_0(0x10000L);
+ case 88:
+ return jjMoveStringLiteralDfa1_0(0x180000L);
+ case 97:
+ return jjMoveStringLiteralDfa1_0(0x200L);
+ case 98:
+ return jjMoveStringLiteralDfa1_0(0x800L);
+ case 101:
+ return jjMoveStringLiteralDfa1_0(0x2000L);
+ case 102:
+ return jjMoveStringLiteralDfa1_0(0x20000L);
+ case 105:
+ return jjMoveStringLiteralDfa1_0(0xc000L);
+ case 108:
+ return jjMoveStringLiteralDfa1_0(0x1000L);
+ case 110:
+ return jjMoveStringLiteralDfa1_0(0x40100L);
+ case 111:
+ return jjMoveStringLiteralDfa1_0(0x400L);
+ case 116:
+ return jjMoveStringLiteralDfa1_0(0x10000L);
+ case 120:
+ return jjMoveStringLiteralDfa1_0(0x180000L);
+ default:
+ return jjMoveNfa_0(5, 0);
+ }
+ }
+
+ private int jjMoveStringLiteralDfa1_0(long active0) {
+ try {
+ curChar = input_stream.readChar();
+ } catch (IOException e) {
+ return jjMoveNfa_0(5, 0);
+ }
+ switch (curChar) {
+ case 61:
+ if ((active0 & 0x80000000L) != 0L) {
+ jjmatchedKind = 31;
+ jjmatchedPos = 1;
+ } else if ((active0 & 0x200000000L) != 0L) {
+ jjmatchedKind = 33;
+ jjmatchedPos = 1;
+ }
+ break;
+ case 62:
+ if ((active0 & 0x20000000L) != 0L) {
+ jjmatchedKind = 29;
+ jjmatchedPos = 1;
+ }
+ break;
+ case 65:
+ return jjMoveStringLiteralDfa2_0(active0, 0x20000L);
+ case 69:
+ return jjMoveStringLiteralDfa2_0(active0, 0x800L);
+ case 73:
+ return jjMoveStringLiteralDfa2_0(active0, 0x1000L);
+ case 78:
+ if ((active0 & 0x4000L) != 0L) {
+ jjmatchedKind = 14;
+ jjmatchedPos = 1;
+ }
+ return jjMoveStringLiteralDfa2_0(active0, 0x200L);
+ case 79:
+ return jjMoveStringLiteralDfa2_0(active0, 0x100L);
+ case 80:
+ return jjMoveStringLiteralDfa2_0(active0, 0x80000L);
+ case 81:
+ return jjMoveStringLiteralDfa2_0(active0, 0x100000L);
+ case 82:
+ if ((active0 & 0x400L) != 0L) {
+ jjmatchedKind = 10;
+ jjmatchedPos = 1;
+ }
+ return jjMoveStringLiteralDfa2_0(active0, 0x10000L);
+ case 83:
+ if ((active0 & 0x8000L) != 0L) {
+ jjmatchedKind = 15;
+ jjmatchedPos = 1;
+ }
+ return jjMoveStringLiteralDfa2_0(active0, 0x2000L);
+ case 85:
+ return jjMoveStringLiteralDfa2_0(active0, 0x40000L);
+ case 97:
+ return jjMoveStringLiteralDfa2_0(active0, 0x20000L);
+ case 101:
+ return jjMoveStringLiteralDfa2_0(active0, 0x800L);
+ case 105:
+ return jjMoveStringLiteralDfa2_0(active0, 0x1000L);
+ case 110:
+ if ((active0 & 0x4000L) != 0L) {
+ jjmatchedKind = 14;
+ jjmatchedPos = 1;
+ }
+ return jjMoveStringLiteralDfa2_0(active0, 0x200L);
+ case 111:
+ return jjMoveStringLiteralDfa2_0(active0, 0x100L);
+ case 112:
+ return jjMoveStringLiteralDfa2_0(active0, 0x80000L);
+ case 113:
+ return jjMoveStringLiteralDfa2_0(active0, 0x100000L);
+ case 114:
+ if ((active0 & 0x400L) != 0L) {
+ jjmatchedKind = 10;
+ jjmatchedPos = 1;
+ }
+ return jjMoveStringLiteralDfa2_0(active0, 0x10000L);
+ case 115:
+ if ((active0 & 0x8000L) != 0L) {
+ jjmatchedKind = 15;
+ jjmatchedPos = 1;
+ }
+ return jjMoveStringLiteralDfa2_0(active0, 0x2000L);
+ case 117:
+ return jjMoveStringLiteralDfa2_0(active0, 0x40000L);
+ default:
+ break;
+ }
+ return jjMoveNfa_0(5, 1);
+ }
+
+ private int jjMoveStringLiteralDfa2_0(long old0, long active0) {
+ if (((active0 &= old0)) == 0L) return jjMoveNfa_0(5, 1);
+ try {
+ curChar = input_stream.readChar();
+ } catch (IOException e) {
+ return jjMoveNfa_0(5, 1);
+ }
+ switch (curChar) {
+ case 65:
+ return jjMoveStringLiteralDfa3_0(active0, 0x80000L);
+ case 67:
+ return jjMoveStringLiteralDfa3_0(active0, 0x2000L);
+ case 68:
+ if ((active0 & 0x200L) != 0L) {
+ jjmatchedKind = 9;
+ jjmatchedPos = 2;
+ }
+ break;
+ case 75:
+ return jjMoveStringLiteralDfa3_0(active0, 0x1000L);
+ case 76:
+ return jjMoveStringLiteralDfa3_0(active0, 0x60000L);
+ case 84:
+ if ((active0 & 0x100L) != 0L) {
+ jjmatchedKind = 8;
+ jjmatchedPos = 2;
+ }
+ return jjMoveStringLiteralDfa3_0(active0, 0x800L);
+ case 85:
+ return jjMoveStringLiteralDfa3_0(active0, 0x110000L);
+ case 97:
+ return jjMoveStringLiteralDfa3_0(active0, 0x80000L);
+ case 99:
+ return jjMoveStringLiteralDfa3_0(active0, 0x2000L);
+ case 100:
+ if ((active0 & 0x200L) != 0L) {
+ jjmatchedKind = 9;
+ jjmatchedPos = 2;
+ }
+ break;
+ case 107:
+ return jjMoveStringLiteralDfa3_0(active0, 0x1000L);
+ case 108:
+ return jjMoveStringLiteralDfa3_0(active0, 0x60000L);
+ case 116:
+ if ((active0 & 0x100L) != 0L) {
+ jjmatchedKind = 8;
+ jjmatchedPos = 2;
+ }
+ return jjMoveStringLiteralDfa3_0(active0, 0x800L);
+ case 117:
+ return jjMoveStringLiteralDfa3_0(active0, 0x110000L);
+ default:
+ break;
+ }
+ return jjMoveNfa_0(5, 2);
+ }
+
+ private int jjMoveStringLiteralDfa3_0(long old0, long active0) {
+ if (((active0 &= old0)) == 0L) return jjMoveNfa_0(5, 2);
+ try {
+ curChar = input_stream.readChar();
+ } catch (IOException e) {
+ return jjMoveNfa_0(5, 2);
+ }
+ switch (curChar) {
+ case 65:
+ return jjMoveStringLiteralDfa4_0(active0, 0x2000L);
+ case 69:
+ if ((active0 & 0x1000L) != 0L) {
+ jjmatchedKind = 12;
+ jjmatchedPos = 3;
+ } else if ((active0 & 0x10000L) != 0L) {
+ jjmatchedKind = 16;
+ jjmatchedPos = 3;
+ }
+ return jjMoveStringLiteralDfa4_0(active0, 0x100000L);
+ case 76:
+ if ((active0 & 0x40000L) != 0L) {
+ jjmatchedKind = 18;
+ jjmatchedPos = 3;
+ }
+ break;
+ case 83:
+ return jjMoveStringLiteralDfa4_0(active0, 0x20000L);
+ case 84:
+ return jjMoveStringLiteralDfa4_0(active0, 0x80000L);
+ case 87:
+ return jjMoveStringLiteralDfa4_0(active0, 0x800L);
+ case 97:
+ return jjMoveStringLiteralDfa4_0(active0, 0x2000L);
+ case 101:
+ if ((active0 & 0x1000L) != 0L) {
+ jjmatchedKind = 12;
+ jjmatchedPos = 3;
+ } else if ((active0 & 0x10000L) != 0L) {
+ jjmatchedKind = 16;
+ jjmatchedPos = 3;
+ }
+ return jjMoveStringLiteralDfa4_0(active0, 0x100000L);
+ case 108:
+ if ((active0 & 0x40000L) != 0L) {
+ jjmatchedKind = 18;
+ jjmatchedPos = 3;
+ }
+ break;
+ case 115:
+ return jjMoveStringLiteralDfa4_0(active0, 0x20000L);
+ case 116:
+ return jjMoveStringLiteralDfa4_0(active0, 0x80000L);
+ case 119:
+ return jjMoveStringLiteralDfa4_0(active0, 0x800L);
+ default:
+ break;
+ }
+ return jjMoveNfa_0(5, 3);
+ }
+
+ private int jjMoveStringLiteralDfa4_0(long old0, long active0) {
+ if (((active0 &= old0)) == 0L) return jjMoveNfa_0(5, 3);
+ try {
+ curChar = input_stream.readChar();
+ } catch (IOException e) {
+ return jjMoveNfa_0(5, 3);
+ }
+ switch (curChar) {
+ case 69:
+ if ((active0 & 0x20000L) != 0L) {
+ jjmatchedKind = 17;
+ jjmatchedPos = 4;
+ }
+ return jjMoveStringLiteralDfa5_0(active0, 0x800L);
+ case 72:
+ if ((active0 & 0x80000L) != 0L) {
+ jjmatchedKind = 19;
+ jjmatchedPos = 4;
+ }
+ break;
+ case 80:
+ return jjMoveStringLiteralDfa5_0(active0, 0x2000L);
+ case 82:
+ return jjMoveStringLiteralDfa5_0(active0, 0x100000L);
+ case 101:
+ if ((active0 & 0x20000L) != 0L) {
+ jjmatchedKind = 17;
+ jjmatchedPos = 4;
+ }
+ return jjMoveStringLiteralDfa5_0(active0, 0x800L);
+ case 104:
+ if ((active0 & 0x80000L) != 0L) {
+ jjmatchedKind = 19;
+ jjmatchedPos = 4;
+ }
+ break;
+ case 112:
+ return jjMoveStringLiteralDfa5_0(active0, 0x2000L);
+ case 114:
+ return jjMoveStringLiteralDfa5_0(active0, 0x100000L);
+ default:
+ break;
+ }
+ return jjMoveNfa_0(5, 4);
+ }
+
+ private int jjMoveStringLiteralDfa5_0(long old0, long active0) {
+ if (((active0 &= old0)) == 0L) return jjMoveNfa_0(5, 4);
+ try {
+ curChar = input_stream.readChar();
+ } catch (IOException e) {
+ return jjMoveNfa_0(5, 4);
+ }
+ switch (curChar) {
+ case 69:
+ if ((active0 & 0x2000L) != 0L) {
+ jjmatchedKind = 13;
+ jjmatchedPos = 5;
+ }
+ return jjMoveStringLiteralDfa6_0(active0, 0x800L);
+ case 89:
+ if ((active0 & 0x100000L) != 0L) {
+ jjmatchedKind = 20;
+ jjmatchedPos = 5;
+ }
+ break;
+ case 101:
+ if ((active0 & 0x2000L) != 0L) {
+ jjmatchedKind = 13;
+ jjmatchedPos = 5;
+ }
+ return jjMoveStringLiteralDfa6_0(active0, 0x800L);
+ case 121:
+ if ((active0 & 0x100000L) != 0L) {
+ jjmatchedKind = 20;
+ jjmatchedPos = 5;
+ }
+ break;
+ default:
+ break;
+ }
+ return jjMoveNfa_0(5, 5);
+ }
+
+ private int jjMoveStringLiteralDfa6_0(long old0, long active0) {
+ if (((active0 &= old0)) == 0L) return jjMoveNfa_0(5, 5);
+ try {
+ curChar = input_stream.readChar();
+ } catch (IOException e) {
+ return jjMoveNfa_0(5, 5);
+ }
+ switch (curChar) {
+ case 78:
+ if ((active0 & 0x800L) != 0L) {
+ jjmatchedKind = 11;
+ jjmatchedPos = 6;
+ }
+ break;
+ case 110:
+ if ((active0 & 0x800L) != 0L) {
+ jjmatchedKind = 11;
+ jjmatchedPos = 6;
+ }
+ break;
+ default:
+ break;
+ }
+ return jjMoveNfa_0(5, 6);
+ }
+
+ static final long[] jjbitVec0 = {
+ 0xfffffffffffffffeL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL
+ };
+ static final long[] jjbitVec2 = {0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL};
+
+ private int jjMoveNfa_0(int startState, int curPos) {
+ int strKind = jjmatchedKind;
+ int strPos = jjmatchedPos;
+ int seenUpto;
+ input_stream.backup(seenUpto = curPos + 1);
+ try {
+ curChar = input_stream.readChar();
+ } catch (IOException e) {
+ throw new Error("Internal Error");
+ }
+ curPos = 0;
+ int startsAt = 0;
+ jjnewStateCnt = 43;
+ int i = 1;
+ jjstateSet[0] = startState;
+ int kind = 0x7fffffff;
+ for (; ; ) {
+ if (++jjround == 0x7fffffff) ReInitRounds();
+ if (curChar < 64) {
+ long l = 1L << curChar;
+ do {
+ switch (jjstateSet[--i]) {
+ case 5:
+ if ((0x3ff000000000000L & l) != 0L) {
+ jjCheckNAddStates(0, 3);
+ } else if (curChar == 36) {
+ if (kind > 27) kind = 27;
+ {
+ jjCheckNAdd(27);
+ }
+ } else if (curChar == 39) {
+ jjCheckNAddStates(4, 6);
+ } else if (curChar == 46) {
+ jjCheckNAdd(17);
+ } else if (curChar == 47) jjstateSet[jjnewStateCnt++] = 6;
+ else if (curChar == 45) jjstateSet[jjnewStateCnt++] = 0;
+ if ((0x3fe000000000000L & l) != 0L) {
+ if (kind > 21) kind = 21;
+ {
+ jjCheckNAddTwoStates(14, 15);
+ }
+ } else if (curChar == 48) {
+ if (kind > 23) kind = 23;
+ {
+ jjCheckNAddTwoStates(40, 42);
+ }
+ }
+ break;
+ case 0:
+ if (curChar == 45) {
+ jjCheckNAddStates(7, 9);
+ }
+ break;
+ case 1:
+ if ((0xffffffffffffdbffL & l) != 0L) {
+ jjCheckNAddStates(7, 9);
+ }
+ break;
+ case 2:
+ if ((0x2400L & l) != 0L && kind > 6) kind = 6;
+ break;
+ case 3:
+ if (curChar == 10 && kind > 6) kind = 6;
+ break;
+ case 4:
+ if (curChar == 13) jjstateSet[jjnewStateCnt++] = 3;
+ break;
+ case 6:
+ if (curChar == 42) {
+ jjCheckNAddTwoStates(7, 8);
+ }
+ break;
+ case 7:
+ if ((0xfffffbffffffffffL & l) != 0L) {
+ jjCheckNAddTwoStates(7, 8);
+ }
+ break;
+ case 8:
+ if (curChar == 42) {
+ jjCheckNAddStates(10, 12);
+ }
+ break;
+ case 9:
+ if ((0xffff7bffffffffffL & l) != 0L) {
+ jjCheckNAddTwoStates(10, 8);
+ }
+ break;
+ case 10:
+ if ((0xfffffbffffffffffL & l) != 0L) {
+ jjCheckNAddTwoStates(10, 8);
+ }
+ break;
+ case 11:
+ if (curChar == 47 && kind > 7) kind = 7;
+ break;
+ case 12:
+ if (curChar == 47) jjstateSet[jjnewStateCnt++] = 6;
+ break;
+ case 13:
+ if ((0x3fe000000000000L & l) == 0L) break;
+ if (kind > 21) kind = 21;
+ {
+ jjCheckNAddTwoStates(14, 15);
+ }
+ break;
+ case 14:
+ if ((0x3ff000000000000L & l) == 0L) break;
+ if (kind > 21) kind = 21;
+ {
+ jjCheckNAddTwoStates(14, 15);
+ }
+ break;
+ case 16:
+ if (curChar == 46) {
+ jjCheckNAdd(17);
+ }
+ break;
+ case 17:
+ if ((0x3ff000000000000L & l) == 0L) break;
+ if (kind > 24) kind = 24;
+ {
+ jjCheckNAddTwoStates(17, 18);
+ }
+ break;
+ case 19:
+ if ((0x280000000000L & l) != 0L) {
+ jjCheckNAdd(20);
+ }
+ break;
+ case 20:
+ if ((0x3ff000000000000L & l) == 0L) break;
+ if (kind > 24) kind = 24;
+ {
+ jjCheckNAdd(20);
+ }
+ break;
+ case 21:
+ case 22:
+ if (curChar == 39) {
+ jjCheckNAddStates(4, 6);
+ }
+ break;
+ case 23:
+ if (curChar == 39) jjstateSet[jjnewStateCnt++] = 22;
+ break;
+ case 24:
+ if ((0xffffff7fffffffffL & l) != 0L) {
+ jjCheckNAddStates(4, 6);
+ }
+ break;
+ case 25:
+ if (curChar == 39 && kind > 26) kind = 26;
+ break;
+ case 26:
+ if (curChar != 36) break;
+ if (kind > 27) kind = 27;
+ {
+ jjCheckNAdd(27);
+ }
+ break;
+ case 27:
+ if ((0x3ff001000000000L & l) == 0L) break;
+ if (kind > 27) kind = 27;
+ {
+ jjCheckNAdd(27);
+ }
+ break;
+ case 28:
+ if ((0x3ff000000000000L & l) != 0L) {
+ jjCheckNAddStates(0, 3);
+ }
+ break;
+ case 29:
+ if ((0x3ff000000000000L & l) != 0L) {
+ jjCheckNAddTwoStates(29, 30);
+ }
+ break;
+ case 30:
+ if (curChar != 46) break;
+ if (kind > 24) kind = 24;
+ {
+ jjCheckNAddTwoStates(31, 32);
+ }
+ break;
+ case 31:
+ if ((0x3ff000000000000L & l) == 0L) break;
+ if (kind > 24) kind = 24;
+ {
+ jjCheckNAddTwoStates(31, 32);
+ }
+ break;
+ case 33:
+ if ((0x280000000000L & l) != 0L) {
+ jjCheckNAdd(34);
+ }
+ break;
+ case 34:
+ if ((0x3ff000000000000L & l) == 0L) break;
+ if (kind > 24) kind = 24;
+ {
+ jjCheckNAdd(34);
+ }
+ break;
+ case 35:
+ if ((0x3ff000000000000L & l) != 0L) {
+ jjCheckNAddTwoStates(35, 36);
+ }
+ break;
+ case 37:
+ if ((0x280000000000L & l) != 0L) {
+ jjCheckNAdd(38);
+ }
+ break;
+ case 38:
+ if ((0x3ff000000000000L & l) == 0L) break;
+ if (kind > 24) kind = 24;
+ {
+ jjCheckNAdd(38);
+ }
+ break;
+ case 39:
+ if (curChar != 48) break;
+ if (kind > 23) kind = 23;
+ {
+ jjCheckNAddTwoStates(40, 42);
+ }
+ break;
+ case 41:
+ if ((0x3ff000000000000L & l) == 0L) break;
+ if (kind > 22) kind = 22;
+ jjstateSet[jjnewStateCnt++] = 41;
+ break;
+ case 42:
+ if ((0xff000000000000L & l) == 0L) break;
+ if (kind > 23) kind = 23;
+ {
+ jjCheckNAdd(42);
+ }
+ break;
+ default:
+ break;
+ }
+ } while (i != startsAt);
+ } else if (curChar < 128) {
+ long l = 1L << (curChar & 077);
+ do {
+ switch (jjstateSet[--i]) {
+ case 5:
+ case 27:
+ if ((0x7fffffe87fffffeL & l) == 0L) break;
+ if (kind > 27) kind = 27;
+ {
+ jjCheckNAdd(27);
+ }
+ break;
+ case 1:
+ {
+ jjAddStates(7, 9);
+ }
+ break;
+ case 7:
+ {
+ jjCheckNAddTwoStates(7, 8);
+ }
+ break;
+ case 9:
+ case 10:
+ {
+ jjCheckNAddTwoStates(10, 8);
+ }
+ break;
+ case 15:
+ if ((0x100000001000L & l) != 0L && kind > 21) kind = 21;
+ break;
+ case 18:
+ if ((0x2000000020L & l) != 0L) {
+ jjAddStates(13, 14);
+ }
+ break;
+ case 24:
+ {
+ jjAddStates(4, 6);
+ }
+ break;
+ case 32:
+ if ((0x2000000020L & l) != 0L) {
+ jjAddStates(15, 16);
+ }
+ break;
+ case 36:
+ if ((0x2000000020L & l) != 0L) {
+ jjAddStates(17, 18);
+ }
+ break;
+ case 40:
+ if ((0x100000001000000L & l) != 0L) {
+ jjCheckNAdd(41);
+ }
+ break;
+ case 41:
+ if ((0x7e0000007eL & l) == 0L) break;
+ if (kind > 22) kind = 22;
+ {
+ jjCheckNAdd(41);
+ }
+ break;
+ default:
+ break;
+ }
+ } while (i != startsAt);
+ } else {
+ int hiByte = (curChar >> 8);
+ int i1 = hiByte >> 6;
+ long l1 = 1L << (hiByte & 077);
+ int i2 = (curChar & 0xff) >> 6;
+ long l2 = 1L << (curChar & 077);
+ do {
+ switch (jjstateSet[--i]) {
+ case 1:
+ if (jjCanMove_0(hiByte, i1, i2, l1, l2)) {
+ jjAddStates(7, 9);
+ }
+ break;
+ case 7:
+ if (jjCanMove_0(hiByte, i1, i2, l1, l2)) {
+ jjCheckNAddTwoStates(7, 8);
+ }
+ break;
+ case 9:
+ case 10:
+ if (jjCanMove_0(hiByte, i1, i2, l1, l2)) {
+ jjCheckNAddTwoStates(10, 8);
+ }
+ break;
+ case 24:
+ if (jjCanMove_0(hiByte, i1, i2, l1, l2)) {
+ jjAddStates(4, 6);
+ }
+ break;
+ default:
+ if (i1 == 0 || l1 == 0 || i2 == 0 || l2 == 0) break;
+ else break;
+ }
+ } while (i != startsAt);
+ }
+ if (kind != 0x7fffffff) {
+ jjmatchedKind = kind;
+ jjmatchedPos = curPos;
+ kind = 0x7fffffff;
+ }
+ ++curPos;
+ if ((i = jjnewStateCnt) == (startsAt = 43 - (jjnewStateCnt = startsAt))) break;
+ try {
+ curChar = input_stream.readChar();
+ } catch (IOException e) {
+ break;
+ }
+ }
+ if (jjmatchedPos > strPos) return curPos;
+
+ int toRet = Math.max(curPos, seenUpto);
+
+ if (curPos < toRet)
+ for (i = toRet - Math.min(curPos, seenUpto); i-- > 0; )
+ try {
+ curChar = input_stream.readChar();
+ } catch (IOException e) {
+ throw new Error("Internal Error : Please send a bug report.");
+ }
+
+ if (jjmatchedPos < strPos) {
+ jjmatchedKind = strKind;
+ jjmatchedPos = strPos;
+ } else if (jjmatchedPos == strPos && jjmatchedKind > strKind) jjmatchedKind = strKind;
+
+ return toRet;
+ }
+
+ /** Token literal values. */
+ public static final String[] jjstrLiteralImages = {
+ "", null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null, null, null, "\75", "\74\76", "\76",
+ "\76\75", "\74", "\74\75", "\50", "\54", "\51", "\53", "\55", "\52", "\57", "\45",
+ };
+
+ protected Token jjFillToken() {
+ final Token t;
+ final String curTokenImage;
+ final int beginLine;
+ final int endLine;
+ final int beginColumn;
+ final int endColumn;
+ String im = jjstrLiteralImages[jjmatchedKind];
+ curTokenImage = (im == null) ? input_stream.GetImage() : im;
+ beginLine = input_stream.getBeginLine();
+ beginColumn = input_stream.getBeginColumn();
+ endLine = input_stream.getEndLine();
+ endColumn = input_stream.getEndColumn();
+ t = Token.newToken(jjmatchedKind, curTokenImage);
+
+ t.beginLine = beginLine;
+ t.endLine = endLine;
+ t.beginColumn = beginColumn;
+ t.endColumn = endColumn;
+
+ return t;
+ }
+
+ static final int[] jjnextStates = {
+ 29, 30, 35, 36, 23, 24, 25, 1, 2, 4, 8, 9, 11, 19, 20, 33, 34, 37, 38,
+ };
+
+ private static final boolean jjCanMove_0(int hiByte, int i1, int i2, long l1, long l2) {
+ switch (hiByte) {
+ case 0:
+ return ((jjbitVec2[i2] & l2) != 0L);
+ default:
+ if ((jjbitVec0[i1] & l1) != 0L) return true;
+ return false;
+ }
+ }
+
+ int curLexState = 0;
+ int defaultLexState = 0;
+ int jjnewStateCnt;
+ int jjround;
+ int jjmatchedPos;
+ int jjmatchedKind;
+
+ /** Get the next Token. */
+ public Token getNextToken() {
+ Token specialToken = null;
+ Token matchedToken;
+ int curPos = 0;
+
+ EOFLoop:
+ for (; ; ) {
+ try {
+ curChar = input_stream.BeginToken();
+ } catch (Exception e) {
+ jjmatchedKind = 0;
+ jjmatchedPos = -1;
+ matchedToken = jjFillToken();
+ matchedToken.specialToken = specialToken;
+ return matchedToken;
+ }
+
+ jjmatchedKind = 0x7fffffff;
+ jjmatchedPos = 0;
+ curPos = jjMoveStringLiteralDfa0_0();
+ if (jjmatchedKind != 0x7fffffff) {
+ if (jjmatchedPos + 1 < curPos) input_stream.backup(curPos - jjmatchedPos - 1);
+ if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) {
+ matchedToken = jjFillToken();
+ matchedToken.specialToken = specialToken;
+ return matchedToken;
+ } else {
+ if ((jjtoSpecial[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) {
+ matchedToken = jjFillToken();
+ if (specialToken == null) specialToken = matchedToken;
+ else {
+ matchedToken.specialToken = specialToken;
+ specialToken = (specialToken.next = matchedToken);
+ }
+ }
+ continue EOFLoop;
+ }
+ }
+ int error_line = input_stream.getEndLine();
+ int error_column = input_stream.getEndColumn();
+ String error_after = null;
+ boolean EOFSeen = false;
+ try {
+ input_stream.readChar();
+ input_stream.backup(1);
+ } catch (IOException e1) {
+ EOFSeen = true;
+ error_after = curPos <= 1 ? "" : input_stream.GetImage();
+ if (curChar == '\n' || curChar == '\r') {
+ error_line++;
+ error_column = 0;
+ } else error_column++;
+ }
+ if (!EOFSeen) {
+ input_stream.backup(1);
+ error_after = curPos <= 1 ? "" : input_stream.GetImage();
+ }
+ throw new TokenMgrError(
+ EOFSeen,
+ curLexState,
+ error_line,
+ error_column,
+ error_after,
+ curChar,
+ TokenMgrError.LEXICAL_ERROR);
+ }
+ }
+
+ void SkipLexicalActions(Token matchedToken) {
+ switch (jjmatchedKind) {
+ default:
+ break;
+ }
+ }
+
+ void MoreLexicalActions() {
+ jjimageLen += (lengthOfMatch = jjmatchedPos + 1);
+ switch (jjmatchedKind) {
+ default:
+ break;
+ }
+ }
+
+ void TokenLexicalActions(Token matchedToken) {
+ switch (jjmatchedKind) {
+ default:
+ break;
+ }
+ }
+
+ private void jjCheckNAdd(int state) {
+ if (jjrounds[state] != jjround) {
+ jjstateSet[jjnewStateCnt++] = state;
+ jjrounds[state] = jjround;
+ }
+ }
+
+ private void jjAddStates(int start, int end) {
+ do {
+ jjstateSet[jjnewStateCnt++] = jjnextStates[start];
+ } while (start++ != end);
+ }
+
+ private void jjCheckNAddTwoStates(int state1, int state2) {
+ jjCheckNAdd(state1);
+ jjCheckNAdd(state2);
+ }
+
+ private void jjCheckNAddStates(int start, int end) {
+ do {
+ jjCheckNAdd(jjnextStates[start]);
+ } while (start++ != end);
+ }
+
+ /** Constructor. */
+ public SelectorParserTokenManager(SimpleCharStream stream) {
+
+ if (SimpleCharStream.staticFlag)
+ throw new Error(
+ "ERROR: Cannot use a static CharStream class with a non-static lexical analyzer.");
+
+ input_stream = stream;
+ }
+
+ /** Constructor. */
+ public SelectorParserTokenManager(SimpleCharStream stream, int lexState) {
+ ReInit(stream);
+ SwitchTo(lexState);
+ }
+
+ /** Reinitialise parser. */
+ public void ReInit(SimpleCharStream stream) {
+
+ jjmatchedPos = jjnewStateCnt = 0;
+ curLexState = defaultLexState;
+ input_stream = stream;
+ ReInitRounds();
+ }
+
+ private void ReInitRounds() {
+ int i;
+ jjround = 0x80000001;
+ for (i = 43; i-- > 0; ) jjrounds[i] = 0x80000000;
+ }
+
+ /** Reinitialise parser. */
+ public void ReInit(SimpleCharStream stream, int lexState) {
+
+ ReInit(stream);
+ SwitchTo(lexState);
+ }
+
+ /** Switch to specified lex state. */
+ public void SwitchTo(int lexState) {
+ if (lexState >= 1 || lexState < 0)
+ throw new TokenMgrError(
+ "Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.",
+ TokenMgrError.INVALID_LEXICAL_STATE);
+ else curLexState = lexState;
+ }
+
+ /** Lexer state names. */
+ public static final String[] lexStateNames = {
+ "DEFAULT",
+ };
+
+ /** Lex State array. */
+ public static final int[] jjnewLexState = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ };
+
+ static final long[] jjtoToken = {
+ 0x3fffdffff01L,
+ };
+ static final long[] jjtoSkip = {
+ 0xfeL,
+ };
+ static final long[] jjtoSpecial = {
+ 0x3eL,
+ };
+ static final long[] jjtoMore = {
+ 0x0L,
+ };
+ protected SimpleCharStream input_stream;
+
+ private final int[] jjrounds = new int[43];
+ private final int[] jjstateSet = new int[2 * 43];
+ private final StringBuilder jjimage = new StringBuilder();
+ private StringBuilder image = jjimage;
+ private int jjimageLen;
+ private int lengthOfMatch;
+ protected int curChar;
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/selector/SimpleCharStream.java b/activemq-filters/src/main/java/org/apache/activemq/selector/SimpleCharStream.java
new file mode 100644
index 00000000..eaa23415
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/selector/SimpleCharStream.java
@@ -0,0 +1,445 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 7.0 */
+/* JavaCCOptions:STATIC=false,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
+/**
+ * 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.selector;
+
+/**
+ * An implementation of interface CharStream, where the stream is assumed to contain only ASCII
+ * characters (without unicode processing).
+ */
+public class SimpleCharStream {
+ /** Whether parser is static. */
+ public static final boolean staticFlag = false;
+
+ int bufsize;
+ int available;
+ int tokenBegin;
+ /** Position in buffer. */
+ public int bufpos = -1;
+
+ protected int bufline[];
+ protected int bufcolumn[];
+
+ protected int column = 0;
+ protected int line = 1;
+
+ protected boolean prevCharIsCR = false;
+ protected boolean prevCharIsLF = false;
+
+ protected java.io.Reader inputStream;
+
+ protected char[] buffer;
+ protected int maxNextCharInd = 0;
+ protected int inBuf = 0;
+ protected int tabSize = 1;
+ protected boolean trackLineColumn = true;
+
+ public void setTabSize(int i) {
+ tabSize = i;
+ }
+
+ public int getTabSize() {
+ return tabSize;
+ }
+
+ protected void ExpandBuff(boolean wrapAround) {
+ char[] newbuffer = new char[bufsize + 2048];
+ int newbufline[] = new int[bufsize + 2048];
+ int newbufcolumn[] = new int[bufsize + 2048];
+
+ try {
+ if (wrapAround) {
+ System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+ System.arraycopy(buffer, 0, newbuffer, bufsize - tokenBegin, bufpos);
+ buffer = newbuffer;
+
+ System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+ System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos);
+ bufline = newbufline;
+
+ System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+ System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos);
+ bufcolumn = newbufcolumn;
+
+ maxNextCharInd = (bufpos += (bufsize - tokenBegin));
+ } else {
+ System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+ buffer = newbuffer;
+
+ System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+ bufline = newbufline;
+
+ System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+ bufcolumn = newbufcolumn;
+
+ maxNextCharInd = (bufpos -= tokenBegin);
+ }
+ } catch (Throwable t) {
+ throw new Error(t.getMessage());
+ }
+
+ bufsize += 2048;
+ available = bufsize;
+ tokenBegin = 0;
+ }
+
+ protected void FillBuff() throws java.io.IOException {
+ if (maxNextCharInd == available) {
+ if (available == bufsize) {
+ if (tokenBegin > 2048) {
+ bufpos = maxNextCharInd = 0;
+ available = tokenBegin;
+ } else if (tokenBegin < 0) bufpos = maxNextCharInd = 0;
+ else ExpandBuff(false);
+ } else if (available > tokenBegin) available = bufsize;
+ else if ((tokenBegin - available) < 2048) ExpandBuff(true);
+ else available = tokenBegin;
+ }
+
+ int i;
+ try {
+ if ((i = inputStream.read(buffer, maxNextCharInd, available - maxNextCharInd)) == -1) {
+ inputStream.close();
+ throw new java.io.IOException();
+ } else maxNextCharInd += i;
+ return;
+ } catch (java.io.IOException e) {
+ --bufpos;
+ backup(0);
+ if (tokenBegin == -1) tokenBegin = bufpos;
+ throw e;
+ }
+ }
+
+ /** Start. */
+ public char BeginToken() throws java.io.IOException {
+ tokenBegin = -1;
+ char c = readChar();
+ tokenBegin = bufpos;
+
+ return c;
+ }
+
+ protected void UpdateLineColumn(char c) {
+ column++;
+
+ if (prevCharIsLF) {
+ prevCharIsLF = false;
+ line += (column = 1);
+ } else if (prevCharIsCR) {
+ prevCharIsCR = false;
+ if (c == '\n') {
+ prevCharIsLF = true;
+ } else line += (column = 1);
+ }
+
+ switch (c) {
+ case '\r':
+ prevCharIsCR = true;
+ break;
+ case '\n':
+ prevCharIsLF = true;
+ break;
+ case '\t':
+ column--;
+ column += (tabSize - (column % tabSize));
+ break;
+ default:
+ break;
+ }
+
+ bufline[bufpos] = line;
+ bufcolumn[bufpos] = column;
+ }
+
+ /** Read a character. */
+ public char readChar() throws java.io.IOException {
+ if (inBuf > 0) {
+ --inBuf;
+
+ if (++bufpos == bufsize) bufpos = 0;
+
+ return buffer[bufpos];
+ }
+
+ if (++bufpos >= maxNextCharInd) FillBuff();
+
+ char c = buffer[bufpos];
+
+ UpdateLineColumn(c);
+ return c;
+ }
+
+ /**
+ * @deprecated
+ * @see #getEndColumn
+ */
+ @Deprecated
+ public int getColumn() {
+ return bufcolumn[bufpos];
+ }
+
+ /**
+ * @deprecated
+ * @see #getEndLine
+ */
+ @Deprecated
+ public int getLine() {
+ return bufline[bufpos];
+ }
+
+ /** Get token end column number. */
+ public int getEndColumn() {
+ return bufcolumn[bufpos];
+ }
+
+ /** Get token end line number. */
+ public int getEndLine() {
+ return bufline[bufpos];
+ }
+
+ /** Get token beginning column number. */
+ public int getBeginColumn() {
+ return bufcolumn[tokenBegin];
+ }
+
+ /** Get token beginning line number. */
+ public int getBeginLine() {
+ return bufline[tokenBegin];
+ }
+
+ /** Backup a number of characters. */
+ public void backup(int amount) {
+
+ inBuf += amount;
+ if ((bufpos -= amount) < 0) bufpos += bufsize;
+ }
+
+ /** Constructor. */
+ public SimpleCharStream(java.io.Reader dstream, int startline, int startcolumn, int buffersize) {
+ inputStream = dstream;
+ line = startline;
+ column = startcolumn - 1;
+
+ available = bufsize = buffersize;
+ buffer = new char[buffersize];
+ bufline = new int[buffersize];
+ bufcolumn = new int[buffersize];
+ }
+
+ /** Constructor. */
+ public SimpleCharStream(java.io.Reader dstream, int startline, int startcolumn) {
+ this(dstream, startline, startcolumn, 4096);
+ }
+
+ /** Constructor. */
+ public SimpleCharStream(java.io.Reader dstream) {
+ this(dstream, 1, 1, 4096);
+ }
+
+ /** Reinitialise. */
+ public void ReInit(java.io.Reader dstream, int startline, int startcolumn, int buffersize) {
+ inputStream = dstream;
+ line = startline;
+ column = startcolumn - 1;
+
+ if (buffer == null || buffersize != buffer.length) {
+ available = bufsize = buffersize;
+ buffer = new char[buffersize];
+ bufline = new int[buffersize];
+ bufcolumn = new int[buffersize];
+ }
+ prevCharIsLF = prevCharIsCR = false;
+ tokenBegin = inBuf = maxNextCharInd = 0;
+ bufpos = -1;
+ }
+
+ /** Reinitialise. */
+ public void ReInit(java.io.Reader dstream, int startline, int startcolumn) {
+ ReInit(dstream, startline, startcolumn, 4096);
+ }
+
+ /** Reinitialise. */
+ public void ReInit(java.io.Reader dstream) {
+ ReInit(dstream, 1, 1, 4096);
+ }
+ /** Constructor. */
+ public SimpleCharStream(
+ java.io.InputStream dstream, String encoding, int startline, int startcolumn, int buffersize)
+ throws java.io.UnsupportedEncodingException {
+ this(
+ encoding == null
+ ? new java.io.InputStreamReader(dstream)
+ : new java.io.InputStreamReader(dstream, encoding),
+ startline,
+ startcolumn,
+ buffersize);
+ }
+
+ /** Constructor. */
+ public SimpleCharStream(
+ java.io.InputStream dstream, int startline, int startcolumn, int buffersize) {
+ this(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+ }
+
+ /** Constructor. */
+ public SimpleCharStream(
+ java.io.InputStream dstream, String encoding, int startline, int startcolumn)
+ throws java.io.UnsupportedEncodingException {
+ this(dstream, encoding, startline, startcolumn, 4096);
+ }
+
+ /** Constructor. */
+ public SimpleCharStream(java.io.InputStream dstream, int startline, int startcolumn) {
+ this(dstream, startline, startcolumn, 4096);
+ }
+
+ /** Constructor. */
+ public SimpleCharStream(java.io.InputStream dstream, String encoding)
+ throws java.io.UnsupportedEncodingException {
+ this(dstream, encoding, 1, 1, 4096);
+ }
+
+ /** Constructor. */
+ public SimpleCharStream(java.io.InputStream dstream) {
+ this(dstream, 1, 1, 4096);
+ }
+
+ /** Reinitialise. */
+ public void ReInit(
+ java.io.InputStream dstream, String encoding, int startline, int startcolumn, int buffersize)
+ throws java.io.UnsupportedEncodingException {
+ ReInit(
+ encoding == null
+ ? new java.io.InputStreamReader(dstream)
+ : new java.io.InputStreamReader(dstream, encoding),
+ startline,
+ startcolumn,
+ buffersize);
+ }
+
+ /** Reinitialise. */
+ public void ReInit(java.io.InputStream dstream, int startline, int startcolumn, int buffersize) {
+ ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+ }
+
+ /** Reinitialise. */
+ public void ReInit(java.io.InputStream dstream, String encoding)
+ throws java.io.UnsupportedEncodingException {
+ ReInit(dstream, encoding, 1, 1, 4096);
+ }
+
+ /** Reinitialise. */
+ public void ReInit(java.io.InputStream dstream) {
+ ReInit(dstream, 1, 1, 4096);
+ }
+ /** Reinitialise. */
+ public void ReInit(java.io.InputStream dstream, String encoding, int startline, int startcolumn)
+ throws java.io.UnsupportedEncodingException {
+ ReInit(dstream, encoding, startline, startcolumn, 4096);
+ }
+ /** Reinitialise. */
+ public void ReInit(java.io.InputStream dstream, int startline, int startcolumn) {
+ ReInit(dstream, startline, startcolumn, 4096);
+ }
+ /** Get token literal value. */
+ public String GetImage() {
+ if (bufpos >= tokenBegin) return new String(buffer, tokenBegin, bufpos - tokenBegin + 1);
+ else
+ return new String(buffer, tokenBegin, bufsize - tokenBegin)
+ + new String(buffer, 0, bufpos + 1);
+ }
+
+ /** Get the suffix. */
+ public char[] GetSuffix(int len) {
+ char[] ret = new char[len];
+
+ if ((bufpos + 1) >= len) System.arraycopy(buffer, bufpos - len + 1, ret, 0, len);
+ else {
+ System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0, len - bufpos - 1);
+ System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1);
+ }
+
+ return ret;
+ }
+
+ /** Reset buffer when finished. */
+ public void Done() {
+ buffer = null;
+ bufline = null;
+ bufcolumn = null;
+ }
+
+ /** Method to adjust line and column numbers for the start of a token. */
+ public void adjustBeginLineColumn(int newLine, int newCol) {
+ int start = tokenBegin;
+ int len;
+
+ if (bufpos >= tokenBegin) {
+ len = bufpos - tokenBegin + inBuf + 1;
+ } else {
+ len = bufsize - tokenBegin + bufpos + 1 + inBuf;
+ }
+
+ int i = 0, j = 0, k = 0;
+ int nextColDiff = 0, columnDiff = 0;
+
+ while (i < len && bufline[j = start % bufsize] == bufline[k = ++start % bufsize]) {
+ bufline[j] = newLine;
+ nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j];
+ bufcolumn[j] = newCol + columnDiff;
+ columnDiff = nextColDiff;
+ i++;
+ }
+
+ if (i < len) {
+ bufline[j] = newLine++;
+ bufcolumn[j] = newCol + columnDiff;
+
+ while (i++ < len) {
+ if (bufline[j = start % bufsize] != bufline[++start % bufsize]) bufline[j] = newLine++;
+ else bufline[j] = newLine;
+ }
+ }
+
+ line = bufline[j];
+ column = bufcolumn[j];
+ }
+
+ boolean getTrackLineColumn() {
+ return trackLineColumn;
+ }
+
+ void setTrackLineColumn(boolean tlc) {
+ trackLineColumn = tlc;
+ }
+}
+/* JavaCC - OriginalChecksum=0304f11a6c7e1b1b9a02d20df5663b19 (do not edit this line) */
diff --git a/activemq-filters/src/main/java/org/apache/activemq/selector/Token.java b/activemq-filters/src/main/java/org/apache/activemq/selector/Token.java
new file mode 100644
index 00000000..05a41e60
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/selector/Token.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* Generated By:JavaCC: Do not edit this line. Token.java Version 7.0 */
+/* JavaCCOptions:TOKEN_EXTENDS=,KEEP_LINE_COLUMN=true,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
+/**
+ * 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.selector;
+
+/** Describes the input token stream. */
+public class Token implements java.io.Serializable {
+
+ /**
+ * The version identifier for this Serializable class. Increment only if the serialized
+ * form of the class changes.
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * An integer that describes the kind of this token. This numbering system is determined by
+ * JavaCCParser, and a table of these numbers is stored in the file ...Constants.java.
+ */
+ public int kind;
+
+ /** The line number of the first character of this Token. */
+ public int beginLine;
+ /** The column number of the first character of this Token. */
+ public int beginColumn;
+ /** The line number of the last character of this Token. */
+ public int endLine;
+ /** The column number of the last character of this Token. */
+ public int endColumn;
+
+ /** The string image of the token. */
+ public String image;
+
+ /**
+ * A reference to the next regular (non-special) token from the input stream. If this is the last
+ * token from the input stream, or if the token manager has not read tokens beyond this one, this
+ * field is set to null. This is true only if this token is also a regular token. Otherwise, see
+ * below for a description of the contents of this field.
+ */
+ public Token next;
+
+ /**
+ * This field is used to access special tokens that occur prior to this token, but after the
+ * immediately preceding regular (non-special) token. If there are no such special tokens, this
+ * field is set to null. When there are more than one such special token, this field refers to the
+ * last of these special tokens, which in turn refers to the next previous special token through
+ * its specialToken field, and so on until the first special token (whose specialToken field is
+ * null). The next fields of special tokens refer to other special tokens that immediately follow
+ * it (without an intervening regular token). If there is no such token, this field is null.
+ */
+ public Token specialToken;
+
+ /**
+ * An optional attribute value of the Token. Tokens which are not used as syntactic sugar will
+ * often contain meaningful values that will be used later on by the compiler or interpreter. This
+ * attribute value is often different from the image. Any subclass of Token that actually wants to
+ * return a non-null value can override this method as appropriate.
+ */
+ public Object getValue() {
+ return null;
+ }
+
+ /** No-argument constructor */
+ public Token() {}
+
+ /** Constructs a new token for the specified Image. */
+ public Token(int kind) {
+ this(kind, null);
+ }
+
+ /** Constructs a new token for the specified Image and Kind. */
+ public Token(int kind, String image) {
+ this.kind = kind;
+ this.image = image;
+ }
+
+ /** Returns the image. */
+ @Override
+ public String toString() {
+ return image;
+ }
+
+ /**
+ * Returns a new Token object, by default. However, if you want, you can create and return
+ * subclass objects based on the value of ofKind. Simply add the cases to the switch for all those
+ * special cases. For example, if you have a subclass of Token called IDToken that you want to
+ * create if ofKind is ID, simply add something like :
+ *
+ *
case MyParserConstants.ID : return new IDToken(ofKind, image);
+ *
+ *
to the following switch statement. Then you can cast matchedToken variable to the
+ * appropriate type and use sit in your lexical actions.
+ */
+ public static Token newToken(int ofKind, String image) {
+ switch (ofKind) {
+ default:
+ return new Token(ofKind, image);
+ }
+ }
+
+ public static Token newToken(int ofKind) {
+ return newToken(ofKind, null);
+ }
+}
+/* JavaCC - OriginalChecksum=25fb2af6d2ed103b192283241e7c16dc (do not edit this line) */
diff --git a/activemq-filters/src/main/java/org/apache/activemq/selector/TokenMgrError.java b/activemq-filters/src/main/java/org/apache/activemq/selector/TokenMgrError.java
new file mode 100644
index 00000000..3777e01e
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/selector/TokenMgrError.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 7.0 */
+/* JavaCCOptions: */
+/**
+ * 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.selector;
+
+/** Token Manager Error. */
+public class TokenMgrError extends Error {
+
+ /**
+ * The version identifier for this Serializable class. Increment only if the serialized
+ * form of the class changes.
+ */
+ private static final long serialVersionUID = 1L;
+
+ /*
+ * Ordinals for various reasons why an Error of this type can be thrown.
+ */
+
+ /** Lexical error occurred. */
+ public static final int LEXICAL_ERROR = 0;
+
+ /** An attempt was made to create a second instance of a static token manager. */
+ public static final int STATIC_LEXER_ERROR = 1;
+
+ /** Tried to change to an invalid lexical state. */
+ public static final int INVALID_LEXICAL_STATE = 2;
+
+ /** Detected (and bailed out of) an infinite loop in the token manager. */
+ public static final int LOOP_DETECTED = 3;
+
+ /** Indicates the reason why the exception is thrown. It will have one of the above 4 values. */
+ int errorCode;
+
+ /**
+ * Replaces unprintable characters by their escaped (or unicode escaped) equivalents in the given
+ * string
+ */
+ protected static final String addEscapes(String str) {
+ StringBuilder retval = new StringBuilder();
+ char ch;
+ for (int i = 0; i < str.length(); i++) {
+ switch (str.charAt(i)) {
+ case '\b':
+ retval.append("\\b");
+ continue;
+ case '\t':
+ retval.append("\\t");
+ continue;
+ case '\n':
+ retval.append("\\n");
+ continue;
+ case '\f':
+ retval.append("\\f");
+ continue;
+ case '\r':
+ retval.append("\\r");
+ continue;
+ case '\"':
+ retval.append("\\\"");
+ continue;
+ case '\'':
+ retval.append("\\\'");
+ continue;
+ case '\\':
+ retval.append("\\\\");
+ continue;
+ default:
+ if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+ String s = "0000" + Integer.toString(ch, 16);
+ retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+ } else {
+ retval.append(ch);
+ }
+ continue;
+ }
+ }
+ return retval.toString();
+ }
+
+ /**
+ * Returns a detailed message for the Error when it is thrown by the token manager to indicate a
+ * lexical error. Parameters : EOFSeen : indicates if EOF caused the lexical error lexState :
+ * lexical state in which this error occurred errorLine : line number when the error occurred
+ * errorColumn : column number when the error occurred errorAfter : prefix that was seen before
+ * this error occurred curchar : the offending character Note: You can customize the lexical error
+ * message by modifying this method.
+ */
+ protected static String LexicalErr(
+ boolean EOFSeen,
+ int lexState,
+ int errorLine,
+ int errorColumn,
+ String errorAfter,
+ int curChar) {
+ return ("Lexical error at line "
+ + //
+ errorLine
+ + ", column "
+ + //
+ errorColumn
+ + ". Encountered: "
+ + //
+ (EOFSeen
+ ? ""
+ : ("'" + addEscapes(String.valueOf(curChar)) + "' (" + curChar + "),"))
+ + //
+ (errorAfter == null || errorAfter.length() == 0
+ ? ""
+ : " after prefix \"" + addEscapes(errorAfter) + "\""))
+ + //
+ (lexState == 0 ? "" : " (in lexical state " + lexState + ")");
+ }
+
+ /**
+ * You can also modify the body of this method to customize your error messages. For example,
+ * cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not of end-users concern, so you can
+ * return something like :
+ *
+ * "Internal Error : Please file a bug report .... "
+ *
+ *
from this method for such cases in the release version of your parser.
+ */
+ @Override
+ public String getMessage() {
+ return super.getMessage();
+ }
+
+ /*
+ * Constructors of various flavors follow.
+ */
+
+ /** No arg constructor. */
+ public TokenMgrError() {}
+
+ /** Constructor with message and reason. */
+ public TokenMgrError(String message, int reason) {
+ super(message);
+ errorCode = reason;
+ }
+
+ /** Full Constructor. */
+ public TokenMgrError(
+ boolean EOFSeen,
+ int lexState,
+ int errorLine,
+ int errorColumn,
+ String errorAfter,
+ int curChar,
+ int reason) {
+ this(LexicalErr(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason);
+ }
+}
+/* JavaCC - OriginalChecksum=4ab41af2e9c16c6a3783eac3da0c0e1a (do not edit this line) */
diff --git a/activemq-filters/src/main/java/org/apache/activemq/usage/DefaultUsageCapacity.java b/activemq-filters/src/main/java/org/apache/activemq/usage/DefaultUsageCapacity.java
new file mode 100644
index 00000000..1daa654f
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/usage/DefaultUsageCapacity.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.usage;
+
+/** Identify if a limit has been reached */
+public class DefaultUsageCapacity implements UsageCapacity {
+
+ private long limit;
+
+ /**
+ * @param size
+ * @return true if the limit is reached
+ * @see UsageCapacity#isLimit(long)
+ */
+ public boolean isLimit(long size) {
+ return size >= limit;
+ }
+
+ /** @return the limit */
+ public final long getLimit() {
+ return this.limit;
+ }
+
+ /** @param limit the limit to set */
+ public final void setLimit(long limit) {
+ this.limit = limit;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/usage/MemoryUsage.java b/activemq-filters/src/main/java/org/apache/activemq/usage/MemoryUsage.java
new file mode 100644
index 00000000..739651c5
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/usage/MemoryUsage.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.usage;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Used to keep track of how much of something is being used so that a productive working set usage
+ * can be controlled. Main use case is manage memory usage.
+ */
+public class MemoryUsage extends Usage {
+
+ private long usage;
+
+ public MemoryUsage() {
+ this(null, null);
+ }
+
+ /**
+ * Create the memory manager linked to a parent. When the memory manager is linked to a parent
+ * then when usage increased or decreased, the parent's usage is also increased or decreased.
+ *
+ * @param parent
+ */
+ public MemoryUsage(MemoryUsage parent) {
+ this(parent, "default");
+ }
+
+ public MemoryUsage(String name) {
+ this(null, name);
+ }
+
+ public MemoryUsage(MemoryUsage parent, String name) {
+ this(parent, name, 1.0f);
+ }
+
+ public MemoryUsage(MemoryUsage parent, String name, float portion) {
+ super(parent, name, portion);
+ }
+
+ /** @throws InterruptedException */
+ @Override
+ public void waitForSpace() throws InterruptedException {
+ if (parent != null) {
+ parent.waitForSpace();
+ }
+ usageLock.readLock().lock();
+ try {
+ if (percentUsage >= 100 && isStarted()) {
+ usageLock.readLock().unlock();
+ usageLock.writeLock().lock();
+ try {
+ while (percentUsage >= 100 && isStarted()) {
+ waitForSpaceCondition.await();
+ }
+ } finally {
+ usageLock.writeLock().unlock();
+ usageLock.readLock().lock();
+ }
+ }
+
+ if (percentUsage >= 100 && !isStarted()) {
+ throw new InterruptedException("waitForSpace stopped during wait.");
+ }
+ } finally {
+ usageLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * @param timeout
+ * @throws InterruptedException
+ * @return true if space
+ */
+ @Override
+ public boolean waitForSpace(final long timeout) throws InterruptedException {
+ if (parent != null) {
+ if (!parent.waitForSpace(timeout)) {
+ return false;
+ }
+ }
+ usageLock.readLock().lock();
+ try {
+ if (percentUsage >= 100) {
+ usageLock.readLock().unlock();
+ usageLock.writeLock().lock();
+ try {
+ final long deadline = timeout > 0 ? System.currentTimeMillis() + timeout : Long.MAX_VALUE;
+ long timeleft = deadline;
+ while (percentUsage >= 100 && timeleft > 0) {
+ waitForSpaceCondition.await(
+ Math.min(getPollingTime(), timeleft), TimeUnit.MILLISECONDS);
+ timeleft = deadline - System.currentTimeMillis();
+ }
+ } finally {
+ usageLock.writeLock().unlock();
+ usageLock.readLock().lock();
+ }
+ }
+
+ return percentUsage < 100;
+ } finally {
+ usageLock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public boolean isFull() {
+ if (parent != null && parent.isFull()) {
+ return true;
+ }
+ usageLock.readLock().lock();
+ try {
+ return percentUsage >= 100;
+ } finally {
+ usageLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Tries to increase the usage by value amount but blocks if this object is currently full.
+ *
+ * @param value
+ * @throws InterruptedException
+ */
+ public void enqueueUsage(long value) throws InterruptedException {
+ waitForSpace();
+ increaseUsage(value);
+ }
+
+ /**
+ * Increases the usage by the value amount.
+ *
+ * @param value
+ */
+ public void increaseUsage(long value) {
+ if (value == 0) {
+ return;
+ }
+
+ usageLock.writeLock().lock();
+ try {
+ usage += value;
+ setPercentUsage(caclPercentUsage());
+ } finally {
+ usageLock.writeLock().unlock();
+ }
+
+ if (parent != null) {
+ parent.increaseUsage(value);
+ }
+ }
+
+ /**
+ * Decreases the usage by the value amount.
+ *
+ * @param value
+ */
+ public void decreaseUsage(long value) {
+ if (value == 0) {
+ return;
+ }
+
+ usageLock.writeLock().lock();
+ try {
+ usage -= value;
+ setPercentUsage(caclPercentUsage());
+ } finally {
+ usageLock.writeLock().unlock();
+ }
+
+ if (parent != null) {
+ parent.decreaseUsage(value);
+ }
+ }
+
+ @Override
+ protected long retrieveUsage() {
+ return usage;
+ }
+
+ @Override
+ public long getUsage() {
+ return usage;
+ }
+
+ public void setUsage(long usage) {
+ this.usage = usage;
+ }
+
+ public void setPercentOfJvmHeap(int percentOfJvmHeap) {
+ if (percentOfJvmHeap > 0) {
+ setLimit(Math.round(Runtime.getRuntime().maxMemory() * percentOfJvmHeap / 100.0));
+ }
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/usage/Usage.java b/activemq-filters/src/main/java/org/apache/activemq/usage/Usage.java
new file mode 100644
index 00000000..2fc6ebc5
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/usage/Usage.java
@@ -0,0 +1,482 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.usage;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import org.apache.activemq.Service;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Used to keep track of how much of something is being used so that a productive working set usage
+ * can be controlled. Main use case is manage memory usage.
+ */
+public abstract class Usage implements Service {
+
+ private static final Logger LOG = LoggerFactory.getLogger(Usage.class);
+
+ protected final ReentrantReadWriteLock usageLock = new ReentrantReadWriteLock();
+ protected final Condition waitForSpaceCondition = usageLock.writeLock().newCondition();
+ protected int percentUsage;
+ protected T parent;
+ protected String name;
+
+ private UsageCapacity limiter = new DefaultUsageCapacity();
+ private int percentUsageMinDelta = 1;
+ private final List listeners = new CopyOnWriteArrayList();
+ private final boolean debug = LOG.isDebugEnabled();
+ private float usagePortion = 1.0f;
+ private final List children = new CopyOnWriteArrayList();
+ private final List callbacks = new LinkedList();
+ private int pollingTime = 100;
+ private final AtomicBoolean started = new AtomicBoolean();
+ private ThreadPoolExecutor executor;
+
+ public Usage(T parent, String name, float portion) {
+ this.parent = parent;
+ this.usagePortion = portion;
+ if (parent != null) {
+ this.limiter.setLimit((long) (parent.getLimit() * (double) portion));
+ name = parent.name + ":" + name;
+ }
+ this.name = name;
+ }
+
+ protected abstract long retrieveUsage();
+
+ /** @throws InterruptedException */
+ public void waitForSpace() throws InterruptedException {
+ waitForSpace(0);
+ }
+
+ public boolean waitForSpace(long timeout) throws InterruptedException {
+ return waitForSpace(timeout, 100);
+ }
+
+ /**
+ * @param timeout
+ * @throws InterruptedException
+ * @return true if space
+ */
+ public boolean waitForSpace(long timeout, int highWaterMark) throws InterruptedException {
+ if (parent != null) {
+ if (!parent.waitForSpace(timeout, highWaterMark)) {
+ return false;
+ }
+ }
+ usageLock.writeLock().lock();
+ try {
+ percentUsage = caclPercentUsage();
+ if (percentUsage >= highWaterMark) {
+ long deadline = timeout > 0 ? System.currentTimeMillis() + timeout : Long.MAX_VALUE;
+ long timeleft = deadline;
+ while (timeleft > 0) {
+ percentUsage = caclPercentUsage();
+ if (percentUsage >= highWaterMark) {
+ waitForSpaceCondition.await(pollingTime, TimeUnit.MILLISECONDS);
+ timeleft = deadline - System.currentTimeMillis();
+ } else {
+ break;
+ }
+ }
+ }
+ return percentUsage < highWaterMark;
+ } finally {
+ usageLock.writeLock().unlock();
+ }
+ }
+
+ public boolean isFull() {
+ return isFull(100);
+ }
+
+ public boolean isFull(int highWaterMark) {
+ if (parent != null && parent.isFull(highWaterMark)) {
+ return true;
+ }
+ usageLock.writeLock().lock();
+ try {
+ percentUsage = caclPercentUsage();
+ return percentUsage >= highWaterMark;
+ } finally {
+ usageLock.writeLock().unlock();
+ }
+ }
+
+ public void addUsageListener(UsageListener listener) {
+ listeners.add(listener);
+ }
+
+ public void removeUsageListener(UsageListener listener) {
+ listeners.remove(listener);
+ }
+
+ public int getNumUsageListeners() {
+ return listeners.size();
+ }
+
+ public long getLimit() {
+ usageLock.readLock().lock();
+ try {
+ return limiter.getLimit();
+ } finally {
+ usageLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Sets the memory limit in bytes. Setting the limit in bytes will set the usagePortion to 0 since
+ * the UsageManager is not going to be portion based off the parent. When set using Xbean, values
+ * of the form "20 Mb", "1024kb", and "1g" can be used
+ *
+ * @org.apache.xbean.Property propertyEditor="org.apache.activemq.util.MemoryPropertyEditor"
+ */
+ public void setLimit(long limit) {
+ if (percentUsageMinDelta < 0) {
+ throw new IllegalArgumentException("percentUsageMinDelta must be greater or equal to 0");
+ }
+ usageLock.writeLock().lock();
+ try {
+ this.limiter.setLimit(limit);
+ this.usagePortion = 0;
+ } finally {
+ usageLock.writeLock().unlock();
+ }
+ onLimitChange();
+ }
+
+ protected void onLimitChange() {
+ // We may need to calculate the limit
+ if (usagePortion > 0 && parent != null) {
+ usageLock.writeLock().lock();
+ try {
+ this.limiter.setLimit((long) (parent.getLimit() * (double) usagePortion));
+ } finally {
+ usageLock.writeLock().unlock();
+ }
+ }
+ // Reset the percent currently being used.
+ usageLock.writeLock().lock();
+ try {
+ setPercentUsage(caclPercentUsage());
+ } finally {
+ usageLock.writeLock().unlock();
+ }
+ // Let the children know that the limit has changed. They may need to
+ // set their limits based on ours.
+ for (T child : children) {
+ child.onLimitChange();
+ }
+ }
+
+ public float getUsagePortion() {
+ usageLock.readLock().lock();
+ try {
+ return usagePortion;
+ } finally {
+ usageLock.readLock().unlock();
+ }
+ }
+
+ public void setUsagePortion(float usagePortion) {
+ usageLock.writeLock().lock();
+ try {
+ this.usagePortion = usagePortion;
+ } finally {
+ usageLock.writeLock().unlock();
+ }
+ onLimitChange();
+ }
+
+ public int getPercentUsage() {
+ usageLock.readLock().lock();
+ try {
+ return percentUsage;
+ } finally {
+ usageLock.readLock().unlock();
+ }
+ }
+
+ public int getPercentUsageMinDelta() {
+ usageLock.readLock().lock();
+ try {
+ return percentUsageMinDelta;
+ } finally {
+ usageLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Sets the minimum number of percentage points the usage has to change before a UsageListener
+ * event is fired by the manager.
+ *
+ * @param percentUsageMinDelta
+ * @org.apache.xbean.Property propertyEditor="org.apache.activemq.util.MemoryPropertyEditor"
+ */
+ public void setPercentUsageMinDelta(int percentUsageMinDelta) {
+ if (percentUsageMinDelta < 1) {
+ throw new IllegalArgumentException("percentUsageMinDelta must be greater than 0");
+ }
+
+ usageLock.writeLock().lock();
+ try {
+ this.percentUsageMinDelta = percentUsageMinDelta;
+ setPercentUsage(caclPercentUsage());
+ } finally {
+ usageLock.writeLock().unlock();
+ }
+ }
+
+ public long getUsage() {
+ usageLock.readLock().lock();
+ try {
+ return retrieveUsage();
+ } finally {
+ usageLock.readLock().unlock();
+ }
+ }
+
+ protected void setPercentUsage(int value) {
+ usageLock.writeLock().lock();
+ try {
+ int oldValue = percentUsage;
+ percentUsage = value;
+ if (oldValue != value) {
+ fireEvent(oldValue, value);
+ }
+ } finally {
+ usageLock.writeLock().unlock();
+ }
+ }
+
+ protected int caclPercentUsage() {
+ if (limiter.getLimit() == 0) {
+ return 0;
+ }
+ return (int)
+ ((((retrieveUsage() * 100) / limiter.getLimit()) / percentUsageMinDelta)
+ * percentUsageMinDelta);
+ }
+
+ // Must be called with the usage lock's writeLock held.
+ private void fireEvent(final int oldPercentUsage, final int newPercentUsage) {
+ if (debug) {
+ LOG.debug(
+ getName()
+ + ": usage change from: "
+ + oldPercentUsage
+ + "% of available memory, to: "
+ + newPercentUsage
+ + "% of available memory");
+ }
+ if (started.get()) {
+ // Switching from being full to not being full..
+ if (oldPercentUsage >= 100 && newPercentUsage < 100) {
+ waitForSpaceCondition.signalAll();
+ if (!callbacks.isEmpty()) {
+ for (Runnable callback : callbacks) {
+ getExecutor().execute(callback);
+ }
+ callbacks.clear();
+ }
+ }
+ if (!listeners.isEmpty()) {
+ // Let the listeners know on a separate thread
+ Runnable listenerNotifier =
+ new Runnable() {
+ @Override
+ public void run() {
+ for (UsageListener listener : listeners) {
+ listener.onUsageChanged(Usage.this, oldPercentUsage, newPercentUsage);
+ }
+ }
+ };
+ if (started.get()) {
+ getExecutor().execute(listenerNotifier);
+ } else {
+ LOG.warn("Not notifying memory usage change to listeners on shutdown");
+ }
+ }
+ }
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String toString() {
+ return "Usage("
+ + getName()
+ + ") percentUsage="
+ + percentUsage
+ + "%, usage="
+ + retrieveUsage()
+ + ", limit="
+ + limiter.getLimit()
+ + ", percentUsageMinDelta="
+ + percentUsageMinDelta
+ + "%"
+ + (parent != null ? ";Parent:" + parent.toString() : "");
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void start() {
+ if (started.compareAndSet(false, true)) {
+ if (parent != null) {
+ parent.addChild(this);
+ if (getLimit() > parent.getLimit()) {
+ LOG.info(
+ "Usage({}) limit={} should be smaller than its parent limit={}",
+ new Object[] {getName(), getLimit(), parent.getLimit()});
+ }
+ }
+ for (T t : children) {
+ t.start();
+ }
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void stop() {
+ if (started.compareAndSet(true, false)) {
+ if (parent != null) {
+ parent.removeChild(this);
+ }
+
+ // clear down any callbacks
+ usageLock.writeLock().lock();
+ try {
+ waitForSpaceCondition.signalAll();
+ for (Runnable callback : this.callbacks) {
+ callback.run();
+ }
+ this.callbacks.clear();
+ } finally {
+ usageLock.writeLock().unlock();
+ }
+
+ for (T t : children) {
+ t.stop();
+ }
+ }
+ }
+
+ protected void addChild(T child) {
+ children.add(child);
+ if (started.get()) {
+ child.start();
+ }
+ }
+
+ protected void removeChild(T child) {
+ children.remove(child);
+ }
+
+ /**
+ * @param callback
+ * @return true if the UsageManager was full. The callback will only be called if this method
+ * returns true.
+ */
+ public boolean notifyCallbackWhenNotFull(final Runnable callback) {
+ if (parent != null) {
+ Runnable r =
+ new Runnable() {
+
+ @Override
+ public void run() {
+ usageLock.writeLock().lock();
+ try {
+ if (percentUsage >= 100) {
+ callbacks.add(callback);
+ } else {
+ callback.run();
+ }
+ } finally {
+ usageLock.writeLock().unlock();
+ }
+ }
+ };
+ if (parent.notifyCallbackWhenNotFull(r)) {
+ return true;
+ }
+ }
+ usageLock.writeLock().lock();
+ try {
+ if (percentUsage >= 100) {
+ callbacks.add(callback);
+ return true;
+ } else {
+ return false;
+ }
+ } finally {
+ usageLock.writeLock().unlock();
+ }
+ }
+
+ /** @return the limiter */
+ public UsageCapacity getLimiter() {
+ return this.limiter;
+ }
+
+ /** @param limiter the limiter to set */
+ public void setLimiter(UsageCapacity limiter) {
+ this.limiter = limiter;
+ }
+
+ /** @return the pollingTime */
+ public int getPollingTime() {
+ return this.pollingTime;
+ }
+
+ /** @param pollingTime the pollingTime to set */
+ public void setPollingTime(int pollingTime) {
+ this.pollingTime = pollingTime;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public T getParent() {
+ return parent;
+ }
+
+ public void setParent(T parent) {
+ this.parent = parent;
+ }
+
+ public void setExecutor(ThreadPoolExecutor executor) {
+ this.executor = executor;
+ }
+
+ public ThreadPoolExecutor getExecutor() {
+ return executor;
+ }
+
+ public boolean isStarted() {
+ return started.get();
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/usage/UsageCapacity.java b/activemq-filters/src/main/java/org/apache/activemq/usage/UsageCapacity.java
new file mode 100644
index 00000000..b74530bb
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/usage/UsageCapacity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.usage;
+
+/** Identify if a limit has been reached */
+public interface UsageCapacity {
+
+ /**
+ * Has the limit been reached ?
+ *
+ * @param size
+ * @return true if it has
+ */
+ boolean isLimit(long size);
+
+ /** @return the limit */
+ long getLimit();
+
+ /** @param limit the limit to set */
+ void setLimit(long limit);
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/usage/UsageListener.java b/activemq-filters/src/main/java/org/apache/activemq/usage/UsageListener.java
new file mode 100644
index 00000000..a3e82c39
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/usage/UsageListener.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.usage;
+
+public interface UsageListener {
+ void onUsageChanged(Usage usage, int oldPercentUsage, int newPercentUsage);
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/util/ByteArrayInputStream.java b/activemq-filters/src/main/java/org/apache/activemq/util/ByteArrayInputStream.java
new file mode 100644
index 00000000..bb504b81
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/util/ByteArrayInputStream.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/** Very similar to the java.io.ByteArrayInputStream but this version is not thread safe. */
+public class ByteArrayInputStream extends InputStream {
+
+ byte buffer[];
+ int limit;
+ int pos;
+ int mark;
+
+ public ByteArrayInputStream(byte data[]) {
+ this(data, 0, data.length);
+ }
+
+ public ByteArrayInputStream(ByteSequence sequence) {
+ this(sequence.getData(), sequence.getOffset(), sequence.getLength());
+ }
+
+ public ByteArrayInputStream(byte data[], int offset, int size) {
+ this.buffer = data;
+ this.mark = offset;
+ this.pos = offset;
+ this.limit = offset + size;
+ }
+
+ public int read() throws IOException {
+ if (pos < limit) {
+ return buffer[pos++] & 0xff;
+ } else {
+ return -1;
+ }
+ }
+
+ public int read(byte[] b) throws IOException {
+ return read(b, 0, b.length);
+ }
+
+ public int read(byte b[], int off, int len) {
+ if (pos < limit) {
+ len = Math.min(len, limit - pos);
+ if (len > 0) {
+ System.arraycopy(buffer, pos, b, off, len);
+ pos += len;
+ }
+ return len;
+ } else {
+ return -1;
+ }
+ }
+
+ public long skip(long len) throws IOException {
+ if (pos < limit) {
+ len = Math.min(len, limit - pos);
+ if (len > 0) {
+ pos += len;
+ }
+ return len;
+ } else {
+ return -1;
+ }
+ }
+
+ public int available() {
+ return limit - pos;
+ }
+
+ public boolean markSupported() {
+ return true;
+ }
+
+ public void mark(int markpos) {
+ mark = pos;
+ }
+
+ public void reset() {
+ pos = mark;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/util/ByteArrayOutputStream.java b/activemq-filters/src/main/java/org/apache/activemq/util/ByteArrayOutputStream.java
new file mode 100644
index 00000000..3dd8fe4c
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/util/ByteArrayOutputStream.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.util;
+
+import java.io.OutputStream;
+
+/**
+ * Very similar to the java.io.ByteArrayOutputStream but this version is not thread safe and the
+ * resulting data is returned in a ByteSequence to avoid an extra byte[] allocation.
+ */
+public class ByteArrayOutputStream extends OutputStream {
+
+ byte buffer[];
+ int size;
+
+ public ByteArrayOutputStream() {
+ this(1028);
+ }
+
+ public ByteArrayOutputStream(int capacity) {
+ buffer = new byte[capacity];
+ }
+
+ public void write(int b) {
+ int newsize = size + 1;
+ checkCapacity(newsize);
+ buffer[size] = (byte) b;
+ size = newsize;
+ }
+
+ public void write(byte b[], int off, int len) {
+ int newsize = size + len;
+ checkCapacity(newsize);
+ System.arraycopy(b, off, buffer, size, len);
+ size = newsize;
+ }
+
+ /**
+ * Ensures the the buffer has at least the minimumCapacity specified.
+ *
+ * @param minimumCapacity
+ */
+ private void checkCapacity(int minimumCapacity) {
+ if (minimumCapacity > buffer.length) {
+ byte b[] = new byte[Math.max(buffer.length << 1, minimumCapacity)];
+ System.arraycopy(buffer, 0, b, 0, size);
+ buffer = b;
+ }
+ }
+
+ public void reset() {
+ size = 0;
+ }
+
+ public ByteSequence toByteSequence() {
+ return new ByteSequence(buffer, 0, size);
+ }
+
+ public byte[] toByteArray() {
+ byte rc[] = new byte[size];
+ System.arraycopy(buffer, 0, rc, 0, size);
+ return rc;
+ }
+
+ public int size() {
+ return size;
+ }
+
+ public boolean endsWith(final byte[] array) {
+ int i = 0;
+ int start = size - array.length;
+ if (start < 0) {
+ return false;
+ }
+ while (start < size) {
+ if (buffer[start++] != array[i++]) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/util/ByteSequence.java b/activemq-filters/src/main/java/org/apache/activemq/util/ByteSequence.java
new file mode 100644
index 00000000..fcb96bd3
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/util/ByteSequence.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.util;
+
+import java.util.Arrays;
+
+public class ByteSequence {
+
+ public byte[] data;
+ public int offset;
+ public int length;
+
+ public ByteSequence() {}
+
+ public ByteSequence(byte data[]) {
+ this.data = data;
+ this.offset = 0;
+ this.length = data.length;
+ }
+
+ public ByteSequence(byte data[], int offset, int length) {
+ this.data = data;
+ this.offset = offset;
+ this.length = length;
+ }
+
+ public byte[] getData() {
+ return data;
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public int remaining() {
+ return length - offset;
+ }
+
+ public void setData(byte[] data) {
+ this.data = data;
+ }
+
+ public void setLength(int length) {
+ this.length = length;
+ }
+
+ public void setOffset(int offset) {
+ this.offset = offset;
+ }
+
+ public void compact() {
+ if (length != data.length) {
+ byte t[] = new byte[length];
+ System.arraycopy(data, offset, t, 0, length);
+ data = t;
+ offset = 0;
+ }
+ }
+
+ public void reset() {
+ length = remaining();
+ if (length > 0) {
+ System.arraycopy(data, offset, data, 0, length);
+ } else {
+ length = 0;
+ }
+ offset = 0;
+ }
+
+ public int indexOf(ByteSequence needle, int pos) {
+ int max = length - needle.length - offset;
+ for (int i = pos; i < max; i++) {
+ if (matches(needle, i)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private boolean matches(ByteSequence needle, int pos) {
+ for (int i = 0; i < needle.length; i++) {
+ if (data[offset + pos + i] != needle.data[needle.offset + i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private byte getByte(int i) {
+ return data[offset + i];
+ }
+
+ public final int indexOf(byte value, int pos) {
+ for (int i = pos; i < length; i++) {
+ if (data[offset + i] == value) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public boolean startsWith(final byte[] bytes) {
+ if (length - offset < bytes.length) {
+ return false;
+ }
+ for (int i = 0; i < bytes.length; i++) {
+ if (data[offset + i] != bytes[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Makes a deep copy of the data into a new byte array starting at the offset.
+ *
+ * @return
+ */
+ public byte[] toArray() {
+ return Arrays.copyOfRange(getData(), getOffset(), getLength());
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/util/Callback.java b/activemq-filters/src/main/java/org/apache/activemq/util/Callback.java
new file mode 100644
index 00000000..0a9d6e46
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/util/Callback.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.util;
+
+/**
+ * A simple callback object used by objects to provide automatic transactional or exception handling
+ * blocks.
+ */
+public interface Callback {
+
+ /**
+ * Executes some piece of code within a transaction performing a commit if there is no exception
+ * thrown else a rollback is performed
+ *
+ * @throws Exception TODO
+ */
+ void execute() throws Exception;
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/util/DataByteArrayInputStream.java b/activemq-filters/src/main/java/org/apache/activemq/util/DataByteArrayInputStream.java
new file mode 100644
index 00000000..22c294a1
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/util/DataByteArrayInputStream.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.util;
+
+import java.io.DataInput;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UTFDataFormatException;
+
+/** Optimized ByteArrayInputStream that can be used more than once */
+public final class DataByteArrayInputStream extends InputStream implements DataInput {
+ private byte[] buf;
+ private int pos;
+ private int offset;
+
+ /**
+ * Creates a StoreByteArrayInputStream
.
+ *
+ * @param buf the input buffer.
+ */
+ public DataByteArrayInputStream(byte buf[]) {
+ this.buf = buf;
+ this.pos = 0;
+ this.offset = 0;
+ }
+
+ /**
+ * Creates a StoreByteArrayInputStream
.
+ *
+ * @param sequence the input buffer.
+ */
+ public DataByteArrayInputStream(ByteSequence sequence) {
+ this.buf = sequence.getData();
+ this.offset = sequence.getOffset();
+ this.pos = this.offset;
+ }
+
+ /** Creates WireByteArrayInputStream
with a minmalist byte array */
+ public DataByteArrayInputStream() {
+ this(new byte[0]);
+ }
+
+ /** @return the size */
+ public int size() {
+ return pos - offset;
+ }
+
+ public int position() {
+ return pos;
+ }
+
+ /** @return the underlying data array */
+ public byte[] getRawData() {
+ return buf;
+ }
+
+ /**
+ * reset the StoreByteArrayInputStream
to use an new byte array
+ *
+ * @param newBuff
+ */
+ public void restart(byte[] newBuff) {
+ buf = newBuff;
+ pos = 0;
+ }
+
+ /**
+ * reset the StoreByteArrayInputStream
to use an new ByteSequence
+ *
+ * @param sequence
+ */
+ public void restart(ByteSequence sequence) {
+ this.buf = sequence.getData();
+ this.pos = sequence.getOffset();
+ }
+
+ /**
+ * re-start the input stream - reusing the current buffer
+ *
+ * @param size
+ */
+ public void restart(int size) {
+ if (buf == null || buf.length < size) {
+ buf = new byte[size];
+ }
+ restart(buf);
+ }
+
+ /**
+ * Reads the next byte of data from this input stream. The value byte is returned as an int
+ *
in the range 0
to 255
. If no byte is available because the
+ * end of the stream has been reached, the value -1
is returned.
+ *
+ * This read
method cannot block.
+ *
+ * @return the next byte of data, or -1
if the end of the stream has been reached.
+ */
+ public int read() {
+ return (pos < buf.length) ? (buf[pos++] & 0xff) : -1;
+ }
+
+ public int readOrIOException() throws IOException {
+ int rc = read();
+ if (rc == -1) {
+ throw new EOFException();
+ }
+ return rc;
+ }
+
+ /**
+ * Reads up to len
bytes of data into an array of bytes from this input stream.
+ *
+ * @param b the buffer into which the data is read.
+ * @param off the start offset of the data.
+ * @param len the maximum number of bytes read.
+ * @return the total number of bytes read into the buffer, or -1
if there is no more
+ * data because the end of the stream has been reached.
+ */
+ public int read(byte b[], int off, int len) {
+ if (b == null) {
+ throw new NullPointerException();
+ }
+ if (pos >= buf.length) {
+ return -1;
+ }
+ if (pos + len > buf.length) {
+ len = buf.length - pos;
+ }
+ if (len <= 0) {
+ return 0;
+ }
+ System.arraycopy(buf, pos, b, off, len);
+ pos += len;
+ return len;
+ }
+
+ /** @return the number of bytes that can be read from the input stream without blocking. */
+ public int available() {
+ return buf.length - pos;
+ }
+
+ public void readFully(byte[] b) {
+ read(b, 0, b.length);
+ }
+
+ public void readFully(byte[] b, int off, int len) {
+ read(b, off, len);
+ }
+
+ public int skipBytes(int n) {
+ if (pos + n > buf.length) {
+ n = buf.length - pos;
+ }
+ if (n < 0) {
+ return 0;
+ }
+ pos += n;
+ return n;
+ }
+
+ public boolean readBoolean() throws IOException {
+ return readOrIOException() != 0;
+ }
+
+ public byte readByte() throws IOException {
+ return (byte) readOrIOException();
+ }
+
+ public int readUnsignedByte() throws IOException {
+ return readOrIOException();
+ }
+
+ public short readShort() throws IOException {
+ int ch1 = readOrIOException();
+ int ch2 = readOrIOException();
+ return (short) ((ch1 << 8) + (ch2 << 0));
+ }
+
+ public int readUnsignedShort() throws IOException {
+ int ch1 = readOrIOException();
+ int ch2 = readOrIOException();
+ return (ch1 << 8) + (ch2 << 0);
+ }
+
+ public char readChar() throws IOException {
+ int ch1 = readOrIOException();
+ int ch2 = readOrIOException();
+ return (char) ((ch1 << 8) + (ch2 << 0));
+ }
+
+ public int readInt() throws IOException {
+ int ch1 = readOrIOException();
+ int ch2 = readOrIOException();
+ int ch3 = readOrIOException();
+ int ch4 = readOrIOException();
+ return (ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0);
+ }
+
+ public long readLong() throws IOException {
+ if (pos + 8 > buf.length) {
+ throw new EOFException();
+ }
+ long rc =
+ ((long) buf[pos++] << 56)
+ + ((long) (buf[pos++] & 255) << 48)
+ + ((long) (buf[pos++] & 255) << 40)
+ + ((long) (buf[pos++] & 255) << 32);
+ return rc
+ + ((long) (buf[pos++] & 255) << 24)
+ + ((buf[pos++] & 255) << 16)
+ + ((buf[pos++] & 255) << 8)
+ + ((buf[pos++] & 255) << 0);
+ }
+
+ public float readFloat() throws IOException {
+ return Float.intBitsToFloat(readInt());
+ }
+
+ public double readDouble() throws IOException {
+ return Double.longBitsToDouble(readLong());
+ }
+
+ public String readLine() {
+ int start = pos;
+ while (pos < buf.length) {
+ int c = read();
+ if (c == '\n') {
+ break;
+ }
+ if (c == '\r') {
+ c = read();
+ if (c != '\n' && c != -1) {
+ pos--;
+ }
+ break;
+ }
+ }
+ return new String(buf, start, pos);
+ }
+
+ public String readUTF() throws IOException {
+ int length = readUnsignedShort();
+ if (pos + length > buf.length) {
+ throw new UTFDataFormatException("bad string");
+ }
+ char chararr[] = new char[length];
+ String result = MarshallingSupport.convertUTF8WithBuf(buf, chararr, pos, length);
+ pos += length;
+ return result;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/util/DataByteArrayOutputStream.java b/activemq-filters/src/main/java/org/apache/activemq/util/DataByteArrayOutputStream.java
new file mode 100644
index 00000000..bf1d064a
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/util/DataByteArrayOutputStream.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.util;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UTFDataFormatException;
+
+/** Optimized ByteArrayOutputStream */
+public final class DataByteArrayOutputStream extends OutputStream implements DataOutput {
+ private static final int DEFAULT_SIZE = 2048;
+ private byte buf[];
+ private int pos;
+
+ /**
+ * Creates a new byte array output stream, with a buffer capacity of the specified size, in bytes.
+ *
+ * @param size the initial size.
+ * @exception IllegalArgumentException if size is negative.
+ */
+ public DataByteArrayOutputStream(int size) {
+ if (size < 0) {
+ throw new IllegalArgumentException("Invalid size: " + size);
+ }
+ buf = new byte[size];
+ }
+
+ /** Creates a new byte array output stream. */
+ public DataByteArrayOutputStream() {
+ this(DEFAULT_SIZE);
+ }
+
+ /**
+ * start using a fresh byte array
+ *
+ * @param size
+ */
+ public void restart(int size) {
+ buf = new byte[size];
+ pos = 0;
+ }
+
+ /** start using a fresh byte array */
+ public void restart() {
+ restart(DEFAULT_SIZE);
+ }
+
+ /**
+ * Get a ByteSequence from the stream
+ *
+ * @return the byte sequence
+ */
+ public ByteSequence toByteSequence() {
+ return new ByteSequence(buf, 0, pos);
+ }
+
+ /**
+ * Writes the specified byte to this byte array output stream.
+ *
+ * @param b the byte to be written.
+ */
+ public void write(int b) {
+ int newcount = pos + 1;
+ ensureEnoughBuffer(newcount);
+ buf[pos] = (byte) b;
+ pos = newcount;
+ }
+
+ /**
+ * Writes len
bytes from the specified byte array starting at offset off
+ * to this byte array output stream.
+ *
+ * @param b the data.
+ * @param off the start offset in the data.
+ * @param len the number of bytes to write.
+ */
+ public void write(byte b[], int off, int len) {
+ if (len == 0) {
+ return;
+ }
+ int newcount = pos + len;
+ ensureEnoughBuffer(newcount);
+ System.arraycopy(b, off, buf, pos, len);
+ pos = newcount;
+ }
+
+ /** @return the underlying byte[] buffer */
+ public byte[] getData() {
+ return buf;
+ }
+
+ /** reset the output stream */
+ public void reset() {
+ pos = 0;
+ }
+
+ /**
+ * Set the current position for writing
+ *
+ * @param offset
+ */
+ public void position(int offset) {
+ ensureEnoughBuffer(offset);
+ pos = offset;
+ }
+
+ public int size() {
+ return pos;
+ }
+
+ public void writeBoolean(boolean v) {
+ ensureEnoughBuffer(pos + 1);
+ buf[pos++] = (byte) (v ? 1 : 0);
+ }
+
+ public void writeByte(int v) {
+ ensureEnoughBuffer(pos + 1);
+ buf[pos++] = (byte) (v >>> 0);
+ }
+
+ public void writeShort(int v) {
+ ensureEnoughBuffer(pos + 2);
+ buf[pos++] = (byte) (v >>> 8);
+ buf[pos++] = (byte) (v >>> 0);
+ }
+
+ public void writeChar(int v) {
+ ensureEnoughBuffer(pos + 2);
+ buf[pos++] = (byte) (v >>> 8);
+ buf[pos++] = (byte) (v >>> 0);
+ }
+
+ public void writeInt(int v) {
+ ensureEnoughBuffer(pos + 4);
+ buf[pos++] = (byte) (v >>> 24);
+ buf[pos++] = (byte) (v >>> 16);
+ buf[pos++] = (byte) (v >>> 8);
+ buf[pos++] = (byte) (v >>> 0);
+ }
+
+ public void writeLong(long v) {
+ ensureEnoughBuffer(pos + 8);
+ buf[pos++] = (byte) (v >>> 56);
+ buf[pos++] = (byte) (v >>> 48);
+ buf[pos++] = (byte) (v >>> 40);
+ buf[pos++] = (byte) (v >>> 32);
+ buf[pos++] = (byte) (v >>> 24);
+ buf[pos++] = (byte) (v >>> 16);
+ buf[pos++] = (byte) (v >>> 8);
+ buf[pos++] = (byte) (v >>> 0);
+ }
+
+ public void writeFloat(float v) throws IOException {
+ writeInt(Float.floatToIntBits(v));
+ }
+
+ public void writeDouble(double v) throws IOException {
+ writeLong(Double.doubleToLongBits(v));
+ }
+
+ public void writeBytes(String s) {
+ int length = s.length();
+ for (int i = 0; i < length; i++) {
+ write((byte) s.charAt(i));
+ }
+ }
+
+ public void writeChars(String s) {
+ int length = s.length();
+ for (int i = 0; i < length; i++) {
+ int c = s.charAt(i);
+ write((c >>> 8) & 0xFF);
+ write((c >>> 0) & 0xFF);
+ }
+ }
+
+ public void writeUTF(String text) throws IOException {
+ long encodedsize = MarshallingSupport.countUTFBytes(text);
+ if (encodedsize > 65535) {
+ throw new UTFDataFormatException("encoded string too long: " + encodedsize + " bytes");
+ }
+ ensureEnoughBuffer((int) (pos + encodedsize + 2));
+ writeShort((int) encodedsize);
+
+ byte[] buffer = new byte[(int) encodedsize];
+ MarshallingSupport.writeUTFBytesToBuffer(text, (int) encodedsize, buf, pos);
+ pos += encodedsize;
+ }
+
+ private void ensureEnoughBuffer(int newcount) {
+ if (newcount > buf.length) {
+ byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)];
+ System.arraycopy(buf, 0, newbuf, 0, pos);
+ buf = newbuf;
+ }
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/util/IntrospectionSupport.java b/activemq-filters/src/main/java/org/apache/activemq/util/IntrospectionSupport.java
new file mode 100644
index 00000000..2316633e
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/util/IntrospectionSupport.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.util;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import javax.net.ssl.SSLServerSocket;
+import org.apache.activemq.command.ActiveMQDestination;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class IntrospectionSupport {
+
+ private static final Logger LOG = LoggerFactory.getLogger(IntrospectionSupport.class);
+
+ private IntrospectionSupport() {}
+
+ public static boolean getProperties(Object target, Map props, String optionPrefix) {
+
+ boolean rc = false;
+ if (target == null) {
+ throw new IllegalArgumentException("target was null.");
+ }
+ if (props == null) {
+ throw new IllegalArgumentException("props was null.");
+ }
+
+ if (optionPrefix == null) {
+ optionPrefix = "";
+ }
+
+ Class> clazz = target.getClass();
+ Method[] methods = clazz.getMethods();
+ for (Method method : methods) {
+ String name = method.getName();
+ Class> type = method.getReturnType();
+ Class> params[] = method.getParameterTypes();
+ if ((name.startsWith("is") || name.startsWith("get")) && params.length == 0 && type != null) {
+
+ try {
+
+ Object value = method.invoke(target);
+ if (value == null) {
+ continue;
+ }
+
+ String strValue = convertToString(value, type);
+ if (strValue == null) {
+ continue;
+ }
+ if (name.startsWith("get")) {
+ name = name.substring(3, 4).toLowerCase(Locale.ENGLISH) + name.substring(4);
+ } else {
+ name = name.substring(2, 3).toLowerCase(Locale.ENGLISH) + name.substring(3);
+ }
+ props.put(optionPrefix + name, strValue);
+ rc = true;
+
+ } catch (Exception ignore) {
+ }
+ }
+ }
+
+ return rc;
+ }
+
+ public static boolean setProperties(Object target, Map props, String optionPrefix) {
+ boolean rc = false;
+ if (target == null) {
+ throw new IllegalArgumentException("target was null.");
+ }
+ if (props == null) {
+ throw new IllegalArgumentException("props was null.");
+ }
+
+ for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) {
+ String name = iter.next();
+ if (name.startsWith(optionPrefix)) {
+ Object value = props.get(name);
+ name = name.substring(optionPrefix.length());
+ if (setProperty(target, name, value)) {
+ iter.remove();
+ rc = true;
+ }
+ }
+ }
+ return rc;
+ }
+
+ public static Map extractProperties(Map props, String optionPrefix) {
+ if (props == null) {
+ throw new IllegalArgumentException("props was null.");
+ }
+
+ HashMap rc = new HashMap(props.size());
+
+ for (Iterator> iter = props.keySet().iterator(); iter.hasNext(); ) {
+ String name = (String) iter.next();
+ if (name.startsWith(optionPrefix)) {
+ Object value = props.get(name);
+ name = name.substring(optionPrefix.length());
+ rc.put(name, value);
+ iter.remove();
+ }
+ }
+
+ return rc;
+ }
+
+ public static boolean setProperties(Object target, Map, ?> props) {
+ return setProperties(target, props, true);
+ }
+
+ public static boolean setProperties(Object target, Map, ?> props, boolean removeIfSet) {
+ boolean rc = false;
+
+ if (target == null) {
+ throw new IllegalArgumentException("target was null.");
+ }
+ if (props == null) {
+ throw new IllegalArgumentException("props was null.");
+ }
+
+ for (Iterator> iter = props.entrySet().iterator(); iter.hasNext(); ) {
+ Entry, ?> entry = (Entry, ?>) iter.next();
+ if (setProperty(target, (String) entry.getKey(), entry.getValue())) {
+ if (removeIfSet) {
+ iter.remove();
+ }
+ rc = true;
+ }
+ }
+
+ return rc;
+ }
+
+ public static boolean setProperty(Object target, String name, Object value) {
+ try {
+ Class> clazz = target.getClass();
+ if (target instanceof SSLServerSocket) {
+ // overcome illegal access issues with internal implementation class
+ clazz = SSLServerSocket.class;
+ }
+ Method setter = findSetterMethod(clazz, name);
+ if (setter == null) {
+ return false;
+ }
+
+ // JDK 11: class or setter might not be publicly accessible
+ setter.setAccessible(true);
+
+ // If the type is null or it matches the needed type, just use the
+ // value directly
+ if (value == null || value.getClass() == setter.getParameterTypes()[0]) {
+ setter.invoke(target, value);
+ } else {
+ // We need to convert it
+ setter.invoke(target, convert(value, setter.getParameterTypes()[0]));
+ }
+ return true;
+ } catch (Exception e) {
+ LOG.error(String.format("Could not set property %s on %s", name, target), e);
+ return false;
+ }
+ }
+
+ private static Object convert(Object value, Class to) {
+ if (value == null) {
+ // lets avoid NullPointerException when converting to boolean for null values
+ if (boolean.class.isAssignableFrom(to)) {
+ return Boolean.FALSE;
+ }
+ return null;
+ }
+
+ // eager same instance type test to avoid the overhead of invoking the type converter
+ // if already same type
+ if (to.isAssignableFrom(value.getClass())) {
+ return to.cast(value);
+ }
+
+ // special for String[] as we do not want to use a PropertyEditor for that
+ if (to.isAssignableFrom(String[].class)) {
+ return StringArrayConverter.convertToStringArray(value);
+ }
+
+ // special for String to List as we do not want to use a PropertyEditor for
+ // that
+ if (value.getClass().equals(String.class) && to.equals(List.class)) {
+ Object answer =
+ StringToListOfActiveMQDestinationConverter.convertToActiveMQDestination(value);
+ if (answer != null) {
+ return answer;
+ }
+ }
+
+ TypeConversionSupport.Converter converter =
+ TypeConversionSupport.lookupConverter(value.getClass(), to);
+ if (converter != null) {
+ return converter.convert(value);
+ } else {
+ throw new IllegalArgumentException(
+ "Cannot convert from " + value.getClass() + " to " + to + " with value " + value);
+ }
+ }
+
+ public static String convertToString(Object value, Class to) {
+ if (value == null) {
+ return null;
+ }
+
+ // already a String
+ if (value instanceof String) {
+ return (String) value;
+ }
+
+ // special for String[] as we do not want to use a PropertyEditor for that
+ if (String[].class.isInstance(value)) {
+ String[] array = (String[]) value;
+ return StringArrayConverter.convertToString(array);
+ }
+
+ // special for String to List as we do not want to use a PropertyEditor for
+ // that
+ if (List.class.isInstance(value)) {
+ // if the list is a ActiveMQDestination, then return a comma list
+ String answer =
+ StringToListOfActiveMQDestinationConverter.convertFromActiveMQDestination(value);
+ if (answer != null) {
+ return answer;
+ }
+ }
+
+ TypeConversionSupport.Converter converter =
+ TypeConversionSupport.lookupConverter(value.getClass(), String.class);
+ if (converter != null) {
+ return (String) converter.convert(value);
+ } else {
+ throw new IllegalArgumentException(
+ "Cannot convert from " + value.getClass() + " to " + to + " with value " + value);
+ }
+ }
+
+ public static Method findSetterMethod(Class clazz, String name) {
+ // Build the method name.
+ name = "set" + Character.toUpperCase(name.charAt(0)) + name.substring(1);
+ Method[] methods = clazz.getMethods();
+ for (Method method : methods) {
+ Class> params[] = method.getParameterTypes();
+ if (method.getName().equals(name) && params.length == 1) {
+ return method;
+ }
+ }
+ return null;
+ }
+
+ public static Method findGetterMethod(Class clazz, String name) {
+ // Build the method name.
+ name = "get" + Character.toUpperCase(name.charAt(0)) + name.substring(1);
+ Method[] methods = clazz.getMethods();
+ for (Method method : methods) {
+ Class> params[] = method.getParameterTypes();
+ if (method.getName().equals(name) && params.length == 0) {
+ return method;
+ }
+ }
+ return null;
+ }
+
+ public static String toString(Object target) {
+ return toString(target, Object.class, null);
+ }
+
+ public static String toString(Object target, Class stopClass) {
+ return toString(target, stopClass, null);
+ }
+
+ public static String toString(
+ Object target, Class stopClass, Map overrideFields) {
+ LinkedHashMap map = new LinkedHashMap();
+ addFields(target, target.getClass(), stopClass, map);
+ if (overrideFields != null) {
+ for (String key : overrideFields.keySet()) {
+ Object value = overrideFields.get(key);
+ map.put(key, value);
+ }
+ }
+ StringBuffer buffer = new StringBuffer(simpleName(target.getClass()));
+ buffer.append(" {");
+ Set> entrySet = map.entrySet();
+ boolean first = true;
+ for (Entry entry : entrySet) {
+ Object value = entry.getValue();
+ Object key = entry.getKey();
+ if (first) {
+ first = false;
+ } else {
+ buffer.append(", ");
+ }
+ buffer.append(key);
+ buffer.append(" = ");
+
+ appendToString(buffer, key, value);
+ }
+ buffer.append("}");
+ return buffer.toString();
+ }
+
+ protected static void appendToString(StringBuffer buffer, Object key, Object value) {
+ if (value instanceof ActiveMQDestination) {
+ ActiveMQDestination destination = (ActiveMQDestination) value;
+ buffer.append(destination.getQualifiedName());
+ } else if (key.toString().toLowerCase(Locale.ENGLISH).contains("password")) {
+ buffer.append("*****");
+ } else {
+ buffer.append(value);
+ }
+ }
+
+ public static String simpleName(Class clazz) {
+ String name = clazz.getName();
+ int p = name.lastIndexOf(".");
+ if (p >= 0) {
+ name = name.substring(p + 1);
+ }
+ return name;
+ }
+
+ private static void addFields(
+ Object target, Class startClass, Class stopClass, LinkedHashMap map) {
+
+ if (startClass != stopClass) {
+ addFields(target, startClass.getSuperclass(), stopClass, map);
+ }
+
+ Field[] fields = startClass.getDeclaredFields();
+ for (Field field : fields) {
+ if (Modifier.isStatic(field.getModifiers())
+ || Modifier.isTransient(field.getModifiers())
+ || Modifier.isPrivate(field.getModifiers())) {
+ continue;
+ }
+
+ try {
+ field.setAccessible(true);
+ Object o = field.get(target);
+ if (o != null && o.getClass().isArray()) {
+ try {
+ o = Arrays.asList((Object[]) o);
+ } catch (Exception e) {
+ }
+ }
+ map.put(field.getName(), o);
+ } catch (Exception e) {
+ LOG.debug(
+ "Error getting field "
+ + field
+ + " on class "
+ + startClass
+ + ". This exception is ignored.",
+ e);
+ }
+ }
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/util/JMSExceptionSupport.java b/activemq-filters/src/main/java/org/apache/activemq/util/JMSExceptionSupport.java
new file mode 100644
index 00000000..23a49ee4
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/util/JMSExceptionSupport.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.util;
+
+import jakarta.jms.IllegalStateRuntimeException;
+import jakarta.jms.InvalidClientIDRuntimeException;
+import jakarta.jms.InvalidDestinationRuntimeException;
+import jakarta.jms.InvalidSelectorRuntimeException;
+import jakarta.jms.JMSException;
+import jakarta.jms.JMSRuntimeException;
+import jakarta.jms.JMSSecurityException;
+import jakarta.jms.JMSSecurityRuntimeException;
+import jakarta.jms.MessageEOFException;
+import jakarta.jms.MessageFormatException;
+import jakarta.jms.MessageFormatRuntimeException;
+import jakarta.jms.MessageNotWriteableRuntimeException;
+import jakarta.jms.ResourceAllocationRuntimeException;
+import jakarta.jms.TransactionInProgressRuntimeException;
+import jakarta.jms.TransactionRolledBackRuntimeException;
+import org.apache.activemq.MaxFrameSizeExceededException;
+
+public final class JMSExceptionSupport {
+
+ private JMSExceptionSupport() {}
+
+ public static JMSException create(String msg, Throwable cause) {
+ JMSException exception = new JMSException(msg);
+ exception.initCause(cause);
+ return exception;
+ }
+
+ public static JMSException create(String msg, Exception cause) {
+ JMSException exception = new JMSException(msg);
+ exception.setLinkedException(cause);
+ exception.initCause(cause);
+ return exception;
+ }
+
+ public static JMSException create(Throwable cause) {
+ if (cause instanceof JMSException) {
+ return (JMSException) cause;
+ }
+ String msg = cause.getMessage();
+ if (msg == null || msg.length() == 0) {
+ msg = cause.toString();
+ }
+ JMSException exception;
+ if (cause instanceof SecurityException) {
+ exception = new JMSSecurityException(msg);
+ } else {
+ exception = new JMSException(msg);
+ }
+ exception.initCause(cause);
+ return exception;
+ }
+
+ public static JMSException create(Exception cause) {
+ if (cause instanceof JMSException) {
+ return (JMSException) cause;
+ }
+ if (cause instanceof MaxFrameSizeExceededException) {
+ JMSException jmsException = new JMSException(cause.getMessage(), "41300");
+ jmsException.setLinkedException(cause);
+ jmsException.initCause(cause);
+ return jmsException;
+ }
+ String msg = cause.getMessage();
+ if (msg == null || msg.length() == 0) {
+ msg = cause.toString();
+ }
+ JMSException exception;
+ if (cause instanceof SecurityException) {
+ exception = new JMSSecurityException(msg);
+ } else {
+ exception = new JMSException(msg);
+ }
+ exception.setLinkedException(cause);
+ exception.initCause(cause);
+ return exception;
+ }
+
+ public static MessageEOFException createMessageEOFException(Exception cause) {
+ String msg = cause.getMessage();
+ if (msg == null || msg.length() == 0) {
+ msg = cause.toString();
+ }
+ MessageEOFException exception = new MessageEOFException(msg);
+ exception.setLinkedException(cause);
+ exception.initCause(cause);
+ return exception;
+ }
+
+ public static MessageFormatException createMessageFormatException(Exception cause) {
+ String msg = cause.getMessage();
+ if (msg == null || msg.length() == 0) {
+ msg = cause.toString();
+ }
+ MessageFormatException exception = new MessageFormatException(msg);
+ exception.setLinkedException(cause);
+ exception.initCause(cause);
+ return exception;
+ }
+
+ public static JMSRuntimeException convertToJMSRuntimeException(JMSException e) {
+ if (e instanceof jakarta.jms.IllegalStateException) {
+ return new IllegalStateRuntimeException(e.getMessage(), e.getErrorCode(), e);
+ }
+ if (e instanceof jakarta.jms.InvalidClientIDException) {
+ return new InvalidClientIDRuntimeException(e.getMessage(), e.getErrorCode(), e);
+ }
+ if (e instanceof jakarta.jms.InvalidDestinationException) {
+ return new InvalidDestinationRuntimeException(e.getMessage(), e.getErrorCode(), e);
+ }
+ if (e instanceof jakarta.jms.InvalidSelectorException) {
+ return new InvalidSelectorRuntimeException(e.getMessage(), e.getErrorCode(), e);
+ }
+ if (e instanceof JMSSecurityException) {
+ return new JMSSecurityRuntimeException(e.getMessage(), e.getErrorCode(), e);
+ }
+ if (e instanceof MessageFormatException) {
+ return new MessageFormatRuntimeException(e.getMessage(), e.getErrorCode(), e);
+ }
+ if (e instanceof jakarta.jms.MessageNotWriteableException) {
+ return new MessageNotWriteableRuntimeException(e.getMessage(), e.getErrorCode(), e);
+ }
+ if (e instanceof jakarta.jms.ResourceAllocationException) {
+ return new ResourceAllocationRuntimeException(e.getMessage(), e.getErrorCode(), e);
+ }
+ if (e instanceof jakarta.jms.TransactionInProgressException) {
+ return new TransactionInProgressRuntimeException(e.getMessage(), e.getErrorCode(), e);
+ }
+ if (e instanceof jakarta.jms.TransactionRolledBackException) {
+ return new TransactionRolledBackRuntimeException(e.getMessage(), e.getErrorCode(), e);
+ }
+ return new JMSRuntimeException(e.getMessage(), e.getErrorCode(), e);
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/util/LRUCache.java b/activemq-filters/src/main/java/org/apache/activemq/util/LRUCache.java
new file mode 100644
index 00000000..5e435d14
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/util/LRUCache.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.util;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * A Simple LRU Cache
+ *
+ * @param Key
+ * @param Value
+ */
+public class LRUCache extends LinkedHashMap {
+ private static final long serialVersionUID = -342098639681884413L;
+ protected int maxCacheSize = 10000;
+
+ /** Default constructor for an LRU Cache The default capacity is 10000 */
+ public LRUCache() {
+ this(0, 10000, 0.75f, true);
+ }
+
+ /**
+ * Constructs a LRUCache with a maximum capacity
+ *
+ * @param maximumCacheSize
+ */
+ public LRUCache(int maximumCacheSize) {
+ this(0, maximumCacheSize, 0.75f, true);
+ }
+
+ /**
+ * Constructs an empty LRUCache instance with the specified initial capacity,
+ * maximumCacheSize,load factor and ordering mode.
+ *
+ * @param initialCapacity the initial capacity.
+ * @param maximumCacheSize
+ * @param loadFactor the load factor.
+ * @param accessOrder the ordering mode - true for access-order, false for
+ * insertion-order.
+ * @throws IllegalArgumentException if the initial capacity is negative or the load factor is
+ * non-positive.
+ */
+ public LRUCache(
+ int initialCapacity, int maximumCacheSize, float loadFactor, boolean accessOrder) {
+ super(initialCapacity, loadFactor, accessOrder);
+ this.maxCacheSize = maximumCacheSize;
+ }
+
+ /** @return Returns the maxCacheSize. */
+ public int getMaxCacheSize() {
+ return maxCacheSize;
+ }
+
+ /** @param maxCacheSize The maxCacheSize to set. */
+ public void setMaxCacheSize(int maxCacheSize) {
+ this.maxCacheSize = maxCacheSize;
+ }
+
+ protected boolean removeEldestEntry(Map.Entry eldest) {
+ if (size() > maxCacheSize) {
+ onCacheEviction(eldest);
+ return true;
+ }
+ return false;
+ }
+
+ protected void onCacheEviction(Map.Entry eldest) {}
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/util/MarshallingSupport.java b/activemq-filters/src/main/java/org/apache/activemq/util/MarshallingSupport.java
new file mode 100644
index 00000000..d989c126
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/util/MarshallingSupport.java
@@ -0,0 +1,426 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.util;
+
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.DataOutput;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.UTFDataFormatException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import org.fusesource.hawtbuf.UTF8Buffer;
+
+/**
+ * The fixed version of the UTF8 encoding function. Some older JVM's UTF8 encoding function breaks
+ * when handling large strings.
+ */
+public final class MarshallingSupport {
+
+ public static final byte NULL = 0;
+ public static final byte BOOLEAN_TYPE = 1;
+ public static final byte BYTE_TYPE = 2;
+ public static final byte CHAR_TYPE = 3;
+ public static final byte SHORT_TYPE = 4;
+ public static final byte INTEGER_TYPE = 5;
+ public static final byte LONG_TYPE = 6;
+ public static final byte DOUBLE_TYPE = 7;
+ public static final byte FLOAT_TYPE = 8;
+ public static final byte STRING_TYPE = 9;
+ public static final byte BYTE_ARRAY_TYPE = 10;
+ public static final byte MAP_TYPE = 11;
+ public static final byte LIST_TYPE = 12;
+ public static final byte BIG_STRING_TYPE = 13;
+
+ private MarshallingSupport() {}
+
+ public static void marshalPrimitiveMap(Map map, DataOutputStream out)
+ throws IOException {
+ if (map == null) {
+ out.writeInt(-1);
+ } else {
+ out.writeInt(map.size());
+ for (String name : map.keySet()) {
+ out.writeUTF(name);
+ Object value = map.get(name);
+ marshalPrimitive(out, value);
+ }
+ }
+ }
+
+ public static Map unmarshalPrimitiveMap(DataInputStream in) throws IOException {
+ return unmarshalPrimitiveMap(in, Integer.MAX_VALUE);
+ }
+
+ public static Map unmarshalPrimitiveMap(DataInputStream in, boolean force)
+ throws IOException {
+ return unmarshalPrimitiveMap(in, Integer.MAX_VALUE, force);
+ }
+
+ public static Map unmarshalPrimitiveMap(DataInputStream in, int maxPropertySize)
+ throws IOException {
+ return unmarshalPrimitiveMap(in, maxPropertySize, false);
+ }
+
+ /**
+ * @param in
+ * @return
+ * @throws IOException
+ * @throws IOException
+ */
+ public static Map unmarshalPrimitiveMap(
+ DataInputStream in, int maxPropertySize, boolean force) throws IOException {
+ int size = in.readInt();
+ if (size > maxPropertySize) {
+ throw new IOException("Primitive map is larger than the allowed size: " + size);
+ }
+ if (size < 0) {
+ return null;
+ } else {
+ Map rc = new HashMap(size);
+ for (int i = 0; i < size; i++) {
+ String name = in.readUTF();
+ rc.put(name, unmarshalPrimitive(in, force));
+ }
+ return rc;
+ }
+ }
+
+ public static void marshalPrimitiveList(List list, DataOutputStream out)
+ throws IOException {
+ out.writeInt(list.size());
+ for (Object element : list) {
+ marshalPrimitive(out, element);
+ }
+ }
+
+ public static List unmarshalPrimitiveList(DataInputStream in) throws IOException {
+ return unmarshalPrimitiveList(in, false);
+ }
+
+ public static List unmarshalPrimitiveList(DataInputStream in, boolean force)
+ throws IOException {
+ int size = in.readInt();
+ List answer = new ArrayList(size);
+ while (size-- > 0) {
+ answer.add(unmarshalPrimitive(in, force));
+ }
+ return answer;
+ }
+
+ public static void marshalPrimitive(DataOutputStream out, Object value) throws IOException {
+ if (value == null) {
+ marshalNull(out);
+ } else if (value.getClass() == Boolean.class) {
+ marshalBoolean(out, (Boolean) value);
+ } else if (value.getClass() == Byte.class) {
+ marshalByte(out, (Byte) value);
+ } else if (value.getClass() == Character.class) {
+ marshalChar(out, (Character) value);
+ } else if (value.getClass() == Short.class) {
+ marshalShort(out, (Short) value);
+ } else if (value.getClass() == Integer.class) {
+ marshalInt(out, (Integer) value);
+ } else if (value.getClass() == Long.class) {
+ marshalLong(out, (Long) value);
+ } else if (value.getClass() == Float.class) {
+ marshalFloat(out, (Float) value);
+ } else if (value.getClass() == Double.class) {
+ marshalDouble(out, (Double) value);
+ } else if (value.getClass() == byte[].class) {
+ marshalByteArray(out, (byte[]) value);
+ } else if (value.getClass() == String.class) {
+ marshalString(out, (String) value);
+ } else if (value.getClass() == UTF8Buffer.class) {
+ marshalString(out, value.toString());
+ } else if (value instanceof Map) {
+ out.writeByte(MAP_TYPE);
+ marshalPrimitiveMap((Map) value, out);
+ } else if (value instanceof List) {
+ out.writeByte(LIST_TYPE);
+ marshalPrimitiveList((List) value, out);
+ } else {
+ throw new IOException("Object is not a primitive: " + value);
+ }
+ }
+
+ public static Object unmarshalPrimitive(DataInputStream in) throws IOException {
+ return unmarshalPrimitive(in, false);
+ }
+
+ public static Object unmarshalPrimitive(DataInputStream in, boolean force) throws IOException {
+ Object value = null;
+ byte type = in.readByte();
+ switch (type) {
+ case BYTE_TYPE:
+ value = in.readByte();
+ break;
+ case BOOLEAN_TYPE:
+ value = in.readBoolean() ? Boolean.TRUE : Boolean.FALSE;
+ break;
+ case CHAR_TYPE:
+ value = in.readChar();
+ break;
+ case SHORT_TYPE:
+ value = in.readShort();
+ break;
+ case INTEGER_TYPE:
+ value = in.readInt();
+ break;
+ case LONG_TYPE:
+ value = in.readLong();
+ break;
+ case FLOAT_TYPE:
+ value = in.readFloat();
+ break;
+ case DOUBLE_TYPE:
+ value = in.readDouble();
+ break;
+ case BYTE_ARRAY_TYPE:
+ value = new byte[in.readInt()];
+ in.readFully((byte[]) value);
+ break;
+ case STRING_TYPE:
+ if (force) {
+ value = in.readUTF();
+ } else {
+ value = readUTF(in, in.readUnsignedShort());
+ }
+ break;
+ case BIG_STRING_TYPE:
+ {
+ if (force) {
+ value = readUTF8(in);
+ } else {
+ value = readUTF(in, in.readInt());
+ }
+ break;
+ }
+ case MAP_TYPE:
+ value = unmarshalPrimitiveMap(in, true);
+ break;
+ case LIST_TYPE:
+ value = unmarshalPrimitiveList(in, true);
+ break;
+ case NULL:
+ value = null;
+ break;
+ default:
+ throw new IOException("Unknown primitive type: " + type);
+ }
+ return value;
+ }
+
+ public static UTF8Buffer readUTF(DataInputStream in, int length) throws IOException {
+ byte data[] = new byte[length];
+ in.readFully(data);
+ return new UTF8Buffer(data);
+ }
+
+ public static void marshalNull(DataOutputStream out) throws IOException {
+ out.writeByte(NULL);
+ }
+
+ public static void marshalBoolean(DataOutputStream out, boolean value) throws IOException {
+ out.writeByte(BOOLEAN_TYPE);
+ out.writeBoolean(value);
+ }
+
+ public static void marshalByte(DataOutputStream out, byte value) throws IOException {
+ out.writeByte(BYTE_TYPE);
+ out.writeByte(value);
+ }
+
+ public static void marshalChar(DataOutputStream out, char value) throws IOException {
+ out.writeByte(CHAR_TYPE);
+ out.writeChar(value);
+ }
+
+ public static void marshalShort(DataOutputStream out, short value) throws IOException {
+ out.writeByte(SHORT_TYPE);
+ out.writeShort(value);
+ }
+
+ public static void marshalInt(DataOutputStream out, int value) throws IOException {
+ out.writeByte(INTEGER_TYPE);
+ out.writeInt(value);
+ }
+
+ public static void marshalLong(DataOutputStream out, long value) throws IOException {
+ out.writeByte(LONG_TYPE);
+ out.writeLong(value);
+ }
+
+ public static void marshalFloat(DataOutputStream out, float value) throws IOException {
+ out.writeByte(FLOAT_TYPE);
+ out.writeFloat(value);
+ }
+
+ public static void marshalDouble(DataOutputStream out, double value) throws IOException {
+ out.writeByte(DOUBLE_TYPE);
+ out.writeDouble(value);
+ }
+
+ public static void marshalByteArray(DataOutputStream out, byte[] value) throws IOException {
+ marshalByteArray(out, value, 0, value.length);
+ }
+
+ public static void marshalByteArray(DataOutputStream out, byte[] value, int offset, int length)
+ throws IOException {
+ out.writeByte(BYTE_ARRAY_TYPE);
+ out.writeInt(length);
+ out.write(value, offset, length);
+ }
+
+ public static void marshalString(DataOutputStream out, String s) throws IOException {
+ // If it's too big, out.writeUTF may not able able to write it out.
+ if (s.length() < Short.MAX_VALUE / 4) {
+ out.writeByte(STRING_TYPE);
+ out.writeUTF(s);
+ } else {
+ out.writeByte(BIG_STRING_TYPE);
+ writeUTF8(out, s);
+ }
+ }
+
+ public static void writeUTF8(DataOutput dataOut, String text) throws IOException {
+ if (text != null) {
+ long utfCount = countUTFBytes(text);
+ dataOut.writeInt((int) utfCount);
+
+ byte[] buffer = new byte[(int) utfCount];
+ int len = writeUTFBytesToBuffer(text, (int) utfCount, buffer, 0);
+ dataOut.write(buffer, 0, len);
+
+ assert utfCount == len;
+ } else {
+ dataOut.writeInt(-1);
+ }
+ }
+
+ /**
+ * From:
+ * http://svn.apache.org/repos/asf/harmony/enhanced/java/trunk/classlib/modules/luni/src/main/java/java/io/DataOutputStream.java
+ */
+ public static long countUTFBytes(String str) {
+ int utfCount = 0, length = str.length();
+ for (int i = 0; i < length; i++) {
+ int charValue = str.charAt(i);
+ if (charValue > 0 && charValue <= 127) {
+ utfCount++;
+ } else if (charValue <= 2047) {
+ utfCount += 2;
+ } else {
+ utfCount += 3;
+ }
+ }
+ return utfCount;
+ }
+
+ /**
+ * From:
+ * http://svn.apache.org/repos/asf/harmony/enhanced/java/trunk/classlib/modules/luni/src/main/java/java/io/DataOutputStream.java
+ */
+ public static int writeUTFBytesToBuffer(String str, long count, byte[] buffer, int offset)
+ throws IOException {
+ int length = str.length();
+ for (int i = 0; i < length; i++) {
+ int charValue = str.charAt(i);
+ if (charValue > 0 && charValue <= 127) {
+ buffer[offset++] = (byte) charValue;
+ } else if (charValue <= 2047) {
+ buffer[offset++] = (byte) (0xc0 | (0x1f & (charValue >> 6)));
+ buffer[offset++] = (byte) (0x80 | (0x3f & charValue));
+ } else {
+ buffer[offset++] = (byte) (0xe0 | (0x0f & (charValue >> 12)));
+ buffer[offset++] = (byte) (0x80 | (0x3f & (charValue >> 6)));
+ buffer[offset++] = (byte) (0x80 | (0x3f & charValue));
+ }
+ }
+ return offset;
+ }
+
+ public static String readUTF8(DataInput dataIn) throws IOException {
+ int utflen = dataIn.readInt();
+ if (utflen > -1) {
+ byte bytearr[] = new byte[utflen];
+ char chararr[] = new char[utflen];
+ dataIn.readFully(bytearr, 0, utflen);
+ return convertUTF8WithBuf(bytearr, chararr, 0, utflen);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * From:
+ * http://svn.apache.org/repos/asf/harmony/enhanced/java/trunk/classlib/modules/luni/src/main/java/org/apache/harmony/luni/util/Util.java
+ */
+ public static String convertUTF8WithBuf(byte[] buf, char[] out, int offset, int utfSize)
+ throws UTFDataFormatException {
+ int count = 0, s = 0, a;
+ while (count < utfSize) {
+ if ((out[s] = (char) buf[offset + count++]) < '\u0080') s++;
+ else if (((a = out[s]) & 0xe0) == 0xc0) {
+ if (count >= utfSize) throw new UTFDataFormatException();
+ int b = buf[offset + count++];
+ if ((b & 0xC0) != 0x80) throw new UTFDataFormatException();
+ out[s++] = (char) (((a & 0x1F) << 6) | (b & 0x3F));
+ } else if ((a & 0xf0) == 0xe0) {
+ if (count + 1 >= utfSize) throw new UTFDataFormatException();
+ int b = buf[offset + count++];
+ int c = buf[offset + count++];
+ if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) throw new UTFDataFormatException();
+ out[s++] = (char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F));
+ } else {
+ throw new UTFDataFormatException();
+ }
+ }
+ return new String(out, 0, s);
+ }
+
+ public static String propertiesToString(Properties props) throws IOException {
+ String result = "";
+ if (props != null) {
+ DataByteArrayOutputStream dataOut = new DataByteArrayOutputStream();
+ props.store(dataOut, "");
+ result = new String(dataOut.getData(), 0, dataOut.size());
+ dataOut.close();
+ }
+ return result;
+ }
+
+ public static Properties stringToProperties(String str) throws IOException {
+ Properties result = new Properties();
+ if (str != null && str.length() > 0) {
+ DataByteArrayInputStream dataIn = new DataByteArrayInputStream(str.getBytes());
+ result.load(dataIn);
+ dataIn.close();
+ }
+ return result;
+ }
+
+ public static String truncate64(String text) {
+ if (text.length() > 63) {
+ text = text.substring(0, 45) + "..." + text.substring(text.length() - 12);
+ }
+ return text;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/util/StringArrayConverter.java b/activemq-filters/src/main/java/org/apache/activemq/util/StringArrayConverter.java
new file mode 100644
index 00000000..a62254b2
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/util/StringArrayConverter.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.util;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+/**
+ * Class for converting to/from String[] to be used instead of a {@link java.beans.PropertyEditor}
+ * which otherwise causes memory leaks as the JDK {@link java.beans.PropertyEditorManager} is a
+ * static class and has strong references to classes, causing problems in hot-deployment
+ * environments.
+ */
+public class StringArrayConverter {
+
+ public static String[] convertToStringArray(Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ String text = value.toString();
+ if (text == null || text.length() == 0) {
+ return null;
+ }
+
+ StringTokenizer stok = new StringTokenizer(text, ",");
+ final List list = new ArrayList();
+
+ while (stok.hasMoreTokens()) {
+ list.add(stok.nextToken());
+ }
+
+ String[] array = list.toArray(new String[list.size()]);
+ return array;
+ }
+
+ public static String convertToString(String[] value) {
+ if (value == null || value.length == 0) {
+ return null;
+ }
+
+ StringBuffer result = new StringBuffer(String.valueOf(value[0]));
+ for (int i = 1; i < value.length; i++) {
+ result.append(",").append(value[i]);
+ }
+
+ return result.toString();
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/util/StringToListOfActiveMQDestinationConverter.java b/activemq-filters/src/main/java/org/apache/activemq/util/StringToListOfActiveMQDestinationConverter.java
new file mode 100644
index 00000000..920f714a
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/util/StringToListOfActiveMQDestinationConverter.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.util;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.activemq.command.ActiveMQDestination;
+
+/**
+ * Special converter for String -> List of ActiveMQDestination to be used instead of a {@link
+ * java.beans.PropertyEditor} which otherwise causes memory leaks as the JDK {@link
+ * java.beans.PropertyEditorManager} is a static class and has strong references to classes, causing
+ * problems in hot-deployment environments.
+ */
+public class StringToListOfActiveMQDestinationConverter {
+
+ public static List convertToActiveMQDestination(Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ // text must be enclosed with []
+
+ String text = value.toString();
+ if (text.startsWith("[") && text.endsWith("]")) {
+ text = text.substring(1, text.length() - 1).trim();
+
+ if (text.isEmpty()) {
+ return null;
+ }
+
+ String[] array = text.split(",");
+
+ List list = new ArrayList();
+ for (String item : array) {
+ list.add(
+ ActiveMQDestination.createDestination(item.trim(), ActiveMQDestination.QUEUE_TYPE));
+ }
+
+ return list;
+ } else {
+ return null;
+ }
+ }
+
+ public static String convertFromActiveMQDestination(Object value) {
+ return convertFromActiveMQDestination(value, false);
+ }
+
+ public static String convertFromActiveMQDestination(Object value, boolean includeOptions) {
+ if (value == null) {
+ return null;
+ }
+
+ StringBuilder sb = new StringBuilder("[");
+ if (value instanceof List) {
+ List list = (List) value;
+ for (int i = 0; i < list.size(); i++) {
+ Object e = list.get(i);
+ if (e instanceof ActiveMQDestination) {
+ ActiveMQDestination destination = (ActiveMQDestination) e;
+ if (includeOptions && destination.getOptions() != null) {
+ try {
+ // Reapply the options as URI parameters
+ sb.append(
+ destination.toString()
+ + URISupport.applyParameters(new URI(""), destination.getOptions()));
+ } catch (URISyntaxException e1) {
+ sb.append(destination);
+ }
+ } else {
+ sb.append(destination);
+ }
+ if (i < list.size() - 1) {
+ sb.append(", ");
+ }
+ }
+ }
+ }
+ sb.append("]");
+
+ if (sb.length() > 2) {
+ return sb.toString();
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/util/TypeConversionSupport.java b/activemq-filters/src/main/java/org/apache/activemq/util/TypeConversionSupport.java
new file mode 100644
index 00000000..43335b77
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/util/TypeConversionSupport.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.util;
+
+import java.math.BigInteger;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.activemq.command.ActiveMQDestination;
+import org.fusesource.hawtbuf.UTF8Buffer;
+
+/** Type conversion support for ActiveMQ. */
+public final class TypeConversionSupport {
+
+ private static final Converter IDENTITY_CONVERTER =
+ new Converter() {
+ @Override
+ public Object convert(Object value) {
+ return value;
+ }
+ };
+
+ private static class ConversionKey {
+ final Class> from;
+ final Class> to;
+ final int hashCode;
+
+ public ConversionKey(Class> from, Class> to) {
+ this.from = from;
+ this.to = to;
+ this.hashCode = from.hashCode() ^ (to.hashCode() << 1);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ ConversionKey other = (ConversionKey) obj;
+ if (from == null) {
+ if (other.from != null) return false;
+ } else if (!from.equals(other.from)) return false;
+ if (to == null) {
+ if (other.to != null) return false;
+ } else if (!to.equals(other.to)) return false;
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+ }
+
+ public interface Converter {
+ Object convert(Object value);
+ }
+
+ private static final Map CONVERSION_MAP =
+ new HashMap();
+
+ static {
+ Converter toStringConverter =
+ new Converter() {
+ @Override
+ public Object convert(Object value) {
+ return value.toString();
+ }
+ };
+ CONVERSION_MAP.put(new ConversionKey(Boolean.class, String.class), toStringConverter);
+ CONVERSION_MAP.put(new ConversionKey(Byte.class, String.class), toStringConverter);
+ CONVERSION_MAP.put(new ConversionKey(Short.class, String.class), toStringConverter);
+ CONVERSION_MAP.put(new ConversionKey(Integer.class, String.class), toStringConverter);
+ CONVERSION_MAP.put(new ConversionKey(Long.class, String.class), toStringConverter);
+ CONVERSION_MAP.put(new ConversionKey(Float.class, String.class), toStringConverter);
+ CONVERSION_MAP.put(new ConversionKey(Double.class, String.class), toStringConverter);
+ CONVERSION_MAP.put(new ConversionKey(UTF8Buffer.class, String.class), toStringConverter);
+ CONVERSION_MAP.put(new ConversionKey(URI.class, String.class), toStringConverter);
+ CONVERSION_MAP.put(new ConversionKey(BigInteger.class, String.class), toStringConverter);
+
+ CONVERSION_MAP.put(
+ new ConversionKey(String.class, Boolean.class),
+ new Converter() {
+ @Override
+ public Object convert(Object value) {
+ return Boolean.valueOf((String) value);
+ }
+ });
+ CONVERSION_MAP.put(
+ new ConversionKey(String.class, Byte.class),
+ new Converter() {
+ @Override
+ public Object convert(Object value) {
+ return Byte.valueOf((String) value);
+ }
+ });
+ CONVERSION_MAP.put(
+ new ConversionKey(String.class, Short.class),
+ new Converter() {
+ @Override
+ public Object convert(Object value) {
+ return Short.valueOf((String) value);
+ }
+ });
+ CONVERSION_MAP.put(
+ new ConversionKey(String.class, Integer.class),
+ new Converter() {
+ @Override
+ public Object convert(Object value) {
+ return Integer.valueOf((String) value);
+ }
+ });
+ CONVERSION_MAP.put(
+ new ConversionKey(String.class, Long.class),
+ new Converter() {
+ @Override
+ public Object convert(Object value) {
+ return Long.valueOf((String) value);
+ }
+ });
+ CONVERSION_MAP.put(
+ new ConversionKey(String.class, Float.class),
+ new Converter() {
+ @Override
+ public Object convert(Object value) {
+ return Float.valueOf((String) value);
+ }
+ });
+ CONVERSION_MAP.put(
+ new ConversionKey(String.class, Double.class),
+ new Converter() {
+ @Override
+ public Object convert(Object value) {
+ return Double.valueOf((String) value);
+ }
+ });
+
+ Converter longConverter =
+ new Converter() {
+ @Override
+ public Object convert(Object value) {
+ return ((Number) value).longValue();
+ }
+ };
+ CONVERSION_MAP.put(new ConversionKey(Byte.class, Long.class), longConverter);
+ CONVERSION_MAP.put(new ConversionKey(Short.class, Long.class), longConverter);
+ CONVERSION_MAP.put(new ConversionKey(Integer.class, Long.class), longConverter);
+ CONVERSION_MAP.put(
+ new ConversionKey(Date.class, Long.class),
+ new Converter() {
+ @Override
+ public Object convert(Object value) {
+ return ((Date) value).getTime();
+ }
+ });
+
+ Converter intConverter =
+ new Converter() {
+ @Override
+ public Object convert(Object value) {
+ return ((Number) value).intValue();
+ }
+ };
+ CONVERSION_MAP.put(new ConversionKey(Byte.class, Integer.class), intConverter);
+ CONVERSION_MAP.put(new ConversionKey(Short.class, Integer.class), intConverter);
+
+ CONVERSION_MAP.put(
+ new ConversionKey(Byte.class, Short.class),
+ new Converter() {
+ @Override
+ public Object convert(Object value) {
+ return ((Number) value).shortValue();
+ }
+ });
+
+ CONVERSION_MAP.put(
+ new ConversionKey(Float.class, Double.class),
+ new Converter() {
+ @Override
+ public Object convert(Object value) {
+ return ((Number) value).doubleValue();
+ }
+ });
+ CONVERSION_MAP.put(
+ new ConversionKey(String.class, ActiveMQDestination.class),
+ new Converter() {
+ @Override
+ public Object convert(Object value) {
+ return ActiveMQDestination.createDestination(
+ (String) value, ActiveMQDestination.QUEUE_TYPE);
+ }
+ });
+ CONVERSION_MAP.put(
+ new ConversionKey(String.class, URI.class),
+ new Converter() {
+ @Override
+ public Object convert(Object value) {
+ String text = value.toString();
+ try {
+ return new URI(text);
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ }
+
+ private TypeConversionSupport() {}
+
+ public static Object convert(Object value, Class> to) {
+ if (value == null) {
+ // lets avoid NullPointerException when converting to boolean for null values
+ if (boolean.class.isAssignableFrom(to)) {
+ return Boolean.FALSE;
+ }
+ return null;
+ }
+
+ // eager same instance type test to avoid the overhead of invoking the type converter
+ // if already same type
+ if (to.isInstance(value)) {
+ return to.cast(value);
+ }
+
+ // lookup converter
+ Converter c = lookupConverter(value.getClass(), to);
+ if (c != null) {
+ return c.convert(value);
+ } else {
+ return null;
+ }
+ }
+
+ public static Converter lookupConverter(Class> from, Class> to) {
+ // use wrapped type for primitives
+ if (from.isPrimitive()) {
+ from = convertPrimitiveTypeToWrapperType(from);
+ }
+ if (to.isPrimitive()) {
+ to = convertPrimitiveTypeToWrapperType(to);
+ }
+
+ if (from.equals(to)) {
+ return IDENTITY_CONVERTER;
+ }
+
+ return CONVERSION_MAP.get(new ConversionKey(from, to));
+ }
+
+ /** Converts primitive types such as int to its wrapper type like {@link Integer} */
+ private static Class> convertPrimitiveTypeToWrapperType(Class> type) {
+ Class> rc = type;
+ if (type.isPrimitive()) {
+ if (type == int.class) {
+ rc = Integer.class;
+ } else if (type == long.class) {
+ rc = Long.class;
+ } else if (type == double.class) {
+ rc = Double.class;
+ } else if (type == float.class) {
+ rc = Float.class;
+ } else if (type == short.class) {
+ rc = Short.class;
+ } else if (type == byte.class) {
+ rc = Byte.class;
+ } else if (type == boolean.class) {
+ rc = Boolean.class;
+ }
+ }
+ return rc;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/util/URISupport.java b/activemq-filters/src/main/java/org/apache/activemq/util/URISupport.java
new file mode 100644
index 00000000..d63b2a98
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/util/URISupport.java
@@ -0,0 +1,565 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.util;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Utility class that provides methods for parsing URI's
+ *
+ * This class can be used to split composite URI's into their component parts and is used to
+ * extract any URI options from each URI in order to set specific properties on Beans.
+ */
+public class URISupport {
+
+ /**
+ * A composite URI can be split into one or more CompositeData object which each represent the
+ * individual URIs that comprise the composite one.
+ */
+ public static class CompositeData {
+ private String host;
+ private String scheme;
+ private String path;
+ private URI components[];
+ private Map parameters;
+ private String fragment;
+
+ public URI[] getComponents() {
+ return components;
+ }
+
+ public String getFragment() {
+ return fragment;
+ }
+
+ public Map getParameters() {
+ return parameters;
+ }
+
+ public String getScheme() {
+ return scheme;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public URI toURI() throws URISyntaxException {
+ StringBuffer sb = new StringBuffer();
+ if (scheme != null) {
+ sb.append(scheme);
+ sb.append(':');
+ }
+
+ if (host != null && host.length() != 0) {
+ sb.append(host);
+ } else {
+ sb.append('(');
+ for (int i = 0; i < components.length; i++) {
+ if (i != 0) {
+ sb.append(',');
+ }
+ sb.append(components[i].toString());
+ }
+ sb.append(')');
+ }
+
+ if (path != null) {
+ sb.append('/');
+ sb.append(path);
+ }
+ if (!parameters.isEmpty()) {
+ sb.append("?");
+ sb.append(createQueryString(parameters));
+ }
+ if (fragment != null) {
+ sb.append("#");
+ sb.append(fragment);
+ }
+ return new URI(sb.toString());
+ }
+ }
+
+ /**
+ * Give a URI break off any URI options and store them in a Key / Value Mapping.
+ *
+ * @param uri The URI whose query should be extracted and processed.
+ * @return A Mapping of the URI options.
+ * @throws URISyntaxException
+ */
+ public static Map parseQuery(String uri) throws URISyntaxException {
+ try {
+ uri = uri.substring(uri.lastIndexOf("?") + 1); // get only the relevant part of the query
+ Map rc = new HashMap();
+ if (uri != null && !uri.isEmpty()) {
+ String[] parameters = uri.split("&");
+ for (int i = 0; i < parameters.length; i++) {
+ int p = parameters[i].indexOf("=");
+ if (p >= 0) {
+ String name = URLDecoder.decode(parameters[i].substring(0, p), "UTF-8");
+ String value = URLDecoder.decode(parameters[i].substring(p + 1), "UTF-8");
+ rc.put(name, value);
+ } else {
+ rc.put(parameters[i], null);
+ }
+ }
+ }
+ return rc;
+ } catch (UnsupportedEncodingException e) {
+ throw (URISyntaxException)
+ new URISyntaxException(e.toString(), "Invalid encoding").initCause(e);
+ }
+ }
+
+ /**
+ * Given a URI parse and extract any URI query options and return them as a Key / Value mapping.
+ *
+ * This method differs from the parseQuery method in that it handles composite URI types and
+ * will extract the URI options from the outermost composite URI.
+ *
+ * @param uri The URI whose query should be extracted and processed.
+ * @return A Mapping of the URI options.
+ * @throws URISyntaxException
+ */
+ public static Map parseParameters(URI uri) throws URISyntaxException {
+ if (!isCompositeURI(uri)) {
+ return uri.getQuery() == null ? emptyMap() : parseQuery(stripPrefix(uri.getQuery(), "?"));
+ } else {
+ CompositeData data = URISupport.parseComposite(uri);
+ Map parameters = new HashMap();
+ parameters.putAll(data.getParameters());
+ if (parameters.isEmpty()) {
+ parameters = emptyMap();
+ }
+
+ return parameters;
+ }
+ }
+
+ /**
+ * Given a Key / Value mapping create and append a URI query value that represents the mapped
+ * entries, return the newly updated URI that contains the value of the given URI and the appended
+ * query value.
+ *
+ * @param uri The source URI that will have the Map entries appended as a URI query value.
+ * @param queryParameters The Key / Value mapping that will be transformed into a URI query
+ * string.
+ * @return A new URI value that combines the given URI and the constructed query string.
+ * @throws URISyntaxException
+ */
+ public static URI applyParameters(URI uri, Map queryParameters)
+ throws URISyntaxException {
+ return applyParameters(uri, queryParameters, "");
+ }
+
+ /**
+ * Given a Key / Value mapping create and append a URI query value that represents the mapped
+ * entries, return the newly updated URI that contains the value of the given URI and the appended
+ * query value. Each entry in the query string is prefixed by the supplied optionPrefix string.
+ *
+ * @param uri The source URI that will have the Map entries appended as a URI query value.
+ * @param queryParameters The Key / Value mapping that will be transformed into a URI query
+ * string.
+ * @param optionPrefix A string value that when not null or empty is used to prefix each query
+ * option key.
+ * @return A new URI value that combines the given URI and the constructed query string.
+ * @throws URISyntaxException
+ */
+ public static URI applyParameters(
+ URI uri, Map queryParameters, String optionPrefix) throws URISyntaxException {
+ if (queryParameters != null && !queryParameters.isEmpty()) {
+ StringBuffer newQuery =
+ uri.getRawQuery() != null ? new StringBuffer(uri.getRawQuery()) : new StringBuffer();
+ for (Map.Entry param : queryParameters.entrySet()) {
+ if (param.getKey().startsWith(optionPrefix)) {
+ if (newQuery.length() != 0) {
+ newQuery.append('&');
+ }
+ final String key = param.getKey().substring(optionPrefix.length());
+ newQuery.append(key).append('=').append(param.getValue());
+ }
+ }
+ uri = createURIWithQuery(uri, newQuery.toString());
+ }
+ return uri;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Map emptyMap() {
+ return Collections.EMPTY_MAP;
+ }
+
+ /**
+ * Removes any URI query from the given uri and return a new URI that does not contain the query
+ * portion.
+ *
+ * @param uri The URI whose query value is to be removed.
+ * @return a new URI that does not contain a query value.
+ * @throws URISyntaxException
+ */
+ public static URI removeQuery(URI uri) throws URISyntaxException {
+ return createURIWithQuery(uri, null);
+ }
+
+ /**
+ * Creates a URI with the given query, removing an previous query value from the given URI.
+ *
+ * @param uri The source URI whose existing query is replaced with the newly supplied one.
+ * @param query The new URI query string that should be appended to the given URI.
+ * @return a new URI that is a combination of the original URI and the given query string.
+ * @throws URISyntaxException
+ */
+ public static URI createURIWithQuery(URI uri, String query) throws URISyntaxException {
+ String schemeSpecificPart = uri.getRawSchemeSpecificPart();
+ // strip existing query if any
+ int questionMark = schemeSpecificPart.lastIndexOf("?");
+ // make sure question mark is not within parentheses
+ if (questionMark < schemeSpecificPart.lastIndexOf(")")) {
+ questionMark = -1;
+ }
+ if (questionMark > 0) {
+ schemeSpecificPart = schemeSpecificPart.substring(0, questionMark);
+ }
+ if (query != null && query.length() > 0) {
+ schemeSpecificPart += "?" + query;
+ }
+ return new URI(uri.getScheme(), schemeSpecificPart, uri.getFragment());
+ }
+
+ /**
+ * Given a composite URI, parse the individual URI elements contained within that URI and return a
+ * CompsoteData instance that contains the parsed URI values.
+ *
+ * @param uri The target URI that should be parsed.
+ * @return a new CompsiteData instance representing the parsed composite URI.
+ * @throws URISyntaxException
+ */
+ public static CompositeData parseComposite(URI uri) throws URISyntaxException {
+
+ CompositeData rc = new CompositeData();
+ rc.scheme = uri.getScheme();
+ String ssp = stripPrefix(uri.getRawSchemeSpecificPart().trim(), "//").trim();
+
+ parseComposite(uri, rc, ssp);
+
+ rc.fragment = uri.getFragment();
+ return rc;
+ }
+
+ /**
+ * Examine a URI and determine if it is a Composite type or not.
+ *
+ * @param uri The URI that is to be examined.
+ * @return true if the given URI is a Compsote type.
+ */
+ public static boolean isCompositeURI(URI uri) {
+ String ssp = stripPrefix(uri.getRawSchemeSpecificPart().trim(), "//").trim();
+
+ if (ssp.indexOf('(') == 0 && checkParenthesis(ssp)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Given a string and a position in that string of an open parend, find the matching close parend.
+ *
+ * @param str The string to be searched for a matching parend.
+ * @param first The index in the string of the opening parend whose close value is to be searched.
+ * @return the index in the string where the closing parend is located.
+ * @throws URISyntaxException fi the string does not contain a matching parend.
+ */
+ public static int indexOfParenthesisMatch(String str, int first) throws URISyntaxException {
+ int index = -1;
+
+ if (first < 0 || first > str.length()) {
+ throw new IllegalArgumentException("Invalid position for first parenthesis: " + first);
+ }
+
+ if (str.charAt(first) != '(') {
+ throw new IllegalArgumentException("character at indicated position is not a parenthesis");
+ }
+
+ int depth = 1;
+ char[] array = str.toCharArray();
+ for (index = first + 1; index < array.length; ++index) {
+ char current = array[index];
+ if (current == '(') {
+ depth++;
+ } else if (current == ')') {
+ if (--depth == 0) {
+ break;
+ }
+ }
+ }
+
+ if (depth != 0) {
+ throw new URISyntaxException(str, "URI did not contain a matching parenthesis.");
+ }
+
+ return index;
+ }
+
+ /**
+ * Given a composite URI and a CompositeData instance and the scheme specific part extracted from
+ * the source URI, parse the composite URI and populate the CompositeData object with the results.
+ * The source URI is used only for logging as the ssp should have already been extracted from it
+ * and passed here.
+ *
+ * @param uri The original source URI whose ssp is parsed into the composite data.
+ * @param rc The CompsositeData instance that will be populated from the given ssp.
+ * @param ssp The scheme specific part from the original string that is a composite or one or more
+ * URIs.
+ * @throws URISyntaxException
+ */
+ private static void parseComposite(URI uri, CompositeData rc, String ssp)
+ throws URISyntaxException {
+ String componentString;
+ String params;
+
+ if (!checkParenthesis(ssp)) {
+ throw new URISyntaxException(
+ uri.toString(), "Not a matching number of '(' and ')' parenthesis");
+ }
+
+ int p;
+ int initialParen = ssp.indexOf("(");
+ if (initialParen == 0) {
+
+ rc.host = ssp.substring(0, initialParen);
+ p = rc.host.indexOf("/");
+
+ if (p >= 0) {
+ rc.path = rc.host.substring(p);
+ rc.host = rc.host.substring(0, p);
+ }
+
+ p = indexOfParenthesisMatch(ssp, initialParen);
+ componentString = ssp.substring(initialParen + 1, p);
+ params = ssp.substring(p + 1).trim();
+
+ } else {
+ componentString = ssp;
+ params = "";
+ }
+
+ String components[] = splitComponents(componentString);
+ rc.components = new URI[components.length];
+ for (int i = 0; i < components.length; i++) {
+ rc.components[i] = new URI(components[i].trim());
+ }
+
+ p = params.indexOf("?");
+ if (p >= 0) {
+ if (p > 0) {
+ rc.path = stripPrefix(params.substring(0, p), "/");
+ }
+ rc.parameters = parseQuery(params.substring(p + 1));
+ } else {
+ if (params.length() > 0) {
+ rc.path = stripPrefix(params, "/");
+ }
+ rc.parameters = emptyMap();
+ }
+ }
+
+ /**
+ * Given the inner portion of a composite URI, split and return each inner URI as a string element
+ * in a new String array.
+ *
+ * @param str The inner URI elements of a composite URI string.
+ * @return an array containing each inner URI from the composite one.
+ */
+ private static String[] splitComponents(String str) {
+ List l = new ArrayList();
+
+ int last = 0;
+ int depth = 0;
+ char chars[] = str.toCharArray();
+ for (int i = 0; i < chars.length; i++) {
+ switch (chars[i]) {
+ case '(':
+ depth++;
+ break;
+ case ')':
+ depth--;
+ break;
+ case ',':
+ if (depth == 0) {
+ String s = str.substring(last, i);
+ l.add(s);
+ last = i + 1;
+ }
+ break;
+ default:
+ }
+ }
+
+ String s = str.substring(last);
+ if (s.length() != 0) {
+ l.add(s);
+ }
+
+ String rc[] = new String[l.size()];
+ l.toArray(rc);
+ return rc;
+ }
+
+ /**
+ * String the given prefix from the target string and return the result.
+ *
+ * @param value The string that should be trimmed of the given prefix if present.
+ * @param prefix The prefix to remove from the target string.
+ * @return either the original string or a new string minus the supplied prefix if present.
+ */
+ public static String stripPrefix(String value, String prefix) {
+ if (value.startsWith(prefix)) {
+ return value.substring(prefix.length());
+ }
+ return value;
+ }
+
+ /**
+ * Strip a URI of its scheme element.
+ *
+ * @param uri The URI whose scheme value should be stripped.
+ * @return The stripped URI value.
+ * @throws URISyntaxException
+ */
+ public static URI stripScheme(URI uri) throws URISyntaxException {
+ return new URI(stripPrefix(uri.getSchemeSpecificPart().trim(), "//"));
+ }
+
+ /**
+ * Given a key / value mapping, create and return a URI formatted query string that is valid and
+ * can be appended to a URI.
+ *
+ * @param options The Mapping that will create the new Query string.
+ * @return a URI formatted query string.
+ * @throws URISyntaxException
+ */
+ public static String createQueryString(Map options)
+ throws URISyntaxException {
+ try {
+ if (options.size() > 0) {
+ StringBuffer rc = new StringBuffer();
+ boolean first = true;
+ for (String key : options.keySet()) {
+ if (first) {
+ first = false;
+ } else {
+ rc.append("&");
+ }
+ String value = (String) options.get(key);
+ rc.append(URLEncoder.encode(key, "UTF-8"));
+ rc.append("=");
+ rc.append(URLEncoder.encode(value, "UTF-8"));
+ }
+ return rc.toString();
+ } else {
+ return "";
+ }
+ } catch (UnsupportedEncodingException e) {
+ throw (URISyntaxException)
+ new URISyntaxException(e.toString(), "Invalid encoding").initCause(e);
+ }
+ }
+
+ /**
+ * Creates a URI from the original URI and the remaining parameters.
+ *
+ * When the query options of a URI are applied to certain objects the used portion of the query
+ * options needs to be removed and replaced with those that remain so that other parts of the code
+ * can attempt to apply the remainder or give an error is unknown values were given. This method
+ * is used to update a URI with those remainder values.
+ *
+ * @param originalURI The URI whose current parameters are remove and replaced with the given
+ * remainder value.
+ * @param params The URI params that should be used to replace the current ones in the target.
+ * @return a new URI that matches the original one but has its query options replaced with the
+ * given ones.
+ * @throws URISyntaxException
+ */
+ public static URI createRemainingURI(URI originalURI, Map params)
+ throws URISyntaxException {
+ String s = createQueryString(params);
+ if (s.length() == 0) {
+ s = null;
+ }
+ return createURIWithQuery(originalURI, s);
+ }
+
+ /**
+ * Given a URI value create and return a new URI that matches the target one but with the scheme
+ * value supplied to this method.
+ *
+ * @param bindAddr The URI whose scheme value should be altered.
+ * @param scheme The new scheme value to use for the returned URI.
+ * @return a new URI that is a copy of the original except that its scheme matches the supplied
+ * one.
+ * @throws URISyntaxException
+ */
+ public static URI changeScheme(URI bindAddr, String scheme) throws URISyntaxException {
+ return new URI(
+ scheme,
+ bindAddr.getUserInfo(),
+ bindAddr.getHost(),
+ bindAddr.getPort(),
+ bindAddr.getPath(),
+ bindAddr.getQuery(),
+ bindAddr.getFragment());
+ }
+
+ /**
+ * Examine the supplied string and ensure that all parends appear as matching pairs.
+ *
+ * @param str The target string to examine.
+ * @return true if the target string has valid parend pairings.
+ */
+ public static boolean checkParenthesis(String str) {
+ boolean result = true;
+ if (str != null) {
+ int open = 0;
+ int closed = 0;
+
+ int i = 0;
+ while ((i = str.indexOf('(', i)) >= 0) {
+ i++;
+ open++;
+ }
+ i = 0;
+ while ((i = str.indexOf(')', i)) >= 0) {
+ i++;
+ closed++;
+ }
+ result = open == closed;
+ }
+ return result;
+ }
+}
diff --git a/activemq-filters/src/main/java/org/apache/activemq/wireformat/WireFormat.java b/activemq-filters/src/main/java/org/apache/activemq/wireformat/WireFormat.java
new file mode 100644
index 00000000..353ce3bb
--- /dev/null
+++ b/activemq-filters/src/main/java/org/apache/activemq/wireformat/WireFormat.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright DataStax, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.wireformat;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import org.apache.activemq.util.ByteSequence;
+
+/**
+ * Provides a mechanism to marshal commands into and out of packets or into and out of streams,
+ * Channels and Datagrams.
+ */
+public interface WireFormat {
+
+ /** Packet based marshaling */
+ ByteSequence marshal(Object command) throws IOException;
+
+ /** Packet based un-marshaling */
+ Object unmarshal(ByteSequence packet) throws IOException;
+
+ /** Stream based marshaling */
+ void marshal(Object command, DataOutput out) throws IOException;
+
+ /** Packet based un-marshaling */
+ Object unmarshal(DataInput in) throws IOException;
+
+ /** @param version the version of the wire format */
+ void setVersion(int version);
+
+ /** @return the version of the wire format */
+ int getVersion();
+}
diff --git a/examples/spring/pom.xml b/examples/spring/pom.xml
index 4c9b364d..ad388418 100644
--- a/examples/spring/pom.xml
+++ b/examples/spring/pom.xml
@@ -30,6 +30,9 @@
Demo project for Spring Boot and Pulsar JMS
1.8
+ 17
+ 17
+ 17
diff --git a/pom.xml b/pom.xml
index dc5411ec..083db5ff 100644
--- a/pom.xml
+++ b/pom.xml
@@ -33,6 +33,7 @@
activemq-filters
pulsar-jms-filters
+ pulsar-jms-filters-common
pulsar-jms-admin-api
pulsar-jms
resource-adapter
@@ -50,9 +51,9 @@
UTF-8
1.8
8
- 17
- 17
- 17
+ 11
+ 11
+ 11
3.0.0
org.apache.pulsar
3.2.2
@@ -166,17 +167,6 @@
pulsar-broker
${pulsar.version}
-
- org.apache.activemq
- activemq-client
- ${activemq.version}
-
-
- *
- *
-
-
-
org.fusesource.hawtbuf
hawtbuf
diff --git a/pulsar-jms-all/pom.xml b/pulsar-jms-all/pom.xml
index 9bd0f0d4..2ec0b093 100644
--- a/pulsar-jms-all/pom.xml
+++ b/pulsar-jms-all/pom.xml
@@ -43,10 +43,6 @@
${pulsar.groupId}
pulsar-client-admin-original
-
- org.apache.activemq
- *
-
com.github.spotbugs
*
@@ -59,7 +55,7 @@
${project.groupId}
- pulsar-jms-filters
+ pulsar-jms-filters-common
${project.version}
diff --git a/pulsar-jms-filters-common/pom.xml b/pulsar-jms-filters-common/pom.xml
new file mode 100644
index 00000000..594fde00
--- /dev/null
+++ b/pulsar-jms-filters-common/pom.xml
@@ -0,0 +1,63 @@
+
+
+
+
+ pulsar-jms-parent
+ com.datastax.oss
+ 7.0.2-SNAPSHOT
+
+ 4.0.0
+ pulsar-jms-filters-common
+ jar
+ DataStax Starlight for JMS - Filters Common
+
+ ${project.build.directory}
+
+
+
+ ${project.groupId}
+ pulsar-jms-activemq-filters
+ ${project.version}
+
+
+ *
+ *
+
+
+
+
+ jakarta.jms
+ jakarta.jms-api
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.slf4j
+ slf4j-api
+
+
+
diff --git a/pulsar-jms-filters/src/main/java/com/datastax/oss/pulsar/jms/selectors/SelectorSupport.java b/pulsar-jms-filters-common/src/main/java/com/datastax/oss/pulsar/jms/selectors/SelectorSupport.java
similarity index 100%
rename from pulsar-jms-filters/src/main/java/com/datastax/oss/pulsar/jms/selectors/SelectorSupport.java
rename to pulsar-jms-filters-common/src/main/java/com/datastax/oss/pulsar/jms/selectors/SelectorSupport.java
diff --git a/pulsar-jms-filters/src/test/java/com/datastax/oss/pulsar/jms/selectors/SelectorSupportTest.java b/pulsar-jms-filters-common/src/test/java/com/datastax/oss/pulsar/jms/selectors/SelectorSupportTest.java
similarity index 100%
rename from pulsar-jms-filters/src/test/java/com/datastax/oss/pulsar/jms/selectors/SelectorSupportTest.java
rename to pulsar-jms-filters-common/src/test/java/com/datastax/oss/pulsar/jms/selectors/SelectorSupportTest.java
diff --git a/pulsar-jms-filters-common/src/test/resources/log4j2.xml b/pulsar-jms-filters-common/src/test/resources/log4j2.xml
new file mode 100644
index 00000000..67d0e660
--- /dev/null
+++ b/pulsar-jms-filters-common/src/test/resources/log4j2.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pulsar-jms-filters/pom.xml b/pulsar-jms-filters/pom.xml
index bfd8709a..ce92d020 100644
--- a/pulsar-jms-filters/pom.xml
+++ b/pulsar-jms-filters/pom.xml
@@ -28,22 +28,15 @@
DataStax Starlight for JMS - Broker Side Filters
${project.build.directory}
+ 17
+ 17
+ 17
${project.groupId}
- pulsar-jms-activemq-filters
+ pulsar-jms-filters-common
${project.version}
-
-
- *
- *
-
-
-
-
- jakarta.jms
- jakarta.jms-api
org.projectlombok
@@ -74,11 +67,6 @@
pulsar-broker
provided
-
- org.apache.activemq
- activemq-client
- provided
-
diff --git a/pulsar-jms-integration-tests/pom.xml b/pulsar-jms-integration-tests/pom.xml
index d381f4cf..15a86e7c 100644
--- a/pulsar-jms-integration-tests/pom.xml
+++ b/pulsar-jms-integration-tests/pom.xml
@@ -103,8 +103,8 @@
copy filters
-
-
+
+
diff --git a/pulsar-jms/pom.xml b/pulsar-jms/pom.xml
index d9b6b1a4..5b23b3e3 100644
--- a/pulsar-jms/pom.xml
+++ b/pulsar-jms/pom.xml
@@ -32,7 +32,7 @@