From be15cbdcc91c87b8ed1b73d170c1b69a83c9da39 Mon Sep 17 00:00:00 2001 From: sandeep-ctds Date: Tue, 7 Jan 2025 20:36:07 +0530 Subject: [PATCH] [STREAM-546] JMS 11 Support excluding ServerSideFiltering (#165) --- activemq-filters/pom.xml | 30 +- .../MaxFrameSizeExceededException.java | 27 + .../java/org/apache/activemq/Message.java | 27 + .../org/apache/activemq/ScheduledMessage.java | 71 + .../java/org/apache/activemq/Service.java | 29 + .../broker/region/MessageReference.java | 69 + .../activemq/command/ActiveMQDestination.java | 441 +++++ .../activemq/command/ActiveMQMessage.java | 840 +++++++++ .../activemq/command/ActiveMQQueue.java | 52 + .../command/ActiveMQTempDestination.java | 77 + .../activemq/command/ActiveMQTempQueue.java | 56 + .../activemq/command/ActiveMQTempTopic.java | 56 + .../activemq/command/ActiveMQTopic.java | 52 + .../apache/activemq/command/BaseCommand.java | 143 ++ .../org/apache/activemq/command/BrokerId.java | 64 + .../apache/activemq/command/BrokerInfo.java | 249 +++ .../org/apache/activemq/command/Command.java | 64 + .../apache/activemq/command/CommandTypes.java | 163 ++ .../apache/activemq/command/ConnectionId.java | 85 + .../apache/activemq/command/ConsumerId.java | 121 ++ .../activemq/command/DataStructure.java | 25 + ...faultUnresolvedDestinationTransformer.java | 56 + .../org/apache/activemq/command/Endpoint.java | 37 + .../activemq/command/MarshallAware.java | 30 + .../org/apache/activemq/command/Message.java | 807 +++++++++ .../apache/activemq/command/MessageId.java | 241 +++ .../apache/activemq/command/ProducerId.java | 132 ++ .../apache/activemq/command/ProducerInfo.java | 134 ++ .../apache/activemq/command/RemoveInfo.java | 74 + .../org/apache/activemq/command/Response.java | 43 + .../apache/activemq/command/SessionId.java | 108 ++ .../apache/activemq/command/SessionInfo.java | 50 + .../activemq/command/TransactionId.java | 29 + .../UnresolvedDestinationTransformer.java | 26 + .../filter/function/inListFunction.java | 2 - .../filter/function/makeListFunction.java | 2 - .../filter/function/regexMatchFunction.java | 2 - .../activemq/jndi/JNDIBaseStorable.java | 99 ++ .../activemq/jndi/JNDIReferenceFactory.java | 130 ++ .../activemq/jndi/JNDIStorableInterface.java | 37 + .../activemq/selector/ParseException.java | 201 +++ .../activemq/selector/SelectorParser.java | 1527 +++++++++++++++++ .../selector/SelectorParserConstants.java | 131 ++ .../selector/SelectorParserTokenManager.java | 1097 ++++++++++++ .../activemq/selector/SimpleCharStream.java | 445 +++++ .../org/apache/activemq/selector/Token.java | 132 ++ .../activemq/selector/TokenMgrError.java | 181 ++ .../activemq/usage/DefaultUsageCapacity.java | 41 + .../apache/activemq/usage/MemoryUsage.java | 209 +++ .../java/org/apache/activemq/usage/Usage.java | 482 ++++++ .../apache/activemq/usage/UsageCapacity.java | 34 + .../apache/activemq/usage/UsageListener.java | 20 + .../activemq/util/ByteArrayInputStream.java | 96 ++ .../activemq/util/ByteArrayOutputStream.java | 95 + .../apache/activemq/util/ByteSequence.java | 139 ++ .../org/apache/activemq/util/Callback.java | 31 + .../util/DataByteArrayInputStream.java | 265 +++ .../util/DataByteArrayOutputStream.java | 211 +++ .../activemq/util/IntrospectionSupport.java | 388 +++++ .../activemq/util/JMSExceptionSupport.java | 150 ++ .../org/apache/activemq/util/LRUCache.java | 82 + .../activemq/util/MarshallingSupport.java | 426 +++++ .../activemq/util/StringArrayConverter.java | 63 + ...gToListOfActiveMQDestinationConverter.java | 103 ++ .../activemq/util/TypeConversionSupport.java | 287 ++++ .../org/apache/activemq/util/URISupport.java | 565 ++++++ .../activemq/wireformat/WireFormat.java | 46 + examples/spring/pom.xml | 3 + pom.xml | 18 +- pulsar-jms-all/pom.xml | 6 +- pulsar-jms-filters-common/pom.xml | 63 + .../pulsar/jms/selectors/SelectorSupport.java | 0 .../jms/selectors/SelectorSupportTest.java | 0 .../src/test/resources/log4j2.xml | 37 + pulsar-jms-filters/pom.xml | 20 +- pulsar-jms-integration-tests/pom.xml | 4 +- pulsar-jms/pom.xml | 14 +- .../TemporaryDestinationsNonAdminTest.java | 2 +- 78 files changed, 12328 insertions(+), 66 deletions(-) create mode 100644 activemq-filters/src/main/java/org/apache/activemq/MaxFrameSizeExceededException.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/Message.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/ScheduledMessage.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/Service.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/broker/region/MessageReference.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQDestination.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQMessage.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQQueue.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQTempDestination.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQTempQueue.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQTempTopic.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/ActiveMQTopic.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/BaseCommand.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/BrokerId.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/BrokerInfo.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/Command.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/CommandTypes.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/ConnectionId.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/ConsumerId.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/DataStructure.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/DefaultUnresolvedDestinationTransformer.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/Endpoint.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/MarshallAware.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/Message.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/MessageId.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/ProducerId.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/ProducerInfo.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/RemoveInfo.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/Response.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/SessionId.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/SessionInfo.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/TransactionId.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/command/UnresolvedDestinationTransformer.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/jndi/JNDIBaseStorable.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/jndi/JNDIReferenceFactory.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/jndi/JNDIStorableInterface.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/selector/ParseException.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/selector/SelectorParser.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/selector/SelectorParserConstants.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/selector/SelectorParserTokenManager.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/selector/SimpleCharStream.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/selector/Token.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/selector/TokenMgrError.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/usage/DefaultUsageCapacity.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/usage/MemoryUsage.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/usage/Usage.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/usage/UsageCapacity.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/usage/UsageListener.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/util/ByteArrayInputStream.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/util/ByteArrayOutputStream.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/util/ByteSequence.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/util/Callback.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/util/DataByteArrayInputStream.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/util/DataByteArrayOutputStream.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/util/IntrospectionSupport.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/util/JMSExceptionSupport.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/util/LRUCache.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/util/MarshallingSupport.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/util/StringArrayConverter.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/util/StringToListOfActiveMQDestinationConverter.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/util/TypeConversionSupport.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/util/URISupport.java create mode 100644 activemq-filters/src/main/java/org/apache/activemq/wireformat/WireFormat.java create mode 100644 pulsar-jms-filters-common/pom.xml rename {pulsar-jms-filters => pulsar-jms-filters-common}/src/main/java/com/datastax/oss/pulsar/jms/selectors/SelectorSupport.java (100%) rename {pulsar-jms-filters => pulsar-jms-filters-common}/src/test/java/com/datastax/oss/pulsar/jms/selectors/SelectorSupportTest.java (100%) create mode 100644 pulsar-jms-filters-common/src/test/resources/log4j2.xml diff --git a/activemq-filters/pom.xml b/activemq-filters/pom.xml index 842e5113..63859e74 100644 --- a/activemq-filters/pom.xml +++ b/activemq-filters/pom.xml @@ -25,15 +25,11 @@ 4.0.0 pulsar-jms-activemq-filters jar - DataStax Starlight for JMS - Apache ActiveMQ Filters (waiting for next ActiveMQ release) + DataStax Starlight for JMS - Apache ActiveMQ Filters ${project.build.directory} - - org.apache.activemq - activemq-client - jakarta.jms jakarta.jms-api @@ -44,9 +40,24 @@ slf4j-api provided + + org.fusesource.hawtbuf + hawtbuf + ${hawtbuf.version} + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.2.0 + + + -Xdoclint:none + + + com.github.spotbugs spotbugs-maven-plugin @@ -58,14 +69,7 @@ org.apache.maven.plugins maven-shade-plugin - - - org.apache.activemq:* - - org/apache/activemq/filter/** - - - + 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 @@ ${project.groupId} - pulsar-jms-filters + pulsar-jms-filters-common ${project.version} @@ -92,10 +92,6 @@ com.fasterxml.jackson.core jackson-annotations - - org.apache.activemq - activemq-client - org.fusesource.hawtbuf hawtbuf @@ -155,10 +151,10 @@ copy filters - - - - + + + + diff --git a/pulsar-jms/src/test/java/com/datastax/oss/pulsar/jms/TemporaryDestinationsNonAdminTest.java b/pulsar-jms/src/test/java/com/datastax/oss/pulsar/jms/TemporaryDestinationsNonAdminTest.java index f163d4b7..34bf69ab 100644 --- a/pulsar-jms/src/test/java/com/datastax/oss/pulsar/jms/TemporaryDestinationsNonAdminTest.java +++ b/pulsar-jms/src/test/java/com/datastax/oss/pulsar/jms/TemporaryDestinationsNonAdminTest.java @@ -19,7 +19,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import com.datastax.oss.pulsar.jms.utils.PulsarContainerExtension; -import java.util.Map; import jakarta.jms.Connection; import jakarta.jms.Destination; import jakarta.jms.JMSException; @@ -27,6 +26,7 @@ import jakarta.jms.MessageConsumer; import jakarta.jms.MessageProducer; import jakarta.jms.Session; +import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test;