diff options
Diffstat (limited to 'Final/java/common')
106 files changed, 14878 insertions, 0 deletions
diff --git a/Final/java/common/bin/qpid-run b/Final/java/common/bin/qpid-run new file mode 100644 index 0000000000..c9e37b21a1 --- /dev/null +++ b/Final/java/common/bin/qpid-run @@ -0,0 +1,238 @@ +#!/bin/bash +# +# 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. +# + +# Test if we're running on cygwin. +cygwin=false +if [[ "$(uname -a | fgrep Cygwin)" != "" ]]; then + cygwin=true +fi + +die() { + if [[ $1 = -usage ]]; then + shift + usage=true + else + usage=false + fi + echo "$@" + $usage && echo + $usage && usage + exit 1 +} + +if [ -z $AMQJ_LOGGING_LEVEL ]; then + export AMQJ_LOGGING_LEVEL=info +fi + +if [ -z "$QPID_HOME" ]; then + die "QPID_HOME must be set" +fi + +if [ -z "$QPID_WORK" ]; then + echo Setting QPID_WORK to $HOME as default + QPID_WORK=$HOME +fi + +if $cygwin; then + QPID_HOME=$(cygpath -w $QPID_HOME) + QPID_WORK=$(cygpath -w $QPID_WORK) +fi + +#Set the default system properties that we'll use now that they have +#all been initialised +SYSTEM_PROPS="-Damqj.logging.level=$AMQJ_LOGGING_LEVEL -DQPID_HOME=$QPID_HOME -DQPID_WORK=$QPID_WORK" + +#If logprefix or logsuffix set to use PID make that happen +#Otherwise just pass the value through for these props +#Using X character to avoid probs with empty strings +if [ -n "$QPID_LOG_PREFIX" ]; then + if [ "X$QPID_LOG_PREFIX" = "XPID" ]; then + echo Using pid in qpid log name prefix + LOG_PREFIX=" -Dlogprefix=$$" + else + echo Using qpid logprefix property + LOG_PREFIX=" -Dlogprefix=$QPID_LOG_PREFIX" + fi + SYSTEM_PROPS="${SYSTEM_PROPS} ${LOG_PREFIX}" +fi + +if [ -n "$QPID_LOG_SUFFIX" ]; then + if [ "X$QPID_LOG_SUFFIX" = "XPID" ]; then + echo Using pid in qpid log name suffix + LOG_SUFFIX=" -Dlogsuffix=$$" + else + echo Using qpig logsuffix property + LOG_SUFFIX=" -Dlogsuffix=$QPID_LOG_SUFFIX" + fi + SYSTEM_PROPS="${SYSTEM_PROPS} ${LOG_SUFFIX}" +fi + +echo System Properties set to $SYSTEM_PROPS + +program=$(basename $0) +sourced=${BASH_SOURCE[0]} +if [[ -z ${sourced:-''} ]]; then + sourced=$(which qpid-run) || ${QPID_HOME}/bin/qpid-run +fi + +usage() { + echo Usage: $program ... "[-run:<option>]" ... + echo + echo Options: + egrep -B 1 "^\s*#USAGE: " ${sourced} |\ + sed "s/#USAGE:/ /" |\ + sed "s/-run:\(.*\))/-run:\1/" |\ + sed "s/-run:\(.*\)=\*/-run:\1=<value>/" |\ + sed "s/^--$//" +} + +export EXTERNAL_CLASSPATH=$CLASSPATH +unset CLASSPATH + +#Use QPID_CLASSPATH if set +if [ -n "$QPID_CLASSPATH" ]; then + export CLASSPATH=$QPID_CLASSPATH + echo "Using QPID_CLASSPATH" $QPID_CLASSPATH +else + echo "Warning: Qpid classpath not set. CLASSPATH must include qpid jars." +fi + +#Use QPID_JAVA_GC if set +if [ -n "$QPID_JAVA_GC" ]; then + export JAVA_GC=$QPID_JAVA_GC + echo "Using QPID_JAVA_GC setting" $QPID_JAVA_GC +else + echo "Info: QPID_JAVA_GC not set. Defaulting to JAVA_GC" $JAVA_GC +fi + + +#Use QPID_JAVA_MEM if set +if [ -n "$QPID_JAVA_MEM" ]; then + export JAVA_MEM=$QPID_JAVA_MEM + echo "Using QPID_JAVA_MEM setting" $QPID_JAVA_MEM +else + echo "Info: QPID_JAVA_MEM not set. Defaulting to JAVA_MEM" $JAVA_MEM +fi + +declare -a RUN_ARGS JAVA_ARGS +for arg in "$@"; do + if [[ $arg == -run:* ]]; then + RUN_ARGS[${#RUN_ARGS[@]}]="$arg" + else + JAVA_ARGS[${#JAVA_ARGS[@]}]="$arg" + fi +done + +# this defines the default behavior, it may be modified during option +# processing below +DISPATCH() { + if $debug; then + echo "CLASSPATH=${CLASSPATH}" + echo "${COMMAND[@]}" + fi + + exec "${COMMAND[@]}" +} + +exclusive() { + if [ -z "$PREVIOUS_ARGS" ]; then + PREVIOUS_ARGS=$1 + else + PREVIOUS_ARGS="${PREVIOUS_ARGS}, $1" + DISPATCH() { + die -usage "you must choose one of: $PREVIOUS_ARGS" + } + fi +} + +debug=false + +for arg in "${RUN_ARGS[@]}"; do + case $arg in + -run:debug) +#USAGE: print the classpath and command before running it + debug=true + ;; + -run:jpda) +#USAGE: adds debugging options to the java command, use +#USAGE: JDPA_TRANSPORT and JPDA_ADDRESS to customize the debugging +#USAGE: behavior and use JPDA_OPTS to override it entirely + if [ -z "$JPDA_OPTS" ]; then + JPDA_OPTS="-Xdebug -Xrunjdwp:transport=${JPDA_TRANSPORT:-dt_socket},address=${JPDA_ADDRESS:-8000},server=y,suspend=n" + fi + QPID_OPTS="${QPID_OPTS} ${JPDA_OPTS}" + ;; + -run:external-classpath=*) +#USAGE: controls how the CLASSPATH environment variable is used by +#USAGE: this script, value can be one of ignore (the default), first, +#USAGE: last, and only + case $arg in + *=ignore) + # do nothing + ;; + *=first) + CLASSPATH=$EXTERNAL_CLASSPATH:$CLASSPATH + ;; + *=last) + CLASSPATH=$CLASSPATH:$EXTERNAL_CLASSPATH + ;; + *=only) + CLASSPATH=$EXTERNAL_CLASSPATH + ;; + *) + die -usage $(echo $arg | sed "s/=/: invalid value '/")\' + ;; + esac + ;; + -run:print-classpath) +#USAGE: print the classpath + DISPATCH() { + echo $CLASSPATH + } + exclusive $arg + ;; + -run:print-command) +#USAGE: print the command + DISPATCH() { + echo "${COMMAND[@]}" + } + exclusive $arg + ;; + -run:help) +#USAGE: print this message + DISPATCH() { + usage + } + exclusive $arg + ;; + *) + die -usage "unrecognized -run option '$arg'" + ;; + esac +done + +if $cygwin; then + CLASSPATH=$(cygpath -w -p $CLASSPATH) + JAVA=$(cygpath -u $JAVA) +fi + +COMMAND=($JAVA $JAVA_VM $JAVA_GC $JAVA_MEM $SYSTEM_PROPS $JAVA_OPTS $QPID_OPTS "${JAVA_ARGS[@]}") + +DISPATCH diff --git a/Final/java/common/etc/qpid-run.conf b/Final/java/common/etc/qpid-run.conf new file mode 100644 index 0000000000..b9765fe3ce --- /dev/null +++ b/Final/java/common/etc/qpid-run.conf @@ -0,0 +1,25 @@ +# +# 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. +# + +QPID_LIBS=$(find ${QPID_HOME}/lib -name "*.jar" -print | sed -e :a -e '$!N;s/\n/:/;ta' -e 'P;D') + +export JAVA=java \ + JAVA_VM=-server \ + JAVA_MEM=-Xmx1024m \ + CLASSPATH=$QPID_LIBS diff --git a/Final/java/common/etc/qpid-run.conf.dev b/Final/java/common/etc/qpid-run.conf.dev new file mode 100644 index 0000000000..a5419eb4e8 --- /dev/null +++ b/Final/java/common/etc/qpid-run.conf.dev @@ -0,0 +1,26 @@ +# +# 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. +# + +QPID_LIBS=$(find ${QPID_HOME} -type d -name "classes" -print | sed -e :a -e '$!N;s/\n/:/;ta' -e 'P;D') +QPID_LIBS=${QPID_LIBS}:$(find $(dirname ${QPID_HOME}) -name "*.jar" -print | sed -e :a -e '$!N;s/\n/:/;ta' -e 'P;D') + +export JAVA=java \ + JAVA_VM=-server \ + JAVA_MEM=-Xmx1024m \ + CLASSPATH=$QPID_LIBS diff --git a/Final/java/common/pom.xml b/Final/java/common/pom.xml new file mode 100644 index 0000000000..b08982fde3 --- /dev/null +++ b/Final/java/common/pom.xml @@ -0,0 +1,144 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.apache.qpid</groupId> + <artifactId>qpid-common</artifactId> + <packaging>jar</packaging> + <version>1.0-incubating-M2</version> + <name>Qpid Common Utilities</name> + <url>http://cwiki.apache.org/confluence/display/qpid</url> + + <parent> + <groupId>org.apache.qpid</groupId> + <artifactId>qpid</artifactId> + <version>1.0-incubating-M2</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <properties> + <topDirectoryLocation>..</topDirectoryLocation> + <gentools.home>${topDirectoryLocation}/../gentools</gentools.home> + <generated.path>${project.build.directory}/generated-sources/gentools</generated.path> + <generated.package>org/apache/qpid/framing</generated.package> + <generated.dir>${generated.path}/${generated.package}</generated.dir> + <generated.timestamp>${generated.dir}/timestamp</generated.timestamp> + <specs.dir>${topDirectoryLocation}/../specs</specs.dir> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-antrun-plugin</artifactId> + <executions> + <execution> + <id>protocol-version</id> + <phase>generate-sources</phase> + <configuration> + <tasks> + <ant antfile="protocol-version.xml" /> + </tasks> + <sourceRoot>${generated.path}</sourceRoot> + </configuration> + <goals> + <goal>run</goal> + </goals> + </execution> + </executions> + </plugin> + + <!-- Backports the module to Java 1.4. This is done during the packaging phase as a transformation of the Jar. --> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>retrotranslator-maven-plugin</artifactId> + <executions> + <execution> + <id>retro-common</id> + <goals> + <goal>translate-project</goal> + </goals> + <configuration> + <destjar>${project.build.directory}/${project.build.finalName}-java14.jar</destjar> + <verify>${retrotranslator.verify}</verify> + <verifyClasspath> + <element>${retrotranslator.1.4-rt-path}</element> + <element>${retrotranslator.1.4-jce-path}</element> + <element>${retrotranslator.1.4-jsse-path}</element> + <element>${retrotranslator.1.4-sasl-path}</element> + </verifyClasspath> + <failonwarning>false</failonwarning> + <classifier>java14</classifier> + <attach>true</attach> + </configuration> + </execution> + + </executions> + </plugin> + + </plugins> + </build> + + <dependencies> + + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <version>1.4.0</version> + </dependency> + + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + <version>1.4.0</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.apache.mina</groupId> + <artifactId>mina-java5</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.mina</groupId> + <artifactId>mina-filter-ssl</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.mina</groupId> + <artifactId>mina-core</artifactId> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + + <!-- This needs to be included at compile time, for the retrotranslator verification to find it. --> + <dependency> + <groupId>net.sf.retrotranslator</groupId> + <artifactId>retrotranslator-runtime</artifactId> + <scope>provided</scope> + </dependency> + + </dependencies> +</project> diff --git a/Final/java/common/protocol-version.xml b/Final/java/common/protocol-version.xml new file mode 100644 index 0000000000..40331a8a84 --- /dev/null +++ b/Final/java/common/protocol-version.xml @@ -0,0 +1,60 @@ +<!-- + - + - Licensed to the Apache Software Foundation (ASF) under one + - or more contributor license agreements. See the NOTICE file + - distributed with this work for additional information + - regarding copyright ownership. The ASF licenses this file + - to you under the Apache License, Version 2.0 (the + - "License"); you may not use this file except in compliance + - with the License. You may obtain a copy of the License at + - + - http://www.apache.org/licenses/LICENSE-2.0 + - + - Unless required by applicable law or agreed to in writing, + - software distributed under the License is distributed on an + - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + - KIND, either express or implied. See the License for the + - specific language governing permissions and limitations + - under the License. + - + --> +<project name="Qpid Common Protocol Versions" default="generate"> + <property name="topDirectoryLocation" location=".." /> + <property name="project.build.directory" location="target" /> + <property name="gentools.home" location="${topDirectoryLocation}/../gentools" /> + <property name="generated.path" location="${project.build.directory}/generated-sources/gentools" /> + <property name="generated.package" value="org/apache/qpid/framing" /> + <property name="generated.dir" location="${generated.path}/${generated.package}" /> + <property name="generated.timestamp" location="${generated.dir}/timestamp" /> + <property name="xml.spec.dir" location="${topDirectoryLocation}/../specs" /> + <property name="xml.spec.deps" value="amqp.0-8.xml cluster.0-8.xml" /> + <property name="xml.spec.list" value="${xml.spec.dir}/amqp.0-8.xml ${xml.spec.dir}/cluster.0-8.xml" /> + + <target name="generate" depends="compile_generator,check_generate_deps" unless="generation.notRequired"> + <mkdir dir="${generated.dir}"/> + <java classname="org.apache.qpid.gentools.Main" fork="true" dir="${gentools.home}/src" failonerror="true"> + <arg line="-j -o ${generated.dir} -t ${gentools.home}/templ.java ${xml.spec.list}" /> + <classpath> + <pathelement path="${gentools.home}/src" /> + </classpath> + </java> + <touch file="${generated.timestamp}" /> + </target> + + <target name="check_generate_deps"> + <uptodate property="generation.notRequired" targetfile="${generated.timestamp}"> + <srcfiles dir="${xml.spec.dir}" includes="${xml.spec.deps}" /> + </uptodate> + </target> + + <target name="compile_generator"> + <ant dir="${gentools.home}" /> + </target> + + <target name="precompile" depends="generate"/> + + <target name="clean"> + <delete dir="${generated.path}" /> + </target> + +</project> diff --git a/Final/java/common/readme.txt b/Final/java/common/readme.txt new file mode 100644 index 0000000000..12841fa08d --- /dev/null +++ b/Final/java/common/readme.txt @@ -0,0 +1,4 @@ +AMQP Common Java API + +Common generated functionality for AMQP Java client and broker. See the +readme in the client and broker directories. diff --git a/Final/java/common/src/main/java/log4j.properties b/Final/java/common/src/main/java/log4j.properties new file mode 100644 index 0000000000..6d596d1d19 --- /dev/null +++ b/Final/java/common/src/main/java/log4j.properties @@ -0,0 +1,28 @@ +# +# 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. +# +log4j.rootLogger=${root.logging.level} + + +log4j.logger.org.apache.qpid=${amqj.logging.level}, console +log4j.additivity.org.apache.qpid=false + +log4j.appender.console=org.apache.log4j.ConsoleAppender +log4j.appender.console.Threshold=all +log4j.appender.console.layout=org.apache.log4j.PatternLayout +log4j.appender.console.layout.ConversionPattern=%t %d %p [%c{4}] %m%n diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQChannelClosedException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQChannelClosedException.java new file mode 100644 index 0000000000..251e91c1b9 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/AMQChannelClosedException.java @@ -0,0 +1,41 @@ +/* + * + * 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.qpid; + +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQChannelClosedException indicates that an operation cannot be performed becauase a channel has been closed. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represents a failed operation on a closed channel. + * </table> + * + * @todo Does this duplicate AMQChannelException? + */ +public class AMQChannelClosedException extends AMQException +{ + public AMQChannelClosedException(AMQConstant errorCode, String msg) + { + super(errorCode, msg); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQChannelException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQChannelException.java new file mode 100644 index 0000000000..9efd271e4d --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/AMQChannelException.java @@ -0,0 +1,68 @@ +/* + * + * 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.qpid; + +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ChannelCloseBody; +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQChannelException indicates that an error that requires the channel to be closed has occurred. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represents an error that rquires the channel to be closed. + * </table> + * + * @todo Does this duplicate AMQChannelClosedException? + */ +public class AMQChannelException extends AMQException +{ + private final int _classId; + private final int _methodId; + /* AMQP version for which exception ocurred */ + private final byte major; + private final byte minor; + + public AMQChannelException(AMQConstant errorCode, String msg, int classId, int methodId, byte major, byte minor, Throwable t) + { + super(errorCode, msg, t); + _classId = classId; + _methodId = methodId; + this.major = major; + this.minor = minor; + } + + public AMQChannelException(AMQConstant errorCode, String msg, int classId, int methodId, byte major, byte minor) + { + super(errorCode, msg); + _classId = classId; + _methodId = methodId; + this.major = major; + this.minor = minor; + } + + public AMQFrame getCloseFrame(int channel) + { + return ChannelCloseBody.createAMQFrame(channel, major, minor, _classId, _methodId, getErrorCode().getCode(), new AMQShortString(getMessage())); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQConnectionClosedException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQConnectionClosedException.java new file mode 100644 index 0000000000..eb736d437f --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/AMQConnectionClosedException.java @@ -0,0 +1,44 @@ +/* + * + * 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.qpid; + +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQConnectionClosedException indicates that a connection has been closed. + * + * <p/>This exception is really used as an event, in order that the method handler that raises it creates an event + * which is propagated to the io handler, in order to notify it of the connection closure. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represents a the closure of a connection. + * </table> + * + * @todo Should review where exceptions-as-events + */ +public class AMQConnectionClosedException extends AMQException +{ + public AMQConnectionClosedException(AMQConstant errorCode, String msg) + { + super(errorCode, msg); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java new file mode 100644 index 0000000000..7edfa648ed --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java @@ -0,0 +1,73 @@ +/* + * + * 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.qpid; + +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ConnectionCloseBody; +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQConnectionException indicates that an error that requires the channel to be closed has occurred. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represents an error that rquires the channel to be closed. + * </table> + * + * @todo Does this duplicate AMQChannelClosedException? + */ +public class AMQConnectionException extends AMQException +{ + private final int _classId; + private final int _methodId; + /* AMQP version for which exception ocurred */ + private final byte major; + private final byte minor; + boolean _closeConnetion; + + public AMQConnectionException(AMQConstant errorCode, String msg, int classId, int methodId, byte major, byte minor, + Throwable t) + { + super(errorCode, msg, t); + _classId = classId; + _methodId = methodId; + this.major = major; + this.minor = minor; + } + + public AMQConnectionException(AMQConstant errorCode, String msg, int classId, int methodId, byte major, byte minor) + { + super(errorCode, msg); + _classId = classId; + _methodId = methodId; + this.major = major; + this.minor = minor; + } + + public AMQFrame getCloseFrame(int channel) + { + return ConnectionCloseBody.createAMQFrame(channel, major, minor, _classId, _methodId, getErrorCode().getCode(), + new AMQShortString(getMessage())); + } + +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQConnectionFailureException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQConnectionFailureException.java new file mode 100644 index 0000000000..f78307d16f --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/AMQConnectionFailureException.java @@ -0,0 +1,40 @@ +/*
+ *
+ * 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.qpid;
+
+/**
+ * AMQConnectionFailureException indicates that a connection to a broker could not be formed.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents failure to connect to a broker.
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ */
+public class AMQConnectionFailureException extends AMQException
+{
+ public AMQConnectionFailureException(String message)
+ {
+ super(message);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQDisconnectedException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQDisconnectedException.java new file mode 100644 index 0000000000..e62b2c10a2 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/AMQDisconnectedException.java @@ -0,0 +1,39 @@ +/* + * + * 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.qpid; + +/** + * AMQDisconnectedException indicates that a broker disconnected without failover. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represents disconnection without failover by the broker. + * </table> + * + * @todo Not an AMQP exception as no status code. + */ +public class AMQDisconnectedException extends AMQException +{ + public AMQDisconnectedException(String msg) + { + super(msg); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQException.java new file mode 100644 index 0000000000..41599ed880 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/AMQException.java @@ -0,0 +1,101 @@ +/* + * + * 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.qpid; + +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQException forms the root exception of all exceptions relating to the AMQ protocol. It provides space to associate + * a required AMQ error code with the exception, which is a numeric value, with a meaning defined by the protocol. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represents an exception condition associated with an AMQ protocol status code. + * </table> + * + * @todo This exception class is also used as a generic exception throughout Qpid code. This usage may not be strictly + * correct if this is to signify a protocol exception. Should review. + */ +public class AMQException extends Exception +{ + /** Holds the AMQ error code constant associated with this exception. */ + private AMQConstant _errorCode; + + /** + * Creates an exception with an optional error code, optional message and optional underlying cause. + * + * @param errorCode The error code. May be null if not to be set. + * @param msg The exception message. May be null if not to be set. + * @param t The underlying cause of the exception. May be null if not to be set. + */ + public AMQException(AMQConstant errorCode, String msg, Throwable t) + { + super(((msg == null) ? "" : msg) + ((errorCode == null) ? "" : (" [error code " + errorCode + "]")), t); + _errorCode = errorCode; + } + + /** + * @param message + * + * @deprecated Use {@link #AMQException(org.apache.qpid.protocol.AMQConstant, String, Throwable)} instead. + */ + public AMQException(String message) + { + super(message); + // fixme This method needs removed and all AMQExceptions need a valid error code + _errorCode = AMQConstant.getConstant(-1); + } + + /** + * @param msg + * @param t + * + * @deprecated Use {@link #AMQException(org.apache.qpid.protocol.AMQConstant, String, Throwable)} instead. + */ + public AMQException(String msg, Throwable t) + { + super(msg, t); + // fixme This method needs removed and all AMQExceptions need a valid error code + _errorCode = AMQConstant.getConstant(-1); + } + + /** + * @param errorCode + * @param msg + * + * @deprecated Use {@link #AMQException(org.apache.qpid.protocol.AMQConstant, String, Throwable)} instead. + */ + public AMQException(AMQConstant errorCode, String msg) + { + super(msg + " [error code " + errorCode + ']'); + _errorCode = errorCode; + } + + /** + * Gets the AMQ protocol exception code associated with this exception. + * + * @return The AMQ protocol exception code associated with this exception. + */ + public AMQConstant getErrorCode() + { + return _errorCode; + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java new file mode 100644 index 0000000000..278128f924 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java @@ -0,0 +1,39 @@ +/* + * 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.qpid; + +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQInvalidArgumentException indicates that an invalid argument has been passed to an AMQP method. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represents an error due to an invalid argument being passed to an AMQP method. + * </table> + */ +public class AMQInvalidArgumentException extends AMQException +{ + public AMQInvalidArgumentException(String message) + { + super(AMQConstant.INVALID_ARGUMENT, message); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQInvalidRoutingKeyException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQInvalidRoutingKeyException.java new file mode 100644 index 0000000000..b5ec9845d6 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/AMQInvalidRoutingKeyException.java @@ -0,0 +1,39 @@ +/* + * 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.qpid; + +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQInvalidRoutingKeyException indicates an error with a routing key having an invalid format. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represents a format error in a routing key. + * </table> + */ +public class AMQInvalidRoutingKeyException extends AMQException +{ + public AMQInvalidRoutingKeyException(String message) + { + super(AMQConstant.INVALID_ROUTING_KEY, message); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQPInvalidClassException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQPInvalidClassException.java new file mode 100644 index 0000000000..a0574efa72 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/AMQPInvalidClassException.java @@ -0,0 +1,39 @@ +/* + * 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.qpid; + +/** + * AMQPInvalidClassException indicates an error when trying to store an illegally typed argument in a field table. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represents illegal argument type for field table values. + * </table> + * + * @todo Could just re-use an exising exception like IllegalArgumentException or ClassCastException. + */ +public class AMQPInvalidClassException extends RuntimeException +{ + public AMQPInvalidClassException(String s) + { + super(s); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQTimeoutException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQTimeoutException.java new file mode 100644 index 0000000000..0f8d9c47db --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/AMQTimeoutException.java @@ -0,0 +1,39 @@ +/* + * 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.qpid; + +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQTimeoutException indicates that an expected response from a broker took too long. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Indicates that an expected response from a broker took too long. + * </table> + */ +public class AMQTimeoutException extends AMQException +{ + public AMQTimeoutException(String message) + { + super(AMQConstant.REQUEST_TIMEOUT, message); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQUndeliveredException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQUndeliveredException.java new file mode 100644 index 0000000000..03220cc95e --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/AMQUndeliveredException.java @@ -0,0 +1,48 @@ +/* + * + * 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.qpid; + +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQUndeliveredException indicates that a message, marked immediate or mandatory, could not be delivered. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represents failure to delivery a message that must be delivered. + * </table> + */ +public class AMQUndeliveredException extends AMQException +{ + private Object _bounced; + + public AMQUndeliveredException(AMQConstant errorCode, String msg, Object bounced) + { + super(errorCode, msg); + + _bounced = bounced; + } + + public Object getUndeliveredMessage() + { + return _bounced; + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.java b/Final/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.java new file mode 100644 index 0000000000..a1e7088817 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.java @@ -0,0 +1,43 @@ +/*
+ *
+ * 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.qpid;
+
+/**
+ * AMQUnknownExchangeType represents coding error where unknown exchange type requested from exchange factory.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents unknown exchange type request.
+ * <tr><td>
+ *
+ * @todo Not an AMQP exception as no status code.
+ *
+ * @todo Represent coding error, where unknown exchange type is requested by passing a string parameter. Use a type safe
+ * enum for the exchange type, or replace with IllegalArgumentException. Should be runtime.
+ */
+public class AMQUnknownExchangeType extends AMQException
+{
+ public AMQUnknownExchangeType(String message)
+ {
+ super(message);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQUnresolvedAddressException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQUnresolvedAddressException.java new file mode 100644 index 0000000000..6cc9c3fe00 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/AMQUnresolvedAddressException.java @@ -0,0 +1,50 @@ +/* + * + * 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.qpid; + +/** + * AMQUnresolvedAddressException indicates failure to resolve a socket address. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represents failre to resolve a socket address. + * </table> + * + * @todo Not an AMQP exception as no status code. + * + * @todo Why replace java.nio.UnresolvedAddressException with this? This is checked, which may explain why, but it + * doesn't wrap the underlying exception. + */ +public class AMQUnresolvedAddressException extends AMQException +{ + String _broker; + + public AMQUnresolvedAddressException(String message, String broker) + { + super(message); + _broker = broker; + } + + public String toString() + { + return super.toString() + " Broker, \"" + _broker + "\""; + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java b/Final/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java new file mode 100644 index 0000000000..fa890d0ebb --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java @@ -0,0 +1,77 @@ +/* + * + * 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.qpid.codec; + +import org.apache.mina.filter.codec.ProtocolCodecFactory; +import org.apache.mina.filter.codec.ProtocolDecoder; +import org.apache.mina.filter.codec.ProtocolEncoder; + +/** + * AMQCodecFactory is a Mina codec factory. It supplies the encoders and decoders need to read and write the bytes to + * the wire. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations. + * <tr><td> Supply the protocol encoder. <td> {@link AMQEncoder} + * <tr><td> Supply the protocol decoder. <td> {@link AMQDecoder} + * </table> + */ +public class AMQCodecFactory implements ProtocolCodecFactory +{ + /** Holds the protocol encoder. */ + private final AMQEncoder _encoder = new AMQEncoder(); + + /** Holds the protocol decoder. */ + private final AMQDecoder _frameDecoder; + + /** + * Creates a new codec factory, specifiying whether it is expected that the first frame of data should be an + * initiation. This is the case for the broker, which always expects to received the protocol initiation on a newly + * connected client. + * + * @param expectProtocolInitiation <tt>true</tt> if the first frame received is going to be a protocol initiation + * frame, <tt>false</tt> if it is going to be a standard AMQ data block. + */ + public AMQCodecFactory(boolean expectProtocolInitiation) + { + _frameDecoder = new AMQDecoder(expectProtocolInitiation); + } + + /** + * Gets the AMQP encoder. + * + * @return The AMQP encoder. + */ + public ProtocolEncoder getEncoder() + { + return _encoder; + } + + /** + * Gets the AMQP decoder. + * + * @return The AMQP decoder. + */ + public ProtocolDecoder getDecoder() + { + return _frameDecoder; + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java b/Final/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java new file mode 100644 index 0000000000..02ae3cb089 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java @@ -0,0 +1,163 @@ +/* + * + * 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.qpid.codec; + +import org.apache.mina.common.ByteBuffer; +import org.apache.mina.common.IoSession; +import org.apache.mina.filter.codec.CumulativeProtocolDecoder; +import org.apache.mina.filter.codec.ProtocolDecoderOutput; + +import org.apache.qpid.framing.AMQDataBlockDecoder; +import org.apache.qpid.framing.ProtocolInitiation; + +/** + * AMQDecoder delegates the decoding of AMQP either to a data block decoder, or in the case of new connections, to a + * protocol initiation decoder. It is a cumulative decoder, which means that it can accumulate data to decode in the + * buffer until there is enough data to decode. + * + * <p/>One instance of this class is created per session, so any changes or configuration done at run time to the + * decoder will only affect decoding of the protocol session data to which is it bound. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Delegate protocol initiation to its decoder. <td> {@link ProtocolInitiation.Decoder} + * <tr><td> Delegate AMQP data to its decoder. <td> {@link AMQDataBlockDecoder} + * <tr><td> Accept notification that protocol initiation has completed. + * </table> + * + * @todo If protocol initiation decoder not needed, then don't create it. Probably not a big deal, but it adds to the + * per-session overhead. + */ +public class AMQDecoder extends CumulativeProtocolDecoder +{ + /** Holds the 'normal' AMQP data decoder. */ + private AMQDataBlockDecoder _dataBlockDecoder = new AMQDataBlockDecoder(); + + /** Holds the protocol initiation decoder. */ + private ProtocolInitiation.Decoder _piDecoder = new ProtocolInitiation.Decoder(); + + /** Flag to indicate whether this decoder needs to handle protocol initiation. */ + private boolean _expectProtocolInitiation; + + /** + * Creates a new AMQP decoder. + * + * @param expectProtocolInitiation <tt>true</tt> if this decoder needs to handle protocol initiation. + */ + public AMQDecoder(boolean expectProtocolInitiation) + { + _expectProtocolInitiation = expectProtocolInitiation; + } + + /** + * Delegates decoding AMQP from the data buffer that Mina has retrieved from the wire, to the data or protocol + * intiation decoders. + * + * @param session The Mina session. + * @param in The raw byte buffer. + * @param out The Mina object output gatherer to write decoded objects to. + * + * @return <tt>true</tt> if the data was decoded, <tt>false<tt> if more is needed and the data should accumulate. + * + * @throws Exception If the data cannot be decoded for any reason. + */ + protected boolean doDecode(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception + { + if (_expectProtocolInitiation) + { + return doDecodePI(session, in, out); + } + else + { + return doDecodeDataBlock(session, in, out); + } + } + + /** + * Decodes AMQP data, delegating the decoding to an {@link AMQDataBlockDecoder}. + * + * @param session The Mina session. + * @param in The raw byte buffer. + * @param out The Mina object output gatherer to write decoded objects to. + * + * @return <tt>true</tt> if the data was decoded, <tt>false<tt> if more is needed and the data should accumulate. + * + * @throws Exception If the data cannot be decoded for any reason. + */ + protected boolean doDecodeDataBlock(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception + { + int pos = in.position(); + boolean enoughData = _dataBlockDecoder.decodable(session, in); + in.position(pos); + if (!enoughData) + { + // returning false means it will leave the contents in the buffer and + // call us again when more data has been read + return false; + } + else + { + _dataBlockDecoder.decode(session, in, out); + + return true; + } + } + + /** + * Decodes an AMQP initiation, delegating the decoding to a {@link ProtocolInitiation.Decoder}. + * + * @param session The Mina session. + * @param in The raw byte buffer. + * @param out The Mina object output gatherer to write decoded objects to. + * + * @return <tt>true</tt> if the data was decoded, <tt>false<tt> if more is needed and the data should accumulate. + * + * @throws Exception If the data cannot be decoded for any reason. + */ + private boolean doDecodePI(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception + { + boolean enoughData = _piDecoder.decodable(session, in); + if (!enoughData) + { + // returning false means it will leave the contents in the buffer and + // call us again when more data has been read + return false; + } + else + { + _piDecoder.decode(session, in, out); + + return true; + } + } + + /** + * Sets the protocol initation flag, that determines whether decoding is handled by the data decoder of the protocol + * initation decoder. This method is expected to be called with <tt>false</tt> once protocol initation completes. + * + * @param expectProtocolInitiation <tt>true</tt> to use the protocol initiation decoder, <tt>false</tt> to use the + * data decoder. + */ + public void setExpectProtocolInitiation(boolean expectProtocolInitiation) + { + _expectProtocolInitiation = expectProtocolInitiation; + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/codec/AMQEncoder.java b/Final/java/common/src/main/java/org/apache/qpid/codec/AMQEncoder.java new file mode 100644 index 0000000000..53f48ae1c8 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/codec/AMQEncoder.java @@ -0,0 +1,66 @@ +/* + * + * 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.qpid.codec; + +import org.apache.mina.common.IoSession; +import org.apache.mina.filter.codec.ProtocolEncoder; +import org.apache.mina.filter.codec.ProtocolEncoderOutput; + +import org.apache.qpid.framing.AMQDataBlockEncoder; + +/** + * AMQEncoder delegates encoding of AMQP to a data encoder. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Delegate AMQP encoding. <td> {@link AMQDataBlockEncoder} + * </table> + * + * @todo This class just delegates to another, so seems to be pointless. Unless it is going to handle some + * responsibilities in the future, then drop it. + */ +public class AMQEncoder implements ProtocolEncoder +{ + /** The data encoder that is delegated to. */ + private AMQDataBlockEncoder _dataBlockEncoder = new AMQDataBlockEncoder(); + + /** + * Encodes AMQP. + * + * @param session The Mina session. + * @param message The data object to encode. + * @param out The Mina writer to output the raw byte data to. + * + * @throws Exception If the data cannot be encoded for any reason. + */ + public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception + { + _dataBlockEncoder.encode(session, message, out); + } + + /** + * Does nothing. Called by Mina to allow this to clean up resources when it is no longer needed. + * + * @param session The Mina session. + */ + public void dispose(IoSession session) + { } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/common/AMQPFilterTypes.java b/Final/java/common/src/main/java/org/apache/qpid/common/AMQPFilterTypes.java new file mode 100644 index 0000000000..9ed915cc35 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/common/AMQPFilterTypes.java @@ -0,0 +1,61 @@ +/* + * 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.qpid.common; + +import org.apache.qpid.framing.AMQShortString; + +/** + * Specifies the different filter types for consumers that filter their messages. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represent different consumer filter types. + * </table> + */ +public enum AMQPFilterTypes +{ + JMS_SELECTOR("x-filter-jms-selector"), + NO_CONSUME("x-filter-no-consume"), + AUTO_CLOSE("x-filter-auto-close"); + + /** The identifying string for the filter type. */ + private final AMQShortString _value; + + /** + * Creates a new filter type from its identifying string. + * + * @param value The identifying string. + */ + AMQPFilterTypes(String value) + { + _value = new AMQShortString(value); + } + + /** + * Gets the identifying string of the filter type. + * + * @return The identifying string of the filter type. + */ + public AMQShortString getValue() + { + return _value; + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/common/ClientProperties.java b/Final/java/common/src/main/java/org/apache/qpid/common/ClientProperties.java new file mode 100644 index 0000000000..67f16e6a87 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/common/ClientProperties.java @@ -0,0 +1,37 @@ +/* + * 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.qpid.common; + +/** + * Specifies the available client property types that different clients can use to identify themselves with. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Specify the available client property types. + * </table> + */ +public enum ClientProperties +{ + instance, + product, + version, + platform +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/common/QpidProperties.java b/Final/java/common/src/main/java/org/apache/qpid/common/QpidProperties.java new file mode 100644 index 0000000000..2c783aeaa4 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/common/QpidProperties.java @@ -0,0 +1,190 @@ +/* + * 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.qpid.common; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; +import java.util.Properties; + +/** + * QpidProperties captures the project name, version number, and source code repository revision number from a properties + * file which is generated as part of the build process. Normally, the name and version number are pulled from the module + * name and version number of the Maven build POM, but could come from other sources if the build system is changed. The + * idea behind this, is that every build has these values incorporated directly into its jar file, so that code in the + * wild can be identified, should its origination be forgotten. + * + * <p/>To get the build version of any Qpid code call the {@link #main} method. This version string is usually also + * printed to the console on broker start up. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><td>Load build versioning information into the runtime, for code identification purposes. + * </table> + * + * @todo Code to locate/load/log properties can be factored into a reusable properties utils class. Avoid having this + * same snippet of loading code scattered in many places. + * + * @todo Could also add a build number property for a sequential build number assigned by an automated build system, for + * build reproducability purposes. + */ +public class QpidProperties +{ + /** Used for debugging purposes. */ + private static final Logger _logger = LoggerFactory.getLogger(QpidProperties.class); + + /** The name of the version properties file to load from the class path. */ + public static final String VERSION_RESOURCE = "qpidversion.properties"; + + /** Defines the name of the product property. */ + public static final String PRODUCT_NAME_PROPERTY = "qpid.name"; + + /** Defines the name of the version property. */ + public static final String RELEASE_VERSION_PROPERTY = "qpid.version"; + + /** Defines the name of the source code revision property. */ + public static final String BUILD_VERSION_PROPERTY = "qpid.svnversion"; + + /** Defines the default value for all properties that cannot be loaded. */ + private static final String DEFAULT = "unknown"; + + /** Holds the product name. */ + private static String productName = DEFAULT; + + /** Holds the product version. */ + private static String releaseVersion = DEFAULT; + + /** Holds the source code revision. */ + private static String buildVersion = DEFAULT; + + // Loads the values from the version properties file. + static + { + Properties props = new Properties(); + + try + { + InputStream propertyStream = QpidProperties.class.getClassLoader().getResourceAsStream(VERSION_RESOURCE); + if (propertyStream == null) + { + _logger.warn("Unable to find resource " + VERSION_RESOURCE + " from classloader"); + } + else + { + props.load(propertyStream); + + if (_logger.isDebugEnabled()) + { + _logger.debug("Dumping QpidProperties"); + for (Map.Entry<Object, Object> entry : props.entrySet()) + { + _logger.debug("Property: " + entry.getKey() + " Value: " + entry.getValue()); + } + + _logger.debug("End of property dump"); + } + + productName = readPropertyValue(props, PRODUCT_NAME_PROPERTY); + releaseVersion = readPropertyValue(props, RELEASE_VERSION_PROPERTY); + buildVersion = readPropertyValue(props, BUILD_VERSION_PROPERTY); + } + } + catch (IOException e) + { + // Log a warning about this and leave the values initialized to unknown. + _logger.error("Could not load version.properties resource: " + e, e); + } + } + + /** + * Gets the product name. + * + * @return The product name. + */ + public static String getProductName() + { + return productName; + } + + /** + * Gets the product version. + * + * @return The product version. + */ + public static String getReleaseVersion() + { + return releaseVersion; + } + + /** + * Gets the source code revision. + * + * @return The source code revision. + */ + public static String getBuildVersion() + { + return buildVersion; + } + + /** + * Extracts all of the version information as a printable string. + * + * @return All of the version information as a printable string. + */ + public static String getVersionString() + { + return getProductName() + " - " + getReleaseVersion() + " build: " + getBuildVersion(); + } + + /** + * Helper method to extract a named property from properties. + * + * @param props The properties. + * @param propertyName The named property to extract. + * + * @return The extracted property or a default value if the properties do not contain the named property. + * + * @todo A bit pointless. + */ + private static String readPropertyValue(Properties props, String propertyName) + { + String retVal = (String) props.get(propertyName); + if (retVal == null) + { + retVal = DEFAULT; + } + + return retVal; + } + + /** + * Prints the versioning information to the console. This is extremely usefull for identifying Qpid code in the + * wild, where the origination of the code has been forgotten. + * + * @param args Does not require any arguments. + */ + public static void main(String[] args) + { + System.out.println(getVersionString()); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/configuration/Configured.java b/Final/java/common/src/main/java/org/apache/qpid/configuration/Configured.java new file mode 100644 index 0000000000..22903888fe --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/configuration/Configured.java @@ -0,0 +1,44 @@ +/* + * + * 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.qpid.configuration; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks a field as having a "configured" value injected into it by a configurator. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Configured +{ + /** + * The Commons Configuration path to the configuration element + */ + String path(); + + /** + * The default value to use should the path not be found in the configuration source + */ + String defaultValue(); +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/configuration/PropertyException.java b/Final/java/common/src/main/java/org/apache/qpid/configuration/PropertyException.java new file mode 100644 index 0000000000..1e5cc57fff --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/configuration/PropertyException.java @@ -0,0 +1,60 @@ +/* + * + * 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.qpid.configuration; + +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; + +/** + * Indicates a failure to parse a property expansion. See {@link PropertyUtils} for the code that does property + * expansions. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaboration + * <tr><td> Represent failure to expand a property name into a value. + * </table> + * + * @todo Not an AMQP exception as no status code. + */ +public class PropertyException extends AMQException +{ + public PropertyException(String message) + { + super(message); + } + + /* + public PropertyException(String msg, Throwable t) + { + super(msg, t); + } + + public PropertyException(AMQConstant errorCode, String msg, Throwable t) + { + super(errorCode, msg, t); + } + + public PropertyException(AMQConstant errorCode, String msg) + { + super(errorCode, msg); + } + */ +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/configuration/PropertyUtils.java b/Final/java/common/src/main/java/org/apache/qpid/configuration/PropertyUtils.java new file mode 100644 index 0000000000..b3c310d23c --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/configuration/PropertyUtils.java @@ -0,0 +1,164 @@ +/* + * + * 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.qpid.configuration; + +import java.util.ArrayList; +import java.util.Iterator; + +/** + * PropertyUtils provides helper methods for dealing with Java properties. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Expand system properties into strings with named expansions. + * </table> + * + * @todo Make the lookup method generic by passing in the properties to use for the expansion, rather than hard coding + * as system properties. The expansion code has greater potential for re-use that way. + * + * @todo Some more property related code could be added to this utils class, which might more appropriately reside under + * org.apache.qpid.util. For example standardised code to load properties from a resource name, currently found in + * QpidProperties and possibly other places could be moved here. + */ +public class PropertyUtils +{ + /** + * Given a string that contains substrings of the form <code>${xxx}</code>, looks up the valuea of 'xxx' as a + * system properties and substitutes tham back into the original string, to provide a property value expanded + * string. + * + * @param value The string to be scanned for property references. May be <code>null</code>, in which case this + * method returns immediately with no effect. + * + * @return The original string with the properties replaced, or <code>null</code> if the original string is + * <code>null</code>. + * + * @throws PropertyException If the string contains an opening <code>${</code> without a balancing <code>}</code>, + * or if the property to expand does not exist as a system property. + */ + public static String replaceProperties(String value) throws PropertyException + { + if (value == null) + { + return null; + } + + ArrayList<String> fragments = new ArrayList<String>(); + ArrayList<String> propertyRefs = new ArrayList<String>(); + parsePropertyString(value, fragments, propertyRefs); + + StringBuffer sb = new StringBuffer(); + Iterator j = propertyRefs.iterator(); + + for (String fragment : fragments) + { + if (fragment == null) + { + String propertyName = (String) j.next(); + + // try to get it from the project or keys + // Backward compatibility + String replacement = System.getProperty(propertyName); + + if (replacement == null) + { + throw new PropertyException("Property ${" + propertyName + "} has not been set"); + } + + fragment = replacement; + } + + sb.append(fragment); + } + + return sb.toString(); + } + + /** + * Parses the supplied value for properties which are specified using ${foo} syntax. $X is left as is, and $$ + * specifies a single $. + * + * @param value The property string to parse. + * @param fragments Is populated with the string fragments. A null means "insert a property value here. The number + * of nulls in the list when populated is equal to the size of the propertyRefs list. + * @param propertyRefs Populated with the property names to be added into the final string. + */ + private static void parsePropertyString(String value, ArrayList<String> fragments, ArrayList<String> propertyRefs) + throws PropertyException + { + int prev = 0; + int pos; + // search for the next instance of $ from the 'prev' position + while ((pos = value.indexOf("$", prev)) >= 0) + { + + // if there was any text before this, add it as a fragment + if (pos > 0) + { + fragments.add(value.substring(prev, pos)); + } + // if we are at the end of the string, we tack on a $ + // then move past it + if (pos == (value.length() - 1)) + { + fragments.add("$"); + prev = pos + 1; + } + else if (value.charAt(pos + 1) != '{') + { + // peek ahead to see if the next char is a property or not + // not a property: insert the char as a literal + if (value.charAt(pos + 1) == '$') + { + // two $ map to one $ + fragments.add("$"); + prev = pos + 2; + } + else + { + // $X maps to $X for all values of X!='$' + fragments.add(value.substring(pos, pos + 2)); + prev = pos + 2; + } + } + else + { + // property found, extract its name or bail on a typo + int endName = value.indexOf('}', pos); + if (endName < 0) + { + throw new PropertyException("Syntax error in property: " + value); + } + + String propertyName = value.substring(pos + 2, endName); + fragments.add(null); + propertyRefs.add(propertyName); + prev = endName + 1; + } + } + // no more $ signs found + // if there is any tail to the file, append it + if (prev < value.length()) + { + fragments.add(value.substring(prev)); + } + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/exchange/ExchangeDefaults.java b/Final/java/common/src/main/java/org/apache/qpid/exchange/ExchangeDefaults.java new file mode 100644 index 0000000000..123901b577 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/exchange/ExchangeDefaults.java @@ -0,0 +1,65 @@ +/* + * + * 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.qpid.exchange; + +import org.apache.qpid.framing.AMQShortString; + +/** + * Defines the names of the standard AMQP exchanges that every AMQP broker should provide. These exchange names + * and type are given in the specification. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Defines the standard AMQP exchange names. + * <tr><td> Defines the standard AMQP exchange types. + * </table> + * + * @todo A type safe enum, might be more appropriate for the exchange types. + */ +public class ExchangeDefaults +{ + /** The default direct exchange, which is a special internal exchange that cannot be explicitly bound to. */ + public static final AMQShortString DEFAULT_EXCHANGE_NAME = new AMQShortString("<<default>>"); + + /** The pre-defined topic exchange, the broker SHOULD provide this. */ + public static final AMQShortString TOPIC_EXCHANGE_NAME = new AMQShortString("amq.topic"); + + /** Defines the identifying type name of topic exchanges. */ + public static final AMQShortString TOPIC_EXCHANGE_CLASS = new AMQShortString("topic"); + + /** The pre-defined direct exchange, the broker MUST provide this. */ + public static final AMQShortString DIRECT_EXCHANGE_NAME = new AMQShortString("amq.direct"); + + /** Defines the identifying type name of direct exchanges. */ + public static final AMQShortString DIRECT_EXCHANGE_CLASS = new AMQShortString("direct"); + + /** The pre-defined headers exchange, the specification does not say this needs to be provided. */ + public static final AMQShortString HEADERS_EXCHANGE_NAME = new AMQShortString("amq.match"); + + /** Defines the identifying type name of headers exchanges. */ + public static final AMQShortString HEADERS_EXCHANGE_CLASS = new AMQShortString("headers"); + + /** The pre-defined fanout exchange, the boker MUST provide this. */ + public static final AMQShortString FANOUT_EXCHANGE_NAME = new AMQShortString("amq.fanout"); + + /** Defines the identifying type name of fanout exchanges. */ + public static final AMQShortString FANOUT_EXCHANGE_CLASS = new AMQShortString("fanout"); +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java new file mode 100644 index 0000000000..ebeea8d2b4 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java @@ -0,0 +1,39 @@ +/* + * + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +public abstract class AMQBody +{ + public abstract byte getFrameType(); + + /** + * Get the size of the body + * @return unsigned short + */ + protected abstract int getSize(); + + protected abstract void writePayload(ByteBuffer buffer); + + protected abstract void populateFromBuffer(ByteBuffer buffer, long size) + throws AMQFrameDecodingException, AMQProtocolVersionException; +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlock.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlock.java new file mode 100644 index 0000000000..903b5bfa7a --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlock.java @@ -0,0 +1,53 @@ +/* + * + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +/** + * A data block represents something that has a size in bytes and the ability to write itself to a byte + * buffer (similar to a byte array). + */ +public abstract class AMQDataBlock implements EncodableAMQDataBlock +{ + /** + * Get the size of buffer needed to store the byte representation of this + * frame. + * @return unsigned integer + */ + public abstract long getSize(); + + /** + * Writes the datablock to the specified buffer. + * @param buffer + */ + public abstract void writePayload(ByteBuffer buffer); + + public ByteBuffer toByteBuffer() + { + final ByteBuffer buffer = ByteBuffer.allocate((int)getSize()); + + writePayload(buffer); + buffer.flip(); + return buffer; + } + +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java new file mode 100644 index 0000000000..82ffc60802 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java @@ -0,0 +1,120 @@ +/* + * + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; +import org.apache.mina.common.IoSession; +import org.apache.mina.filter.codec.ProtocolDecoderOutput; + +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AMQDataBlockDecoder +{ + private static final String SESSION_METHOD_BODY_FACTORY = "QPID_SESSION_METHOD_BODY_FACTORY"; + + private static final BodyFactory[] _bodiesSupported = new BodyFactory[Byte.MAX_VALUE]; + + static + { + _bodiesSupported[ContentHeaderBody.TYPE] = ContentHeaderBodyFactory.getInstance(); + _bodiesSupported[ContentBody.TYPE] = ContentBodyFactory.getInstance(); + _bodiesSupported[HeartbeatBody.TYPE] = new HeartbeatBodyFactory(); + } + + Logger _logger = LoggerFactory.getLogger(AMQDataBlockDecoder.class); + + public AMQDataBlockDecoder() + { } + + public boolean decodable(IoSession session, ByteBuffer in) throws AMQFrameDecodingException + { + final int remainingAfterAttributes = in.remaining() - (1 + 2 + 4 + 1); + // type, channel, body length and end byte + if (remainingAfterAttributes < 0) + { + return false; + } + + in.skip(1 + 2); + final long bodySize = in.getUnsignedInt(); + + return (remainingAfterAttributes >= bodySize); + + } + + protected Object createAndPopulateFrame(IoSession session, ByteBuffer in) + throws AMQFrameDecodingException, AMQProtocolVersionException + { + final byte type = in.get(); + + BodyFactory bodyFactory; + if (type == AMQMethodBody.TYPE) + { + bodyFactory = (BodyFactory) session.getAttribute(SESSION_METHOD_BODY_FACTORY); + if (bodyFactory == null) + { + AMQVersionAwareProtocolSession protocolSession = (AMQVersionAwareProtocolSession) session.getAttachment(); + bodyFactory = new AMQMethodBodyFactory(protocolSession); + session.setAttribute(SESSION_METHOD_BODY_FACTORY, bodyFactory); + + } + + } + else + { + bodyFactory = _bodiesSupported[type]; + } + + if (bodyFactory == null) + { + throw new AMQFrameDecodingException(null, "Unsupported frame type: " + type, null); + } + + final int channel = in.getUnsignedShort(); + final long bodySize = in.getUnsignedInt(); + + // bodySize can be zero + if ((channel < 0) || (bodySize < 0)) + { + throw new AMQFrameDecodingException(null, "Undecodable frame: type = " + type + " channel = " + channel + + " bodySize = " + bodySize, null); + } + + AMQFrame frame = new AMQFrame(in, channel, bodySize, bodyFactory); + + byte marker = in.get(); + if ((marker & 0xFF) != 0xCE) + { + throw new AMQFrameDecodingException(null, "End of frame marker not found. Read " + marker + " length=" + bodySize + + " type=" + type, null); + } + + return frame; + } + + public void decode(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception + { + out.write(createAndPopulateFrame(session, in)); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java new file mode 100644 index 0000000000..05fd2bb480 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java @@ -0,0 +1,61 @@ +/* + * + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; +import org.apache.mina.common.IoSession; +import org.apache.mina.filter.codec.ProtocolEncoderOutput; +import org.apache.mina.filter.codec.demux.MessageEncoder; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.Set; + +public final class AMQDataBlockEncoder implements MessageEncoder +{ + private static final Logger _logger = LoggerFactory.getLogger(AMQDataBlockEncoder.class); + + private final Set _messageTypes = Collections.singleton(EncodableAMQDataBlock.class); + + public AMQDataBlockEncoder() + { } + + public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception + { + final AMQDataBlock frame = (AMQDataBlock) message; + + final ByteBuffer buffer = frame.toByteBuffer(); + + if (_logger.isDebugEnabled()) + { + _logger.debug("Encoded frame byte-buffer is '" + EncodingUtils.convertToHexString(buffer) + "'"); + } + + out.write(buffer); + } + + public Set getMessageTypes() + { + return _messageTypes; + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java new file mode 100644 index 0000000000..11f505fd4b --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java @@ -0,0 +1,75 @@ +/* + * + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +public class AMQFrame extends AMQDataBlock implements EncodableAMQDataBlock +{ + private final int _channel; + + private final AMQBody _bodyFrame; + + + + public AMQFrame(final int channel, final AMQBody bodyFrame) + { + _channel = channel; + _bodyFrame = bodyFrame; + } + + public AMQFrame(final ByteBuffer in, final int channel, final long bodySize, final BodyFactory bodyFactory) throws AMQFrameDecodingException + { + this._channel = channel; + this._bodyFrame = bodyFactory.createBody(in,bodySize); + } + + public long getSize() + { + return 1 + 2 + 4 + _bodyFrame.getSize() + 1; + } + + public void writePayload(ByteBuffer buffer) + { + buffer.put(_bodyFrame.getFrameType()); + EncodingUtils.writeUnsignedShort(buffer, _channel); + EncodingUtils.writeUnsignedInteger(buffer, _bodyFrame.getSize()); + _bodyFrame.writePayload(buffer); + buffer.put((byte) 0xCE); + } + + public final int getChannel() + { + return _channel; + } + + public final AMQBody getBodyFrame() + { + return _bodyFrame; + } + + + + public String toString() + { + return "Frame channelId: " + _channel + ", bodyFrame: " + String.valueOf(_bodyFrame); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQFrameDecodingException.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQFrameDecodingException.java new file mode 100644 index 0000000000..cd5ccf8e04 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQFrameDecodingException.java @@ -0,0 +1,41 @@ +/* + * + * 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.qpid.framing; + +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQFrameDecodingException indicates that an AMQP frame cannot be decoded because it does not have the correct + * format as defined by the protocol. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represents a format error in a protocol frame. + * </table> + */ +public class AMQFrameDecodingException extends AMQException +{ + public AMQFrameDecodingException(AMQConstant errorCode, String message, Throwable t) + { + super(errorCode, message, t); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java new file mode 100644 index 0000000000..f2e91083ca --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java @@ -0,0 +1,132 @@ +/* + * + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQChannelException; +import org.apache.qpid.AMQConnectionException; +import org.apache.qpid.protocol.AMQConstant; + +public abstract class AMQMethodBody extends AMQBody +{ + public static final byte TYPE = 1; + + /** AMQP version */ + protected byte major; + protected byte minor; + + public byte getMajor() + { + return major; + } + + public byte getMinor() + { + return minor; + } + + public AMQMethodBody(byte major, byte minor) + { + this.major = major; + this.minor = minor; + } + + /** unsigned short */ + protected abstract int getBodySize(); + + /** @return unsigned short */ + protected abstract int getClazz(); + + /** @return unsigned short */ + protected abstract int getMethod(); + + protected abstract void writeMethodPayload(ByteBuffer buffer); + + public byte getFrameType() + { + return TYPE; + } + + protected int getSize() + { + return 2 + 2 + getBodySize(); + } + + protected void writePayload(ByteBuffer buffer) + { + EncodingUtils.writeUnsignedShort(buffer, getClazz()); + EncodingUtils.writeUnsignedShort(buffer, getMethod()); + writeMethodPayload(buffer); + } + + protected abstract void populateMethodBodyFromBuffer(ByteBuffer buffer) throws AMQFrameDecodingException; + + protected void populateFromBuffer(ByteBuffer buffer, long size) throws AMQFrameDecodingException + { + populateMethodBodyFromBuffer(buffer); + } + + public String toString() + { + StringBuffer buf = new StringBuffer(getClass().getName()); + buf.append("[ Class: ").append(getClazz()); + buf.append(" Method: ").append(getMethod()).append(']'); + return buf.toString(); + } + + /** + * Creates an AMQChannelException for the corresponding body type (a channel exception should include the class and + * method ids of the body it resulted from). + */ + + /** + * Convenience Method to create a channel not found exception + * + * @param channelId The channel id that is not found + * + * @return new AMQChannelException + */ + public AMQChannelException getChannelNotFoundException(int channelId) + { + return getChannelException(AMQConstant.NOT_FOUND, "Channel not found for id:" + channelId); + } + + public AMQChannelException getChannelException(AMQConstant code, String message) + { + return new AMQChannelException(code, message, getClazz(), getMethod(), major, minor); + } + + public AMQChannelException getChannelException(AMQConstant code, String message, Throwable cause) + { + return new AMQChannelException(code, message, getClazz(), getMethod(), major, minor, cause); + } + + public AMQConnectionException getConnectionException(AMQConstant code, String message) + { + return new AMQConnectionException(code, message, getClazz(), getMethod(), major, minor); + } + + public AMQConnectionException getConnectionException(AMQConstant code, String message, Throwable cause) + { + return new AMQConnectionException(code, message, getClazz(), getMethod(), major, minor, cause); + } + +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyFactory.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyFactory.java new file mode 100644 index 0000000000..cf85bdab31 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyFactory.java @@ -0,0 +1,46 @@ +/* + * + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AMQMethodBodyFactory implements BodyFactory +{ + private static final Logger _log = LoggerFactory.getLogger(AMQMethodBodyFactory.class); + + private final AMQVersionAwareProtocolSession _protocolSession; + + public AMQMethodBodyFactory(AMQVersionAwareProtocolSession protocolSession) + { + _protocolSession = protocolSession; + } + + public AMQBody createBody(ByteBuffer in, long bodySize) throws AMQFrameDecodingException + { + return _protocolSession.getRegistry().get((short) in.getUnsignedShort(), (short) in.getUnsignedShort(), in, + bodySize); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java new file mode 100644 index 0000000000..359efe7eb7 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java @@ -0,0 +1,31 @@ +/*
+ *
+ * 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.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+
+public abstract interface AMQMethodBodyInstanceFactory
+{
+ public AMQMethodBody newInstance(byte major, byte minor, ByteBuffer buffer, long size) throws AMQFrameDecodingException;
+ public AMQMethodBody newInstance(byte major, byte minor, int clazzID, int methodID, ByteBuffer buffer, long size) throws AMQFrameDecodingException;
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolClassException.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolClassException.java new file mode 100644 index 0000000000..e48fd2e7f9 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolClassException.java @@ -0,0 +1,39 @@ +/* + * + * 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.qpid.framing; + +/** + * AMQProtocolInstanceException indicates that the protocol class is incorrect in a header. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represent incorrect protocol class in frame header. + * </table> + * + * @todo Not an AMQP exception as no status code. + */ +public class AMQProtocolClassException extends AMQProtocolHeaderException +{ + public AMQProtocolClassException(String message) + { + super(message); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolHeaderException.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolHeaderException.java new file mode 100644 index 0000000000..1ce49aba83 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolHeaderException.java @@ -0,0 +1,41 @@ +/* + * + * 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.qpid.framing; + +import org.apache.qpid.AMQException; + +/** + * AMQProtocolHeaderException indicates a format error in an AMQP frame header. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represent format error in frame header. + * </table> + * + * @todo Not an AMQP exception as no status code. + */ +public class AMQProtocolHeaderException extends AMQException +{ + public AMQProtocolHeaderException(String message) + { + super(message); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolInstanceException.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolInstanceException.java new file mode 100644 index 0000000000..9049eace2a --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolInstanceException.java @@ -0,0 +1,39 @@ +/* + * + * 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.qpid.framing; + +/** + * AMQProtocolInstanceException indicates that the protocol instance is incorrect in a header. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represent incorrect protocol instance in frame header. + * </table> + * + * @todo Not an AMQP exception as no status code. + */ +public class AMQProtocolInstanceException extends AMQProtocolHeaderException +{ + public AMQProtocolInstanceException(String message) + { + super(message); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolVersionException.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolVersionException.java new file mode 100644 index 0000000000..9074931617 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolVersionException.java @@ -0,0 +1,39 @@ +/* + * + * 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.qpid.framing; + +/** + * AMQProtocolInstanceException indicates that the client and server differ on expected protocol version in a header. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represent incorrect protocol version in frame header. + * </table> + * + * @todo Not an AMQP exception as no status code. + */ +public class AMQProtocolVersionException extends AMQProtocolHeaderException +{ + public AMQProtocolVersionException(String message) + { + super(message); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java new file mode 100644 index 0000000000..ec501951af --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java @@ -0,0 +1,436 @@ +/*
+ *
+ * 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.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.lang.ref.WeakReference;
+
+/**
+ * A short string is a representation of an AMQ Short String
+ * Short strings differ from the Java String class by being limited to on ASCII characters (0-127)
+ * and thus can be held more effectively in a byte buffer.
+ *
+ */
+public final class AMQShortString implements CharSequence, Comparable<AMQShortString>
+{
+
+ private static final ThreadLocal<Map<AMQShortString, WeakReference<AMQShortString>>> _localInternMap =
+ new ThreadLocal<Map<AMQShortString, WeakReference<AMQShortString>>>()
+ {
+ protected Map<AMQShortString, WeakReference<AMQShortString>> initialValue()
+ {
+ return new WeakHashMap<AMQShortString, WeakReference<AMQShortString>>();
+ };
+ };
+
+ private static final Map<AMQShortString, WeakReference<AMQShortString>> _globalInternMap =
+ new WeakHashMap<AMQShortString, WeakReference<AMQShortString>>();
+
+ private static final Logger _logger = LoggerFactory.getLogger(AMQShortString.class);
+
+ private final ByteBuffer _data;
+ private int _hashCode;
+ private final int _length;
+ private static final char[] EMPTY_CHAR_ARRAY = new char[0];
+
+ public AMQShortString(byte[] data)
+ {
+
+ _data = ByteBuffer.wrap(data);
+ _length = data.length;
+ }
+
+ public AMQShortString(String data)
+ {
+ this((data == null) ? EMPTY_CHAR_ARRAY : data.toCharArray());
+ if (data != null)
+ {
+ _hashCode = data.hashCode();
+ }
+ }
+
+ public AMQShortString(char[] data)
+ {
+ if (data == null)
+ {
+ throw new NullPointerException("Cannot create AMQShortString with null char[]");
+ }
+
+ final int length = data.length;
+ final byte[] stringBytes = new byte[length];
+ for (int i = 0; i < length; i++)
+ {
+ stringBytes[i] = (byte) (0xFF & data[i]);
+ }
+
+ _data = ByteBuffer.wrap(stringBytes);
+ _data.rewind();
+ _length = length;
+
+ }
+
+ public AMQShortString(CharSequence charSequence)
+ {
+ final int length = charSequence.length();
+ final byte[] stringBytes = new byte[length];
+ int hash = 0;
+ for (int i = 0; i < length; i++)
+ {
+ stringBytes[i] = ((byte) (0xFF & charSequence.charAt(i)));
+ hash = (31 * hash) + stringBytes[i];
+
+ }
+
+ _data = ByteBuffer.wrap(stringBytes);
+ _data.rewind();
+ _hashCode = hash;
+ _length = length;
+
+ }
+
+ private AMQShortString(ByteBuffer data)
+ {
+ _data = data;
+ _length = data.limit();
+
+ }
+
+ /**
+ * Get the length of the short string
+ * @return length of the underlying byte array
+ */
+ public int length()
+ {
+ return _length;
+ }
+
+ public char charAt(int index)
+ {
+
+ return (char) _data.get(index);
+
+ }
+
+ public CharSequence subSequence(int start, int end)
+ {
+ return new CharSubSequence(start, end);
+ }
+
+ public int writeToByteArray(byte[] encoding, int pos)
+ {
+ final int size = length();
+ encoding[pos++] = (byte) length();
+ for (int i = 0; i < size; i++)
+ {
+ encoding[pos++] = _data.get(i);
+ }
+
+ return pos;
+ }
+
+ public static AMQShortString readFromByteArray(byte[] byteEncodedDestination, int pos)
+ {
+
+ final byte len = byteEncodedDestination[pos];
+ if (len == 0)
+ {
+ return null;
+ }
+
+ ByteBuffer data = ByteBuffer.wrap(byteEncodedDestination, pos + 1, len).slice();
+
+ return new AMQShortString(data);
+ }
+
+ public static AMQShortString readFromBuffer(ByteBuffer buffer)
+ {
+ final short length = buffer.getUnsigned();
+ if (length == 0)
+ {
+ return null;
+ }
+ else
+ {
+ ByteBuffer data = buffer.slice();
+ data.limit(length);
+ data.rewind();
+ buffer.skip(length);
+
+ return new AMQShortString(data);
+ }
+ }
+
+ public byte[] getBytes()
+ {
+
+ if (_data.buf().hasArray() && (_data.arrayOffset() == 0))
+ {
+ return _data.array();
+ }
+ else
+ {
+ final int size = length();
+ byte[] b = new byte[size];
+ ByteBuffer buf = _data.duplicate();
+ buf.rewind();
+ buf.get(b);
+
+ return b;
+ }
+
+ }
+
+ public void writeToBuffer(ByteBuffer buffer)
+ {
+
+ final int size = length();
+ if (size != 0)
+ {
+
+ buffer.setAutoExpand(true);
+ buffer.put((byte) size);
+ if (_data.buf().hasArray())
+ {
+ buffer.put(_data.array(), _data.arrayOffset(), length());
+ }
+ else
+ {
+
+ for (int i = 0; i < size; i++)
+ {
+
+ buffer.put(_data.get(i));
+ }
+ }
+ }
+ else
+ {
+ // really writing out unsigned byte
+ buffer.put((byte) 0);
+ }
+
+ }
+
+ private final class CharSubSequence implements CharSequence
+ {
+ private final int _offset;
+ private final int _end;
+
+ public CharSubSequence(final int offset, final int end)
+ {
+ _offset = offset;
+ _end = end;
+ }
+
+ public int length()
+ {
+ return _end - _offset;
+ }
+
+ public char charAt(int index)
+ {
+ return AMQShortString.this.charAt(index + _offset);
+ }
+
+ public CharSequence subSequence(int start, int end)
+ {
+ return new CharSubSequence(start + _offset, end + _offset);
+ }
+ }
+
+ public char[] asChars()
+ {
+ final int size = length();
+ final char[] chars = new char[size];
+
+ for (int i = 0; i < size; i++)
+ {
+ chars[i] = (char) _data.get(i);
+ }
+
+ return chars;
+ }
+
+ public String asString()
+ {
+ return new String(asChars());
+ }
+
+ public boolean equals(Object o)
+ {
+ if (o == null)
+ {
+ return false;
+ }
+
+ if (o == this)
+ {
+ return true;
+ }
+
+ if (o instanceof AMQShortString)
+ {
+
+ final AMQShortString otherString = (AMQShortString) o;
+
+ if ((_hashCode != 0) && (otherString._hashCode != 0) && (_hashCode != otherString._hashCode))
+ {
+ return false;
+ }
+
+ return _data.equals(otherString._data);
+
+ }
+
+ return (o instanceof CharSequence) && equals((CharSequence) o);
+
+ }
+
+ public boolean equals(CharSequence s)
+ {
+ if (s == null)
+ {
+ return false;
+ }
+
+ if (s.length() != length())
+ {
+ return false;
+ }
+
+ for (int i = 0; i < length(); i++)
+ {
+ if (charAt(i) != s.charAt(i))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public int hashCode()
+ {
+ int hash = _hashCode;
+ if (hash == 0)
+ {
+ final int size = length();
+
+ for (int i = 0; i < size; i++)
+ {
+ hash = (31 * hash) + _data.get(i);
+ }
+
+ _hashCode = hash;
+ }
+
+ return hash;
+ }
+
+ public void setDirty()
+ {
+ _hashCode = 0;
+ }
+
+ public String toString()
+ {
+ return asString();
+ }
+
+ public int compareTo(AMQShortString name)
+ {
+ if (name == null)
+ {
+ return 1;
+ }
+ else
+ {
+
+ if (name.length() < length())
+ {
+ return -name.compareTo(this);
+ }
+
+ for (int i = 0; i < length(); i++)
+ {
+ final byte d = _data.get(i);
+ final byte n = name._data.get(i);
+ if (d < n)
+ {
+ return -1;
+ }
+
+ if (d > n)
+ {
+ return 1;
+ }
+ }
+
+ return (length() == name.length()) ? 0 : -1;
+ }
+ }
+
+
+ public AMQShortString intern()
+ {
+
+ hashCode();
+
+ Map<AMQShortString, WeakReference<AMQShortString>> localMap =
+ _localInternMap.get();
+
+ WeakReference<AMQShortString> ref = localMap.get(this);
+ AMQShortString internString;
+
+ if(ref != null)
+ {
+ internString = ref.get();
+ if(internString != null)
+ {
+ return internString;
+ }
+ }
+
+
+ synchronized(_globalInternMap)
+ {
+
+ ref = _globalInternMap.get(this);
+ if((ref == null) || ((internString = ref.get()) == null))
+ {
+ internString = new AMQShortString(getBytes());
+ ref = new WeakReference(internString);
+ _globalInternMap.put(internString, ref);
+ }
+
+ }
+ localMap.put(internString, ref);
+ return internString;
+
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQType.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQType.java new file mode 100644 index 0000000000..6dda91a488 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQType.java @@ -0,0 +1,701 @@ +/*
+ *
+ * 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.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+public enum AMQType
+{
+ //AMQP FieldTable Wire Types
+
+ LONG_STRING('S')
+ {
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedLongStringLength((String) value);
+ }
+
+
+ public String toNativeValue(Object value)
+ {
+ if (value != null)
+ {
+ return value.toString();
+ }
+ else
+ {
+ throw new NullPointerException("Cannot convert: null to String.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeLongStringBytes(buffer, (String) value);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readLongString(buffer);
+ }
+
+ },
+
+ INTEGER('i')
+ {
+
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.unsignedIntegerLength();
+ }
+
+ public Long toNativeValue(Object value)
+ {
+ if (value instanceof Long)
+ {
+ return (Long) value;
+ }
+ else if (value instanceof Integer)
+ {
+ return ((Integer) value).longValue();
+ }
+ else if (value instanceof Short)
+ {
+ return ((Short) value).longValue();
+ }
+ else if (value instanceof Byte)
+ {
+ return ((Byte) value).longValue();
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Long.valueOf((String)value);
+ }
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to int.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeUnsignedInteger(buffer, (Long) value);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readUnsignedInteger(buffer);
+ }
+ },
+
+ DECIMAL('D')
+ {
+
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedByteLength()+ EncodingUtils.encodedIntegerLength();
+ }
+
+ public Object toNativeValue(Object value)
+ {
+ if(value instanceof BigDecimal)
+ {
+ return (BigDecimal) value;
+ }
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to BigDecimal.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ BigDecimal bd = (BigDecimal) value;
+
+ byte places = new Integer(bd.scale()).byteValue();
+
+ int unscaled = bd.intValue();
+
+ EncodingUtils.writeByte(buffer, places);
+
+ EncodingUtils.writeInteger(buffer, unscaled);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ byte places = EncodingUtils.readByte(buffer);
+
+ int unscaled = EncodingUtils.readInteger(buffer);
+
+ BigDecimal bd = new BigDecimal(unscaled);
+ return bd.setScale(places);
+ }
+ },
+
+ TIMESTAMP('T')
+ {
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedLongLength();
+ }
+
+ public Object toNativeValue(Object value)
+ {
+ if(value instanceof Long)
+ {
+ return (Long) value;
+ }
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to timestamp.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeLong(buffer, (Long) value);
+ }
+
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readLong(buffer);
+ }
+ },
+
+ FIELD_TABLE('F')
+ {
+ public int getEncodingSize(Object value)
+ {
+ // TODO : fixme
+ throw new UnsupportedOperationException();
+ }
+
+ public Object toNativeValue(Object value)
+ {
+ // TODO : fixme
+ throw new UnsupportedOperationException();
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ // TODO : fixme
+ throw new UnsupportedOperationException();
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ // TODO : fixme
+ throw new UnsupportedOperationException();
+ }
+ },
+
+ VOID('V')
+ {
+ public int getEncodingSize(Object value)
+ {
+ return 0;
+ }
+
+
+ public Object toNativeValue(Object value)
+ {
+ if (value == null)
+ {
+ return null;
+ }
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to null String.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return null;
+ }
+ },
+
+ // Extended types
+
+ BINARY('x')
+ {
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedLongstrLength((byte[]) value);
+ }
+
+
+ public Object toNativeValue(Object value)
+ {
+ if((value instanceof byte[]) || (value == null))
+ {
+ return value;
+ }
+ else
+ {
+ throw new IllegalArgumentException("Value: " + value + " (" + value.getClass().getName() +
+ ") cannot be converted to byte[]");
+ }
+ }
+
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeLongstr(buffer, (byte[]) value);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readLongstr(buffer);
+ }
+
+ },
+
+ ASCII_STRING('c')
+ {
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedLongStringLength((String) value);
+ }
+
+
+ public String toNativeValue(Object value)
+ {
+ if (value != null)
+ {
+ return value.toString();
+ }
+ else
+ {
+ throw new NullPointerException("Cannot convert: null to String.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeLongStringBytes(buffer, (String) value);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readLongString(buffer);
+ }
+
+ },
+
+ WIDE_STRING('C')
+ {
+ public int getEncodingSize(Object value)
+ {
+ // FIXME: use proper charset encoder
+ return EncodingUtils.encodedLongStringLength((String) value);
+ }
+
+
+ public String toNativeValue(Object value)
+ {
+ if (value != null)
+ {
+ return value.toString();
+ }
+ else
+ {
+ throw new NullPointerException("Cannot convert: null to String.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeLongStringBytes(buffer, (String) value);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readLongString(buffer);
+ }
+ },
+
+ BOOLEAN('t')
+ {
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedBooleanLength();
+ }
+
+
+ public Object toNativeValue(Object value)
+ {
+ if (value instanceof Boolean)
+ {
+ return (Boolean) value;
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Boolean.valueOf((String)value);
+ }
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to boolean.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeBoolean(buffer, (Boolean) value);
+ }
+
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readBoolean(buffer);
+ }
+ },
+
+ ASCII_CHARACTER('k')
+ {
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedCharLength();
+ }
+
+
+ public Character toNativeValue(Object value)
+ {
+ if (value instanceof Character)
+ {
+ return (Character) value;
+ }
+ else if (value == null)
+ {
+ throw new NullPointerException("Cannot convert null into char");
+ }
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to char.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeChar(buffer, (Character) value);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readChar(buffer);
+ }
+
+ },
+
+ BYTE('b')
+ {
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedByteLength();
+ }
+
+
+ public Byte toNativeValue(Object value)
+ {
+ if (value instanceof Byte)
+ {
+ return (Byte) value;
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Byte.valueOf((String)value);
+ }
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to byte.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeByte(buffer, (Byte) value);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readByte(buffer);
+ }
+ },
+
+ SHORT('s')
+ {
+
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedShortLength();
+ }
+
+
+ public Short toNativeValue(Object value)
+ {
+ if (value instanceof Short)
+ {
+ return (Short) value;
+ }
+ else if (value instanceof Byte)
+ {
+ return ((Byte) value).shortValue();
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Short.valueOf((String)value);
+ }
+
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to short.");
+ }
+
+
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeShort(buffer, (Short) value);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readShort(buffer);
+ }
+ },
+
+ INT('I')
+ {
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedIntegerLength();
+ }
+
+ public Integer toNativeValue(Object value)
+ {
+ if (value instanceof Integer)
+ {
+ return (Integer) value;
+ }
+ else if (value instanceof Short)
+ {
+ return ((Short) value).intValue();
+ }
+ else if (value instanceof Byte)
+ {
+ return ((Byte) value).intValue();
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Integer.valueOf((String)value);
+ }
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to int.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeInteger(buffer, (Integer) value);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readInteger(buffer);
+ }
+ },
+
+ LONG('l')
+ {
+
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedLongLength();
+ }
+
+ public Object toNativeValue(Object value)
+ {
+ if(value instanceof Long)
+ {
+ return (Long) value;
+ }
+ else if (value instanceof Integer)
+ {
+ return ((Integer) value).longValue();
+ }
+ else if (value instanceof Short)
+ {
+ return ((Short) value).longValue();
+ }
+ else if (value instanceof Byte)
+ {
+ return ((Byte) value).longValue();
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Long.valueOf((String)value);
+ }
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to long.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeLong(buffer, (Long) value);
+ }
+
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readLong(buffer);
+ }
+ },
+
+ FLOAT('f')
+ {
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedFloatLength();
+ }
+
+
+ public Float toNativeValue(Object value)
+ {
+ if (value instanceof Float)
+ {
+ return (Float) value;
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Float.valueOf((String)value);
+ }
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to float.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeFloat(buffer, (Float) value);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readFloat(buffer);
+ }
+ },
+
+ DOUBLE('d')
+ {
+
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedDoubleLength();
+ }
+
+
+ public Double toNativeValue(Object value)
+ {
+ if (value instanceof Double)
+ {
+ return (Double) value;
+ }
+ else if (value instanceof Float)
+ {
+ return ((Float) value).doubleValue();
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Double.valueOf((String)value);
+ }
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to double.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeDouble(buffer, (Double) value);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readDouble(buffer);
+ }
+ };
+
+ private final byte _identifier;
+
+ AMQType(char identifier)
+ {
+ _identifier = (byte) identifier;
+ }
+
+ public final byte identifier()
+ {
+ return _identifier;
+ }
+
+
+ public abstract int getEncodingSize(Object value);
+
+ public abstract Object toNativeValue(Object value);
+
+ public AMQTypedValue asTypedValue(Object value)
+ {
+ return new AMQTypedValue(this, toNativeValue(value));
+ }
+
+ public void writeToBuffer(Object value, ByteBuffer buffer)
+ {
+ buffer.put((byte)identifier());
+ writeValueImpl(value, buffer);
+ }
+
+ abstract void writeValueImpl(Object value, ByteBuffer buffer);
+
+ abstract Object readValueFromBuffer(ByteBuffer buffer);
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java new file mode 100644 index 0000000000..1419dd75b1 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java @@ -0,0 +1,48 @@ +/*
+ *
+ * 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.qpid.framing;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class AMQTypeMap
+{
+ public static Map<Byte, AMQType> _reverseTypeMap = new HashMap<Byte, AMQType>();
+
+ static
+ {
+ for(AMQType type : AMQType.values())
+ {
+ _reverseTypeMap.put(type.identifier(), type);
+ }
+ }
+
+ public static AMQType getType(Byte identifier)
+ {
+ AMQType result = _reverseTypeMap.get(identifier);
+ if (result == null) {
+ throw new IllegalArgumentException
+ ("no such type code: " + Integer.toHexString(identifier.intValue()));
+ }
+ return result;
+ }
+
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java new file mode 100644 index 0000000000..7193580884 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java @@ -0,0 +1,80 @@ +/*
+ *
+ * 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.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+public class AMQTypedValue
+{
+ private final AMQType _type;
+ private final Object _value;
+
+
+ public AMQTypedValue(AMQType type, Object value)
+ {
+ if(type == null)
+ {
+ throw new NullPointerException("Cannot create a typed value with null type");
+ }
+ _type = type;
+ _value = type.toNativeValue(value);
+ }
+
+ private AMQTypedValue(AMQType type, ByteBuffer buffer)
+ {
+ _type = type;
+ _value = type.readValueFromBuffer( buffer );
+ }
+
+
+ public AMQType getType()
+ {
+ return _type;
+ }
+
+ public Object getValue()
+ {
+ return _value;
+ }
+
+
+ public void writeToBuffer(ByteBuffer buffer)
+ {
+ _type.writeToBuffer(_value,buffer);
+ }
+
+ public int getEncodingSize()
+ {
+ return _type.getEncodingSize(_value);
+ }
+
+ public static AMQTypedValue readFromBuffer(ByteBuffer buffer)
+ {
+ AMQType type = AMQTypeMap.getType(buffer.get());
+ return new AMQTypedValue(type, buffer);
+ }
+
+ public String toString()
+ {
+ return "["+getType()+": "+getValue()+"]";
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java b/Final/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java new file mode 100644 index 0000000000..61f2f891e5 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java @@ -0,0 +1,799 @@ +/* + * + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BasicContentHeaderProperties implements CommonContentHeaderProperties +{ + private static final Logger _logger = LoggerFactory.getLogger(BasicContentHeaderProperties.class); + + private static final AMQShortString ZERO_STRING = null; + + /** + * We store the encoded form when we decode the content header so that if we need to write it out without modifying + * it we can do so without incurring the expense of reencoding it + */ + private byte[] _encodedForm; + + /** Flag indicating whether the entire content header has been decoded yet */ + private boolean _decoded = true; + + /** + * We have some optimisations for partial decoding for maximum performance. The headers are used in the broker for + * routing in some cases so we can decode that separately. + */ + private boolean _decodedHeaders = true; + + /** + * We have some optimisations for partial decoding for maximum performance. The content type is used by all clients + * to determine the message type + */ + private boolean _decodedContentType = true; + + private AMQShortString _contentType; + + private AMQShortString _encoding; + + private FieldTable _headers; + + private byte _deliveryMode; + + private byte _priority; + + private AMQShortString _correlationId; + + private AMQShortString _replyTo; + + private long _expiration; + + private AMQShortString _messageId; + + private long _timestamp; + + private AMQShortString _type; + + private AMQShortString _userId; + + private AMQShortString _appId; + + private AMQShortString _clusterId; + + private int _propertyFlags = 0; + private static final int CONTENT_TYPE_MASK = 1 << 15; + private static final int ENCONDING_MASK = 1 << 14; + private static final int HEADERS_MASK = 1 << 13; + private static final int DELIVERY_MODE_MASK = 1 << 12; + private static final int PROPRITY_MASK = 1 << 11; + private static final int CORRELATION_ID_MASK = 1 << 10; + private static final int REPLY_TO_MASK = 1 << 9; + private static final int EXPIRATION_MASK = 1 << 8; + private static final int MESSAGE_ID_MASK = 1 << 7; + private static final int TIMESTAMP_MASK = 1 << 6; + private static final int TYPE_MASK = 1 << 5; + private static final int USER_ID_MASK = 1 << 4; + private static final int APPLICATION_ID_MASK = 1 << 3; + private static final int CLUSTER_ID_MASK = 1 << 2; + + public BasicContentHeaderProperties() + { } + + public int getPropertyListSize() + { + if (_encodedForm != null) + { + return _encodedForm.length; + } + else + { + int size = 0; + + if ((_propertyFlags & (CONTENT_TYPE_MASK)) > 0) + { + size += EncodingUtils.encodedShortStringLength(_contentType); + } + + if ((_propertyFlags & ENCONDING_MASK) > 0) + { + size += EncodingUtils.encodedShortStringLength(_encoding); + } + + if ((_propertyFlags & HEADERS_MASK) > 0) + { + size += EncodingUtils.encodedFieldTableLength(_headers); + } + + if ((_propertyFlags & DELIVERY_MODE_MASK) > 0) + { + size += 1; + } + + if ((_propertyFlags & PROPRITY_MASK) > 0) + { + size += 1; + } + + if ((_propertyFlags & CORRELATION_ID_MASK) > 0) + { + size += EncodingUtils.encodedShortStringLength(_correlationId); + } + + if ((_propertyFlags & REPLY_TO_MASK) > 0) + { + size += EncodingUtils.encodedShortStringLength(_replyTo); + } + + if ((_propertyFlags & EXPIRATION_MASK) > 0) + { + if (_expiration == 0L) + { + size += EncodingUtils.encodedShortStringLength(ZERO_STRING); + } + else + { + size += EncodingUtils.encodedShortStringLength(_expiration); + } + } + + if ((_propertyFlags & MESSAGE_ID_MASK) > 0) + { + size += EncodingUtils.encodedShortStringLength(_messageId); + } + + if ((_propertyFlags & TIMESTAMP_MASK) > 0) + { + size += 8; + } + + if ((_propertyFlags & TYPE_MASK) > 0) + { + size += EncodingUtils.encodedShortStringLength(_type); + } + + if ((_propertyFlags & USER_ID_MASK) > 0) + { + size += EncodingUtils.encodedShortStringLength(_userId); + } + + if ((_propertyFlags & APPLICATION_ID_MASK) > 0) + { + size += EncodingUtils.encodedShortStringLength(_appId); + } + + if ((_propertyFlags & CLUSTER_ID_MASK) > 0) + { + size += EncodingUtils.encodedShortStringLength(_clusterId); + } + + return size; + } + } + + private void clearEncodedForm() + { + if (!_decoded && (_encodedForm != null)) + { + // decode(); + } + + _encodedForm = null; + } + + public void setPropertyFlags(int propertyFlags) + { + clearEncodedForm(); + _propertyFlags = propertyFlags; + } + + public int getPropertyFlags() + { + return _propertyFlags; + } + + public void writePropertyListPayload(ByteBuffer buffer) + { + if (_encodedForm != null) + { + buffer.put(_encodedForm); + } + else + { + if ((_propertyFlags & (CONTENT_TYPE_MASK)) != 0) + { + EncodingUtils.writeShortStringBytes(buffer, _contentType); + } + + if ((_propertyFlags & ENCONDING_MASK) != 0) + { + EncodingUtils.writeShortStringBytes(buffer, _encoding); + } + + if ((_propertyFlags & HEADERS_MASK) != 0) + { + EncodingUtils.writeFieldTableBytes(buffer, _headers); + } + + if ((_propertyFlags & DELIVERY_MODE_MASK) != 0) + { + buffer.put(_deliveryMode); + } + + if ((_propertyFlags & PROPRITY_MASK) != 0) + { + buffer.put(_priority); + } + + if ((_propertyFlags & CORRELATION_ID_MASK) != 0) + { + EncodingUtils.writeShortStringBytes(buffer, _correlationId); + } + + if ((_propertyFlags & REPLY_TO_MASK) != 0) + { + EncodingUtils.writeShortStringBytes(buffer, _replyTo); + } + + if ((_propertyFlags & EXPIRATION_MASK) != 0) + { + if (_expiration == 0L) + { + EncodingUtils.writeShortStringBytes(buffer, ZERO_STRING); + } + else + { + EncodingUtils.writeShortStringBytes(buffer, String.valueOf(_expiration)); + } + } + + if ((_propertyFlags & MESSAGE_ID_MASK) != 0) + { + EncodingUtils.writeShortStringBytes(buffer, _messageId); + } + + if ((_propertyFlags & TIMESTAMP_MASK) != 0) + { + EncodingUtils.writeTimestamp(buffer, _timestamp); + } + + if ((_propertyFlags & TYPE_MASK) != 0) + { + EncodingUtils.writeShortStringBytes(buffer, _type); + } + + if ((_propertyFlags & USER_ID_MASK) != 0) + { + EncodingUtils.writeShortStringBytes(buffer, _userId); + } + + if ((_propertyFlags & APPLICATION_ID_MASK) != 0) + { + EncodingUtils.writeShortStringBytes(buffer, _appId); + } + + if ((_propertyFlags & CLUSTER_ID_MASK) != 0) + { + EncodingUtils.writeShortStringBytes(buffer, _clusterId); + } + } + } + + public void populatePropertiesFromBuffer(ByteBuffer buffer, int propertyFlags, int size) throws AMQFrameDecodingException + { + _propertyFlags = propertyFlags; + + if (_logger.isDebugEnabled()) + { + _logger.debug("Property flags: " + _propertyFlags); + } + + decode(buffer); + /*_encodedForm = new byte[size]; + buffer.get(_encodedForm, 0, size); + _decoded = false; + _decodedHeaders = false; + _decodedContentType = false;*/ + } + + private void decode(ByteBuffer buffer) + { + // ByteBuffer buffer = ByteBuffer.wrap(_encodedForm); + int pos = buffer.position(); + try + { + if ((_propertyFlags & (CONTENT_TYPE_MASK)) != 0) + { + _contentType = EncodingUtils.readAMQShortString(buffer); + } + + if ((_propertyFlags & ENCONDING_MASK) != 0) + { + _encoding = EncodingUtils.readAMQShortString(buffer); + } + + if ((_propertyFlags & HEADERS_MASK) != 0) + { + _headers = EncodingUtils.readFieldTable(buffer); + } + + if ((_propertyFlags & DELIVERY_MODE_MASK) != 0) + { + _deliveryMode = buffer.get(); + } + + if ((_propertyFlags & PROPRITY_MASK) != 0) + { + _priority = buffer.get(); + } + + if ((_propertyFlags & CORRELATION_ID_MASK) != 0) + { + _correlationId = EncodingUtils.readAMQShortString(buffer); + } + + if ((_propertyFlags & REPLY_TO_MASK) != 0) + { + _replyTo = EncodingUtils.readAMQShortString(buffer); + } + + if ((_propertyFlags & EXPIRATION_MASK) != 0) + { + _expiration = EncodingUtils.readLongAsShortString(buffer); + } + + if ((_propertyFlags & MESSAGE_ID_MASK) != 0) + { + _messageId = EncodingUtils.readAMQShortString(buffer); + } + + if ((_propertyFlags & TIMESTAMP_MASK) != 0) + { + _timestamp = EncodingUtils.readTimestamp(buffer); + } + + if ((_propertyFlags & TYPE_MASK) != 0) + { + _type = EncodingUtils.readAMQShortString(buffer); + } + + if ((_propertyFlags & USER_ID_MASK) != 0) + { + _userId = EncodingUtils.readAMQShortString(buffer); + } + + if ((_propertyFlags & APPLICATION_ID_MASK) != 0) + { + _appId = EncodingUtils.readAMQShortString(buffer); + } + + if ((_propertyFlags & CLUSTER_ID_MASK) != 0) + { + _clusterId = EncodingUtils.readAMQShortString(buffer); + } + } + catch (AMQFrameDecodingException e) + { + throw new RuntimeException("Error in content header data: " + e, e); + } + + final int endPos = buffer.position(); + buffer.position(pos); + final int len = endPos - pos; + _encodedForm = new byte[len]; + final int limit = buffer.limit(); + buffer.limit(endPos); + buffer.get(_encodedForm, 0, len); + buffer.limit(limit); + buffer.position(endPos); + _decoded = true; + } + + private void decodeUpToHeaders() + { + ByteBuffer buffer = ByteBuffer.wrap(_encodedForm); + try + { + if ((_propertyFlags & (CONTENT_TYPE_MASK)) != 0) + { + byte length = buffer.get(); + buffer.skip(length); + } + + if ((_propertyFlags & ENCONDING_MASK) != 0) + { + byte length = buffer.get(); + buffer.skip(length); + } + + if ((_propertyFlags & HEADERS_MASK) != 0) + { + _headers = EncodingUtils.readFieldTable(buffer); + + } + + _decodedHeaders = true; + } + catch (AMQFrameDecodingException e) + { + throw new RuntimeException("Error in content header data: " + e, e); + } + } + + private void decodeUpToContentType() + { + ByteBuffer buffer = ByteBuffer.wrap(_encodedForm); + + if ((_propertyFlags & (CONTENT_TYPE_MASK)) != 0) + { + _contentType = EncodingUtils.readAMQShortString(buffer); + } + + _decodedContentType = true; + } + + private void decodeIfNecessary() + { + if (!_decoded) + { + // decode(); + } + } + + private void decodeHeadersIfNecessary() + { + if (!_decoded && !_decodedHeaders) + { + decodeUpToHeaders(); + } + } + + private void decodeContentTypeIfNecessary() + { + if (!_decoded && !_decodedContentType) + { + decodeUpToContentType(); + } + } + + public AMQShortString getContentType() + { + decodeContentTypeIfNecessary(); + + return _contentType; + } + + public String getContentTypeAsString() + { + decodeContentTypeIfNecessary(); + + return (_contentType == null) ? null : _contentType.toString(); + } + + public void setContentType(AMQShortString contentType) + { + clearEncodedForm(); + _propertyFlags |= (CONTENT_TYPE_MASK); + _contentType = contentType; + } + + public void setContentType(String contentType) + { + setContentType((contentType == null) ? null : new AMQShortString(contentType)); + } + + public String getEncodingAsString() + { + + return (getEncoding() == null) ? null : getEncoding().toString(); + } + + public AMQShortString getEncoding() + { + decodeIfNecessary(); + + return _encoding; + } + + public void setEncoding(String encoding) + { + clearEncodedForm(); + _propertyFlags |= ENCONDING_MASK; + _encoding = (encoding == null) ? null : new AMQShortString(encoding); + } + + public void setEncoding(AMQShortString encoding) + { + clearEncodedForm(); + _propertyFlags |= ENCONDING_MASK; + _encoding = encoding; + } + + public FieldTable getHeaders() + { + decodeHeadersIfNecessary(); + + if (_headers == null) + { + setHeaders(FieldTableFactory.newFieldTable()); + } + + return _headers; + } + + public void setHeaders(FieldTable headers) + { + clearEncodedForm(); + _propertyFlags |= HEADERS_MASK; + _headers = headers; + } + + public byte getDeliveryMode() + { + decodeIfNecessary(); + + return _deliveryMode; + } + + public void setDeliveryMode(byte deliveryMode) + { + clearEncodedForm(); + _propertyFlags |= DELIVERY_MODE_MASK; + _deliveryMode = deliveryMode; + } + + public byte getPriority() + { + decodeIfNecessary(); + + return _priority; + } + + public void setPriority(byte priority) + { + clearEncodedForm(); + _propertyFlags |= PROPRITY_MASK; + _priority = priority; + } + + public AMQShortString getCorrelationId() + { + decodeIfNecessary(); + + return _correlationId; + } + + public String getCorrelationIdAsString() + { + decodeIfNecessary(); + + return (_correlationId == null) ? null : _correlationId.toString(); + } + + public void setCorrelationId(String correlationId) + { + setCorrelationId((correlationId == null) ? null : new AMQShortString(correlationId)); + } + + public void setCorrelationId(AMQShortString correlationId) + { + clearEncodedForm(); + _propertyFlags |= CORRELATION_ID_MASK; + _correlationId = correlationId; + } + + public String getReplyToAsString() + { + decodeIfNecessary(); + + return (_replyTo == null) ? null : _replyTo.toString(); + } + + public AMQShortString getReplyTo() + { + decodeIfNecessary(); + + return _replyTo; + } + + public void setReplyTo(String replyTo) + { + setReplyTo((replyTo == null) ? null : new AMQShortString(replyTo)); + } + + public void setReplyTo(AMQShortString replyTo) + { + + clearEncodedForm(); + _propertyFlags |= REPLY_TO_MASK; + _replyTo = replyTo; + } + + public long getExpiration() + { + decodeIfNecessary(); + + return _expiration; + } + + public void setExpiration(long expiration) + { + clearEncodedForm(); + _propertyFlags |= EXPIRATION_MASK; + _expiration = expiration; + } + + public AMQShortString getMessageId() + { + decodeIfNecessary(); + + return _messageId; + } + + public String getMessageIdAsString() + { + decodeIfNecessary(); + + return (_messageId == null) ? null : _messageId.toString(); + } + + public void setMessageId(String messageId) + { + clearEncodedForm(); + _propertyFlags |= MESSAGE_ID_MASK; + _messageId = (messageId == null) ? null : new AMQShortString(messageId); + } + + public void setMessageId(AMQShortString messageId) + { + clearEncodedForm(); + _propertyFlags |= MESSAGE_ID_MASK; + _messageId = messageId; + } + + public long getTimestamp() + { + decodeIfNecessary(); + + return _timestamp; + } + + public void setTimestamp(long timestamp) + { + clearEncodedForm(); + _propertyFlags |= TIMESTAMP_MASK; + _timestamp = timestamp; + } + + public String getTypeAsString() + { + decodeIfNecessary(); + + return (_type == null) ? null : _type.toString(); + } + + public AMQShortString getType() + { + decodeIfNecessary(); + + return _type; + } + + public void setType(String type) + { + setType((type == null) ? null : new AMQShortString(type)); + } + + public void setType(AMQShortString type) + { + clearEncodedForm(); + _propertyFlags |= TYPE_MASK; + _type = type; + } + + public String getUserIdAsString() + { + decodeIfNecessary(); + + return (_userId == null) ? null : _userId.toString(); + } + + public AMQShortString getUserId() + { + decodeIfNecessary(); + + return _userId; + } + + public void setUserId(String userId) + { + setUserId((userId == null) ? null : new AMQShortString(userId)); + } + + public void setUserId(AMQShortString userId) + { + clearEncodedForm(); + _propertyFlags |= USER_ID_MASK; + _userId = userId; + } + + public String getAppIdAsString() + { + decodeIfNecessary(); + + return (_appId == null) ? null : _appId.toString(); + } + + public AMQShortString getAppId() + { + decodeIfNecessary(); + + return _appId; + } + + public void setAppId(String appId) + { + setAppId((appId == null) ? null : new AMQShortString(appId)); + } + + public void setAppId(AMQShortString appId) + { + clearEncodedForm(); + _propertyFlags |= APPLICATION_ID_MASK; + _appId = appId; + } + + public String getClusterIdAsString() + { + decodeIfNecessary(); + + return (_clusterId == null) ? null : _clusterId.toString(); + } + + public AMQShortString getClusterId() + { + decodeIfNecessary(); + + return _clusterId; + } + + public void setClusterId(String clusterId) + { + setClusterId((clusterId == null) ? null : new AMQShortString(clusterId)); + } + + public void setClusterId(AMQShortString clusterId) + { + clearEncodedForm(); + _propertyFlags |= CLUSTER_ID_MASK; + _clusterId = clusterId; + } + + public String toString() + { + return "reply-to = " + _replyTo + ",propertyFlags = " + _propertyFlags + ",ApplicationID = " + _appId + + ",ClusterID = " + _clusterId + ",UserId = " + _userId + ",JMSMessageID = " + _messageId + + ",JMSCorrelationID = " + _correlationId + ",JMSDeliveryMode = " + _deliveryMode + ",JMSExpiration = " + + _expiration + ",JMSPriority = " + _priority + ",JMSTimestamp = " + _timestamp + ",JMSType = " + _type; + } + +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/BodyFactory.java b/Final/java/common/src/main/java/org/apache/qpid/framing/BodyFactory.java new file mode 100644 index 0000000000..59646577e1 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/BodyFactory.java @@ -0,0 +1,31 @@ +/* + * + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +/** + * Any class that is capable of turning a stream of bytes into an AMQ structure must implement this interface. + */ +public interface BodyFactory +{ + AMQBody createBody(ByteBuffer in, long bodySize) throws AMQFrameDecodingException; +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.java b/Final/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.java new file mode 100644 index 0000000000..6a608a8bff --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.java @@ -0,0 +1,81 @@ +/*
+ *
+ * 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.qpid.framing;
+
+public interface CommonContentHeaderProperties extends ContentHeaderProperties
+{
+ AMQShortString getContentType();
+
+ void setContentType(AMQShortString contentType);
+
+ FieldTable getHeaders();
+
+ void setHeaders(FieldTable headers);
+
+ byte getDeliveryMode();
+
+ void setDeliveryMode(byte deliveryMode);
+
+ byte getPriority();
+
+ void setPriority(byte priority);
+
+ AMQShortString getCorrelationId();
+
+ void setCorrelationId(AMQShortString correlationId);
+
+ AMQShortString getReplyTo();
+
+ void setReplyTo(AMQShortString replyTo);
+
+ long getExpiration();
+
+ void setExpiration(long expiration);
+
+ AMQShortString getMessageId();
+
+ void setMessageId(AMQShortString messageId);
+
+ long getTimestamp();
+
+ void setTimestamp(long timestamp);
+
+ AMQShortString getType();
+
+ void setType(AMQShortString type);
+
+ AMQShortString getUserId();
+
+ void setUserId(AMQShortString userId);
+
+ AMQShortString getAppId();
+
+ void setAppId(AMQShortString appId);
+
+ AMQShortString getClusterId();
+
+ void setClusterId(AMQShortString clusterId);
+
+ AMQShortString getEncoding();
+
+ void setEncoding(AMQShortString encoding);
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/CompositeAMQDataBlock.java b/Final/java/common/src/main/java/org/apache/qpid/framing/CompositeAMQDataBlock.java new file mode 100644 index 0000000000..5ec62ede93 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/CompositeAMQDataBlock.java @@ -0,0 +1,103 @@ +/* + * + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +public class CompositeAMQDataBlock extends AMQDataBlock implements EncodableAMQDataBlock +{ + private ByteBuffer _encodedBlock; + + private AMQDataBlock[] _blocks; + + public CompositeAMQDataBlock(AMQDataBlock[] blocks) + { + _blocks = blocks; + } + + /** + * The encoded block will be logically first before the AMQDataBlocks which are encoded + * into the buffer afterwards. + * @param encodedBlock already-encoded data + * @param blocks some blocks to be encoded. + */ + public CompositeAMQDataBlock(ByteBuffer encodedBlock, AMQDataBlock[] blocks) + { + this(blocks); + _encodedBlock = encodedBlock; + } + + public AMQDataBlock[] getBlocks() + { + return _blocks; + } + + public ByteBuffer getEncodedBlock() + { + return _encodedBlock; + } + + public long getSize() + { + long frameSize = 0; + for (int i = 0; i < _blocks.length; i++) + { + frameSize += _blocks[i].getSize(); + } + if (_encodedBlock != null) + { + _encodedBlock.rewind(); + frameSize += _encodedBlock.remaining(); + } + return frameSize; + } + + public void writePayload(ByteBuffer buffer) + { + if (_encodedBlock != null) + { + buffer.put(_encodedBlock); + } + for (int i = 0; i < _blocks.length; i++) + { + _blocks[i].writePayload(buffer); + } + } + + public String toString() + { + if (_blocks == null) + { + return "No blocks contained in composite frame"; + } + else + { + StringBuilder buf = new StringBuilder(this.getClass().getName()); + buf.append("{encodedBlock=").append(_encodedBlock); + for (int i = 0 ; i < _blocks.length; i++) + { + buf.append(" ").append(i).append("=[").append(_blocks[i].toString()).append("]"); + } + buf.append("}"); + return buf.toString(); + } + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/Content.java b/Final/java/common/src/main/java/org/apache/qpid/framing/Content.java new file mode 100644 index 0000000000..e5feeec2a4 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/Content.java @@ -0,0 +1,26 @@ +/* + * + * 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.qpid.framing; + +public interface Content +{ + // TODO: New Content class required for AMQP 0-9. +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java new file mode 100644 index 0000000000..be38695384 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java @@ -0,0 +1,106 @@ +/* + * + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +public class ContentBody extends AMQBody +{ + public static final byte TYPE = 3; + + public ByteBuffer payload; + + public ContentBody() + { + } + + public ContentBody(ByteBuffer buffer, long size) throws AMQFrameDecodingException + { + if (size > 0) + { + payload = buffer.slice(); + payload.limit((int) size); + buffer.skip((int) size); + } + + } + + + public ContentBody(ByteBuffer payload) + { + this.payload = payload; + } + + public byte getFrameType() + { + return TYPE; + } + + public int getSize() + { + return (payload == null ? 0 : payload.limit()); + } + + public void writePayload(ByteBuffer buffer) + { + if (payload != null) + { + ByteBuffer copy = payload.duplicate(); + buffer.put(copy.rewind()); + } + } + + protected void populateFromBuffer(ByteBuffer buffer, long size) throws AMQFrameDecodingException + { + if (size > 0) + { + payload = buffer.slice(); + payload.limit((int) size); + buffer.skip((int) size); + } + + } + + public void reduceBufferToFit() + { + if (payload != null && (payload.remaining() < payload.capacity() / 2)) + { + int size = payload.limit(); + ByteBuffer newPayload = ByteBuffer.allocate(size); + + newPayload.put(payload); + newPayload.flip(); + + //reduce reference count on payload + payload.release(); + + payload = newPayload; + } + } + + + + public static AMQFrame createAMQFrame(int channelId, ContentBody body) + { + final AMQFrame frame = new AMQFrame(channelId, body); + return frame; + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/ContentBodyFactory.java b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentBodyFactory.java new file mode 100644 index 0000000000..c42995d148 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentBodyFactory.java @@ -0,0 +1,48 @@ +/* + * + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ContentBodyFactory implements BodyFactory +{ + private static final Logger _log = LoggerFactory.getLogger(AMQMethodBodyFactory.class); + + private static final ContentBodyFactory _instance = new ContentBodyFactory(); + + public static ContentBodyFactory getInstance() + { + return _instance; + } + + private ContentBodyFactory() + { + _log.debug("Creating content body factory"); + } + + public AMQBody createBody(ByteBuffer in, long bodySize) throws AMQFrameDecodingException + { + return new ContentBody(in, bodySize); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java new file mode 100644 index 0000000000..02631a5f88 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java @@ -0,0 +1,123 @@ +/* + * + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +public class ContentHeaderBody extends AMQBody +{ + public static final byte TYPE = 2; + + public int classId; + + public int weight; + + /** unsigned long but java can't handle that anyway when allocating byte array */ + public long bodySize; + + /** must never be null */ + public ContentHeaderProperties properties; + + public ContentHeaderBody() + { + } + + public ContentHeaderBody(ByteBuffer buffer, long size) throws AMQFrameDecodingException + { + classId = buffer.getUnsignedShort(); + weight = buffer.getUnsignedShort(); + bodySize = buffer.getLong(); + int propertyFlags = buffer.getUnsignedShort(); + ContentHeaderPropertiesFactory factory = ContentHeaderPropertiesFactory.getInstance(); + properties = factory.createContentHeaderProperties(classId, propertyFlags, buffer, (int)size - 14); + + } + + + public ContentHeaderBody(ContentHeaderProperties props, int classId) + { + properties = props; + this.classId = classId; + } + + public ContentHeaderBody(int classId, int weight, ContentHeaderProperties props, long bodySize) + { + this(props, classId); + this.weight = weight; + this.bodySize = bodySize; + } + + public byte getFrameType() + { + return TYPE; + } + + protected void populateFromBuffer(ByteBuffer buffer, long size) + throws AMQFrameDecodingException, AMQProtocolVersionException + { + classId = buffer.getUnsignedShort(); + weight = buffer.getUnsignedShort(); + bodySize = buffer.getLong(); + int propertyFlags = buffer.getUnsignedShort(); + ContentHeaderPropertiesFactory factory = ContentHeaderPropertiesFactory.getInstance(); + properties = factory.createContentHeaderProperties(classId, propertyFlags, buffer, (int)size - 14); + } + + /** + * Helper method that is used currently by the persistence layer (by BDB at the moment). + * @param buffer + * @param size + * @return + * @throws AMQFrameDecodingException + */ + public static ContentHeaderBody createFromBuffer(ByteBuffer buffer, long size) + throws AMQFrameDecodingException, AMQProtocolVersionException + { + ContentHeaderBody body = new ContentHeaderBody(buffer, size); + + return body; + } + + public int getSize() + { + return 2 + 2 + 8 + 2 + properties.getPropertyListSize(); + } + + public void writePayload(ByteBuffer buffer) + { + EncodingUtils.writeUnsignedShort(buffer, classId); + EncodingUtils.writeUnsignedShort(buffer, weight); + buffer.putLong(bodySize); + EncodingUtils.writeUnsignedShort(buffer, properties.getPropertyFlags()); + properties.writePropertyListPayload(buffer); + } + + public static AMQFrame createAMQFrame(int channelId, int classId, int weight, BasicContentHeaderProperties properties, + long bodySize) + { + return new AMQFrame(channelId, new ContentHeaderBody(classId, weight, properties, bodySize)); + } + + public static AMQFrame createAMQFrame(int channelId, ContentHeaderBody body) + { + return new AMQFrame(channelId, body); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBodyFactory.java b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBodyFactory.java new file mode 100644 index 0000000000..8d5e2f9fb4 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBodyFactory.java @@ -0,0 +1,50 @@ +/* + * + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ContentHeaderBodyFactory implements BodyFactory +{ + private static final Logger _log = LoggerFactory.getLogger(AMQMethodBodyFactory.class); + + private static final ContentHeaderBodyFactory _instance = new ContentHeaderBodyFactory(); + + public static ContentHeaderBodyFactory getInstance() + { + return _instance; + } + + private ContentHeaderBodyFactory() + { + _log.debug("Creating content header body factory"); + } + + public AMQBody createBody(ByteBuffer in, long bodySize) throws AMQFrameDecodingException + { + // all content headers are the same - it is only the properties that differ. + // the content header body further delegates construction of properties + return new ContentHeaderBody(in, bodySize); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderProperties.java b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderProperties.java new file mode 100644 index 0000000000..561d7852fd --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderProperties.java @@ -0,0 +1,58 @@ +/* + * + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +/** + * There will be an implementation of this interface for each content type. All content types have associated + * header properties and this provides a way to encode and decode them. + */ +public interface ContentHeaderProperties +{ + /** + * Writes the property list to the buffer, in a suitably encoded form. + * @param buffer The buffer to write to + */ + void writePropertyListPayload(ByteBuffer buffer); + + /** + * Populates the properties from buffer. + * @param buffer The buffer to read from. + * @param propertyFlags he property flags. + * @throws AMQFrameDecodingException when the buffer does not contain valid data + */ + void populatePropertiesFromBuffer(ByteBuffer buffer, int propertyFlags, int size) + throws AMQFrameDecodingException; + + /** + * @return the size of the encoded property list in bytes. + */ + int getPropertyListSize(); + + /** + * Gets the property flags. Property flags indicate which properties are set in the list. The + * position and meaning of each flag is defined in the protocol specification for the particular + * content type with which these properties are associated. + * @return flags + */ + int getPropertyFlags(); +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderPropertiesFactory.java b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderPropertiesFactory.java new file mode 100644 index 0000000000..712eb437db --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderPropertiesFactory.java @@ -0,0 +1,57 @@ +/* + * + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +public class ContentHeaderPropertiesFactory +{ + private static final ContentHeaderPropertiesFactory _instance = new ContentHeaderPropertiesFactory(); + + public static ContentHeaderPropertiesFactory getInstance() + { + return _instance; + } + + private ContentHeaderPropertiesFactory() + { + } + + public ContentHeaderProperties createContentHeaderProperties(int classId, int propertyFlags, + ByteBuffer buffer, int size) + throws AMQFrameDecodingException + { + ContentHeaderProperties properties; + // AMQP version change: "Hardwired" version to major=8, minor=0 + // TODO: Change so that the actual version is obtained from + // the ProtocolInitiation object for this session. + if (classId == BasicConsumeBody.getClazz((byte)8, (byte)0)) + { + properties = new BasicContentHeaderProperties(); + } + else + { + throw new AMQFrameDecodingException(null, "Unsupport content header class id: " + classId, null); + } + properties.populatePropertiesFromBuffer(buffer, propertyFlags, size); + return properties; + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/EncodableAMQDataBlock.java b/Final/java/common/src/main/java/org/apache/qpid/framing/EncodableAMQDataBlock.java new file mode 100644 index 0000000000..9cf96e698c --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/EncodableAMQDataBlock.java @@ -0,0 +1,35 @@ +/* + * + * 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.qpid.framing; + +/** + * Marker interface to indicate to MINA that a data block should be encoded with the + * single encoder/decoder that we have defined. + * + * Note that due to a bug in MINA all classes must directly implement this interface, even if + * a superclass implements it. + * TODO: fix MINA so that this is not necessary + * + */ +public interface EncodableAMQDataBlock +{ + +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java b/Final/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java new file mode 100644 index 0000000000..ccba8bd41e --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java @@ -0,0 +1,1028 @@ +/* + * + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.charset.Charset; + +public class EncodingUtils +{ + private static final Logger _logger = LoggerFactory.getLogger(EncodingUtils.class); + + private static final String STRING_ENCODING = "iso8859-15"; + + private static final Charset _charset = Charset.forName("iso8859-15"); + + public static final int SIZEOF_UNSIGNED_SHORT = 2; + public static final int SIZEOF_UNSIGNED_INT = 4; + private static final boolean[] ALL_FALSE_ARRAY = new boolean[8]; + + public static int encodedShortStringLength(String s) + { + if (s == null) + { + return 1; + } + else + { + return (short) (1 + s.length()); + } + } + + public static int encodedShortStringLength(short s) + { + if (s == 0) + { + return 1 + 1; + } + + int len = 0; + if (s < 0) + { + len = 1; + // sloppy - doesn't work of Integer.MIN_VALUE + s = (short) -s; + } + + if (s > 9999) + { + return 1 + 5; + } + else if (s > 999) + { + return 1 + 4; + } + else if (s > 99) + { + return 1 + 3; + } + else if (s > 9) + { + return 1 + 2; + } + else + { + return 1 + 1; + } + + } + + public static int encodedShortStringLength(int i) + { + if (i == 0) + { + return 1 + 1; + } + + int len = 0; + if (i < 0) + { + len = 1; + // sloppy - doesn't work of Integer.MIN_VALUE + i = -i; + } + + // range is now 1 - 2147483647 + if (i < Short.MAX_VALUE) + { + return len + encodedShortStringLength((short) i); + } + else if (i > 999999) + { + return len + 6 + encodedShortStringLength((short) (i / 1000000)); + } + else // if (i > 99999) + { + return len + 5 + encodedShortStringLength((short) (i / 100000)); + } + + } + + public static int encodedShortStringLength(long l) + { + if (l == 0) + { + return 1 + 1; + } + + int len = 0; + if (l < 0) + { + len = 1; + // sloppy - doesn't work of Long.MIN_VALUE + l = -l; + } + + if (l < Integer.MAX_VALUE) + { + return len + encodedShortStringLength((int) l); + } + else if (l > 9999999999L) + { + return len + 10 + encodedShortStringLength((int) (l / 10000000000L)); + } + else + { + return len + 1 + encodedShortStringLength((int) (l / 10L)); + } + + } + + public static int encodedShortStringLength(AMQShortString s) + { + if (s == null) + { + return 1; + } + else + { + return (1 + s.length()); + } + } + + public static int encodedLongStringLength(String s) + { + if (s == null) + { + return 4; + } + else + { + return 4 + s.length(); + } + } + + public static int encodedLongStringLength(char[] s) + { + if (s == null) + { + return 4; + } + else + { + return 4 + s.length; + } + } + + public static int encodedLongstrLength(byte[] bytes) + { + if (bytes == null) + { + return 4; + } + else + { + return 4 + bytes.length; + } + } + + public static int encodedFieldTableLength(FieldTable table) + { + if (table == null) + { + // length is encoded as 4 octets + return 4; + } + else + { + // length of the table plus 4 octets for the length + return (int) table.getEncodedSize() + 4; + } + } + + public static int encodedContentLength(Content table) + { + // TODO: New Content class required for AMQP 0-9. + return 0; + } + + public static void writeShortStringBytes(ByteBuffer buffer, String s) + { + if (s != null) + { + byte[] encodedString = new byte[s.length()]; + char[] cha = s.toCharArray(); + for (int i = 0; i < cha.length; i++) + { + encodedString[i] = (byte) cha[i]; + } + + writeBytes(buffer, encodedString); + } + else + { + // really writing out unsigned byte + buffer.put((byte) 0); + } + } + + public static void writeShortStringBytes(ByteBuffer buffer, AMQShortString s) + { + if (s != null) + { + + s.writeToBuffer(buffer); + } + else + { + // really writing out unsigned byte + buffer.put((byte) 0); + } + } + + public static void writeLongStringBytes(ByteBuffer buffer, String s) + { + assert (s == null) || (s.length() <= 0xFFFE); + if (s != null) + { + int len = s.length(); + writeUnsignedInteger(buffer, s.length()); + byte[] encodedString = new byte[len]; + char[] cha = s.toCharArray(); + for (int i = 0; i < cha.length; i++) + { + encodedString[i] = (byte) cha[i]; + } + + buffer.put(encodedString); + } + else + { + writeUnsignedInteger(buffer, 0); + } + } + + public static void writeLongStringBytes(ByteBuffer buffer, char[] s) + { + assert (s == null) || (s.length <= 0xFFFE); + if (s != null) + { + int len = s.length; + writeUnsignedInteger(buffer, s.length); + byte[] encodedString = new byte[len]; + for (int i = 0; i < s.length; i++) + { + encodedString[i] = (byte) s[i]; + } + + buffer.put(encodedString); + } + else + { + writeUnsignedInteger(buffer, 0); + } + } + + public static void writeLongStringBytes(ByteBuffer buffer, byte[] bytes) + { + assert (bytes == null) || (bytes.length <= 0xFFFE); + if (bytes != null) + { + writeUnsignedInteger(buffer, bytes.length); + buffer.put(bytes); + } + else + { + writeUnsignedInteger(buffer, 0); + } + } + + public static void writeUnsignedByte(ByteBuffer buffer, short b) + { + byte bv = (byte) b; + buffer.put(bv); + } + + public static void writeUnsignedShort(ByteBuffer buffer, int s) + { + // TODO: Is this comparison safe? Do I need to cast RHS to long? + if (s < Short.MAX_VALUE) + { + buffer.putShort((short) s); + } + else + { + short sv = (short) s; + buffer.put((byte) (0xFF & (sv >> 8))); + buffer.put((byte) (0xFF & sv)); + } + } + + public static int unsignedIntegerLength() + { + return 4; + } + + public static void writeUnsignedInteger(ByteBuffer buffer, long l) + { + // TODO: Is this comparison safe? Do I need to cast RHS to long? + if (l < Integer.MAX_VALUE) + { + buffer.putInt((int) l); + } + else + { + int iv = (int) l; + + // FIXME: This *may* go faster if we build this into a local 4-byte array and then + // put the array in a single call. + buffer.put((byte) (0xFF & (iv >> 24))); + buffer.put((byte) (0xFF & (iv >> 16))); + buffer.put((byte) (0xFF & (iv >> 8))); + buffer.put((byte) (0xFF & iv)); + } + } + + public static void writeFieldTableBytes(ByteBuffer buffer, FieldTable table) + { + if (table != null) + { + table.writeToBuffer(buffer); + } + else + { + EncodingUtils.writeUnsignedInteger(buffer, 0); + } + } + + public static void writeContentBytes(ByteBuffer buffer, Content content) + { + // TODO: New Content class required for AMQP 0-9. + } + + public static void writeBooleans(ByteBuffer buffer, boolean[] values) + { + byte packedValue = 0; + for (int i = 0; i < values.length; i++) + { + if (values[i]) + { + packedValue = (byte) (packedValue | (1 << i)); + } + } + + buffer.put(packedValue); + } + + public static void writeBooleans(ByteBuffer buffer, boolean value) + { + + buffer.put(value ? (byte) 1 : (byte) 0); + } + + public static void writeBooleans(ByteBuffer buffer, boolean value0, boolean value1) + { + byte packedValue = value0 ? (byte) 1 : (byte) 0; + + if (value1) + { + packedValue = (byte) (packedValue | (byte) (1 << 1)); + } + + buffer.put(packedValue); + } + + public static void writeBooleans(ByteBuffer buffer, boolean value0, boolean value1, boolean value2) + { + byte packedValue = value0 ? (byte) 1 : (byte) 0; + + if (value1) + { + packedValue = (byte) (packedValue | (byte) (1 << 1)); + } + + if (value2) + { + packedValue = (byte) (packedValue | (byte) (1 << 2)); + } + + buffer.put(packedValue); + } + + public static void writeBooleans(ByteBuffer buffer, boolean value0, boolean value1, boolean value2, boolean value3) + { + byte packedValue = value0 ? (byte) 1 : (byte) 0; + + if (value1) + { + packedValue = (byte) (packedValue | (byte) (1 << 1)); + } + + if (value2) + { + packedValue = (byte) (packedValue | (byte) (1 << 2)); + } + + if (value3) + { + packedValue = (byte) (packedValue | (byte) (1 << 3)); + } + + buffer.put(packedValue); + } + + public static void writeBooleans(ByteBuffer buffer, boolean value0, boolean value1, boolean value2, boolean value3, + boolean value4) + { + byte packedValue = value0 ? (byte) 1 : (byte) 0; + + if (value1) + { + packedValue = (byte) (packedValue | (byte) (1 << 1)); + } + + if (value2) + { + packedValue = (byte) (packedValue | (byte) (1 << 2)); + } + + if (value3) + { + packedValue = (byte) (packedValue | (byte) (1 << 3)); + } + + if (value4) + { + packedValue = (byte) (packedValue | (byte) (1 << 4)); + } + + buffer.put(packedValue); + } + + public static void writeBooleans(ByteBuffer buffer, boolean value0, boolean value1, boolean value2, boolean value3, + boolean value4, boolean value5) + { + byte packedValue = value0 ? (byte) 1 : (byte) 0; + + if (value1) + { + packedValue = (byte) (packedValue | (byte) (1 << 1)); + } + + if (value2) + { + packedValue = (byte) (packedValue | (byte) (1 << 2)); + } + + if (value3) + { + packedValue = (byte) (packedValue | (byte) (1 << 3)); + } + + if (value4) + { + packedValue = (byte) (packedValue | (byte) (1 << 4)); + } + + if (value5) + { + packedValue = (byte) (packedValue | (byte) (1 << 5)); + } + + buffer.put(packedValue); + } + + public static void writeBooleans(ByteBuffer buffer, boolean value0, boolean value1, boolean value2, boolean value3, + boolean value4, boolean value5, boolean value6) + { + byte packedValue = value0 ? (byte) 1 : (byte) 0; + + if (value1) + { + packedValue = (byte) (packedValue | (byte) (1 << 1)); + } + + if (value2) + { + packedValue = (byte) (packedValue | (byte) (1 << 2)); + } + + if (value3) + { + packedValue = (byte) (packedValue | (byte) (1 << 3)); + } + + if (value4) + { + packedValue = (byte) (packedValue | (byte) (1 << 4)); + } + + if (value5) + { + packedValue = (byte) (packedValue | (byte) (1 << 5)); + } + + if (value6) + { + packedValue = (byte) (packedValue | (byte) (1 << 6)); + } + + buffer.put(packedValue); + } + + public static void writeBooleans(ByteBuffer buffer, boolean value0, boolean value1, boolean value2, boolean value3, + boolean value4, boolean value5, boolean value6, boolean value7) + { + byte packedValue = value0 ? (byte) 1 : (byte) 0; + + if (value1) + { + packedValue = (byte) (packedValue | (byte) (1 << 1)); + } + + if (value2) + { + packedValue = (byte) (packedValue | (byte) (1 << 2)); + } + + if (value3) + { + packedValue = (byte) (packedValue | (byte) (1 << 3)); + } + + if (value4) + { + packedValue = (byte) (packedValue | (byte) (1 << 4)); + } + + if (value5) + { + packedValue = (byte) (packedValue | (byte) (1 << 5)); + } + + if (value6) + { + packedValue = (byte) (packedValue | (byte) (1 << 6)); + } + + if (value7) + { + packedValue = (byte) (packedValue | (byte) (1 << 7)); + } + + buffer.put(packedValue); + } + + /** + * This is used for writing longstrs. + * + * @param buffer + * @param data + */ + public static void writeLongstr(ByteBuffer buffer, byte[] data) + { + if (data != null) + { + writeUnsignedInteger(buffer, data.length); + buffer.put(data); + } + else + { + writeUnsignedInteger(buffer, 0); + } + } + + public static void writeTimestamp(ByteBuffer buffer, long timestamp) + { + writeLong(buffer, timestamp); + } + + public static boolean[] readBooleans(ByteBuffer buffer) + { + final byte packedValue = buffer.get(); + if (packedValue == 0) + { + return ALL_FALSE_ARRAY; + } + + final boolean[] result = new boolean[8]; + + result[0] = ((packedValue & 1) != 0); + result[1] = ((packedValue & (1 << 1)) != 0); + result[2] = ((packedValue & (1 << 2)) != 0); + result[3] = ((packedValue & (1 << 3)) != 0); + if ((packedValue & 0xF0) == 0) + { + result[0] = ((packedValue & 1) != 0); + } + + result[4] = ((packedValue & (1 << 4)) != 0); + result[5] = ((packedValue & (1 << 5)) != 0); + result[6] = ((packedValue & (1 << 6)) != 0); + result[7] = ((packedValue & (1 << 7)) != 0); + + return result; + } + + public static FieldTable readFieldTable(ByteBuffer buffer) throws AMQFrameDecodingException + { + long length = buffer.getUnsignedInt(); + if (length == 0) + { + return null; + } + else + { + return FieldTableFactory.newFieldTable(buffer, length); + } + } + + public static Content readContent(ByteBuffer buffer) throws AMQFrameDecodingException + { + // TODO: New Content class required for AMQP 0-9. + return null; + } + + public static AMQShortString readAMQShortString(ByteBuffer buffer) + { + return AMQShortString.readFromBuffer(buffer); + + } + + public static String readShortString(ByteBuffer buffer) + { + short length = buffer.getUnsigned(); + if (length == 0) + { + return null; + } + else + { + // this may seem rather odd to declare two array but testing has shown + // that constructing a string from a byte array is 5 (five) times slower + // than constructing one from a char array. + // this approach here is valid since we know that all the chars are + // ASCII (0-127) + byte[] stringBytes = new byte[length]; + buffer.get(stringBytes, 0, length); + char[] stringChars = new char[length]; + for (int i = 0; i < stringChars.length; i++) + { + stringChars[i] = (char) stringBytes[i]; + } + + return new String(stringChars); + } + } + + public static String readLongString(ByteBuffer buffer) + { + long length = buffer.getUnsignedInt(); + if (length == 0) + { + return ""; + } + else + { + // this may seem rather odd to declare two array but testing has shown + // that constructing a string from a byte array is 5 (five) times slower + // than constructing one from a char array. + // this approach here is valid since we know that all the chars are + // ASCII (0-127) + byte[] stringBytes = new byte[(int) length]; + buffer.get(stringBytes, 0, (int) length); + char[] stringChars = new char[(int) length]; + for (int i = 0; i < stringChars.length; i++) + { + stringChars[i] = (char) stringBytes[i]; + } + + return new String(stringChars); + } + } + + public static byte[] readLongstr(ByteBuffer buffer) + { + long length = buffer.getUnsignedInt(); + if (length == 0) + { + return null; + } + else + { + byte[] result = new byte[(int) length]; + buffer.get(result); + + return result; + } + } + + public static long readTimestamp(ByteBuffer buffer) + { + // Discard msb from AMQ timestamp + // buffer.getUnsignedInt(); + return buffer.getLong(); + } + + static byte[] hexToByteArray(String id) + { + // Should check param for null, long enough for this check, upper-case and trailing char + String s = (id.charAt(1) == 'x') ? id.substring(2) : id; // strip 0x + + int len = s.length(); + int byte_len = len / 2; + byte[] b = new byte[byte_len]; + + for (int i = 0; i < byte_len; i++) + { + // fixme: refine these repetitive subscript calcs. + int ch = i * 2; + + byte b1 = Byte.parseByte(s.substring(ch, ch + 1), 16); + byte b2 = Byte.parseByte(s.substring(ch + 1, ch + 2), 16); + + b[i] = (byte) ((b1 * 16) + b2); + } + + return (b); + } + + public static char[] convertToHexCharArray(byte[] from) + { + int length = from.length; + char[] result_buff = new char[(length * 2) + 2]; + + result_buff[0] = '0'; + result_buff[1] = 'x'; + + int bite; + int dest = 2; + + for (int i = 0; i < length; i++) + { + bite = from[i]; + + if (bite < 0) + { + bite += 256; + } + + result_buff[dest++] = hex_chars[bite >> 4]; + result_buff[dest++] = hex_chars[bite & 0x0f]; + } + + return (result_buff); + } + + public static String convertToHexString(byte[] from) + { + return (new String(convertToHexCharArray(from))); + } + + public static String convertToHexString(ByteBuffer bb) + { + int size = bb.limit(); + + byte[] from = new byte[size]; + + // Is this not the same. + // bb.get(from, 0, length); + for (int i = 0; i < size; i++) + { + from[i] = bb.get(i); + } + + return (new String(convertToHexCharArray(from))); + } + + private static char[] hex_chars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + // **** new methods + + // AMQP_BOOLEAN_PROPERTY_PREFIX + + public static void writeBoolean(ByteBuffer buffer, Boolean aBoolean) + { + buffer.put((byte) (aBoolean ? 1 : 0)); + } + + public static boolean readBoolean(ByteBuffer buffer) + { + byte packedValue = buffer.get(); + + return (packedValue == 1); + } + + public static int encodedBooleanLength() + { + return 1; + } + + // AMQP_BYTE_PROPERTY_PREFIX + public static void writeByte(ByteBuffer buffer, Byte aByte) + { + buffer.put(aByte); + } + + public static byte readByte(ByteBuffer buffer) + { + return buffer.get(); + } + + public static int encodedByteLength() + { + return 1; + } + + // AMQP_SHORT_PROPERTY_PREFIX + public static void writeShort(ByteBuffer buffer, Short aShort) + { + buffer.putShort(aShort); + } + + public static short readShort(ByteBuffer buffer) + { + return buffer.getShort(); + } + + public static int encodedShortLength() + { + return 2; + } + + // INTEGER_PROPERTY_PREFIX + public static void writeInteger(ByteBuffer buffer, Integer aInteger) + { + buffer.putInt(aInteger); + } + + public static int readInteger(ByteBuffer buffer) + { + return buffer.getInt(); + } + + public static int encodedIntegerLength() + { + return 4; + } + + // AMQP_LONG_PROPERTY_PREFIX + public static void writeLong(ByteBuffer buffer, Long aLong) + { + buffer.putLong(aLong); + } + + public static long readLong(ByteBuffer buffer) + { + return buffer.getLong(); + } + + public static int encodedLongLength() + { + return 8; + } + + // Float_PROPERTY_PREFIX + public static void writeFloat(ByteBuffer buffer, Float aFloat) + { + buffer.putFloat(aFloat); + } + + public static float readFloat(ByteBuffer buffer) + { + return buffer.getFloat(); + } + + public static int encodedFloatLength() + { + return 4; + } + + // Double_PROPERTY_PREFIX + public static void writeDouble(ByteBuffer buffer, Double aDouble) + { + buffer.putDouble(aDouble); + } + + public static double readDouble(ByteBuffer buffer) + { + return buffer.getDouble(); + } + + public static int encodedDoubleLength() + { + return 8; + } + + public static byte[] readBytes(ByteBuffer buffer) + { + short length = buffer.getUnsigned(); + if (length == 0) + { + return null; + } + else + { + byte[] dataBytes = new byte[length]; + buffer.get(dataBytes, 0, length); + + return dataBytes; + } + } + + public static void writeBytes(ByteBuffer buffer, byte[] data) + { + if (data != null) + { + // TODO: check length fits in an unsigned byte + writeUnsignedByte(buffer, (short) data.length); + buffer.put(data); + } + else + { + // really writing out unsigned byte + buffer.put((byte) 0); + } + } + + // CHAR_PROPERTY + public static int encodedCharLength() + { + return encodedByteLength(); + } + + public static char readChar(ByteBuffer buffer) + { + // This is valid as we know that the Character is ASCII 0..127 + return (char) buffer.get(); + } + + public static void writeChar(ByteBuffer buffer, char character) + { + // This is valid as we know that the Character is ASCII 0..127 + writeByte(buffer, (byte) character); + } + + public static long readLongAsShortString(ByteBuffer buffer) + { + short length = buffer.getUnsigned(); + short pos = 0; + if (length == 0) + { + return 0L; + } + + byte digit = buffer.get(); + boolean isNegative; + long result = 0; + if (digit == (byte) '-') + { + isNegative = true; + pos++; + digit = buffer.get(); + } + else + { + isNegative = false; + } + + result = digit - (byte) '0'; + pos++; + + while (pos < length) + { + pos++; + digit = buffer.get(); + result = (result << 3) + (result << 1); + result += digit - (byte) '0'; + } + + return result; + } + + public static long readUnsignedInteger(ByteBuffer buffer) + { + long l = 0xFF & buffer.get(); + l <<= 8; + l = l | (0xFF & buffer.get()); + l <<= 8; + l = l | (0xFF & buffer.get()); + l <<= 8; + l = l | (0xFF & buffer.get()); + + return l; + } + +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java b/Final/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java new file mode 100644 index 0000000000..46b10b5963 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java @@ -0,0 +1,1017 @@ +/* + * + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +import org.apache.qpid.AMQPInvalidClassException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.math.BigDecimal; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +// extends FieldTable +public class FieldTable +{ + private static final Logger _logger = LoggerFactory.getLogger(FieldTable.class); + private static final String STRICT_AMQP = "STRICT_AMQP"; + private final boolean _strictAMQP = Boolean.valueOf(System.getProperty(STRICT_AMQP, "false")); + + private ByteBuffer _encodedForm; + private LinkedHashMap<AMQShortString, AMQTypedValue> _properties; + private long _encodedSize; + private static final int INITIAL_HASHMAP_CAPACITY = 16; + private static final int INITIAL_ENCODED_FORM_SIZE = 256; + + public FieldTable() + { + super(); + // _encodedForm = ByteBuffer.allocate(INITIAL_ENCODED_FORM_SIZE); + // _encodedForm.setAutoExpand(true); + // _encodedForm.limit(0); + } + + /** + * Construct a new field table. + * + * @param buffer the buffer from which to read data. The length byte must be read already + * @param length the length of the field table. Must be > 0. + * + * @throws AMQFrameDecodingException if there is an error decoding the table + */ + public FieldTable(ByteBuffer buffer, long length) throws AMQFrameDecodingException + { + this(); + _encodedForm = buffer.slice(); + _encodedForm.limit((int) length); + _encodedSize = length; + buffer.skip((int) length); + } + + private AMQTypedValue getProperty(AMQShortString string) + { + checkPropertyName(string); + + synchronized (this) + { + if (_properties == null) + { + if (_encodedForm == null) + { + return null; + } + else + { + populateFromBuffer(); + } + } + } + + if (_properties == null) + { + return null; + } + else + { + return _properties.get(string); + } + } + + private void populateFromBuffer() + { + try + { + setFromBuffer(_encodedForm, _encodedSize); + } + catch (AMQFrameDecodingException e) + { + _logger.error("Error decoding FieldTable in deferred decoding mode ", e); + throw new IllegalArgumentException(e); + } + } + + private AMQTypedValue setProperty(AMQShortString key, AMQTypedValue val) + { + checkPropertyName(key); + initMapIfNecessary(); + if (_properties.containsKey(key)) + { + _encodedForm = null; + + if (val == null) + { + return removeKey(key); + } + } + else if ((_encodedForm != null) && (val != null)) + { + EncodingUtils.writeShortStringBytes(_encodedForm, key); + val.writeToBuffer(_encodedForm); + + } + else if (val == null) + { + return null; + } + + AMQTypedValue oldVal = _properties.put(key, val); + if (oldVal != null) + { + _encodedSize -= oldVal.getEncodingSize(); + } + else + { + _encodedSize += EncodingUtils.encodedShortStringLength(key) + 1; + } + + _encodedSize += val.getEncodingSize(); + + return oldVal; + } + + private void initMapIfNecessary() + { + synchronized (this) + { + if (_properties == null) + { + if ((_encodedForm == null) || (_encodedSize == 0)) + { + _properties = new LinkedHashMap<AMQShortString, AMQTypedValue>(); + } + else + { + populateFromBuffer(); + } + } + + } + } + + public Boolean getBoolean(String string) + { + return getBoolean(new AMQShortString(string)); + } + + public Boolean getBoolean(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.BOOLEAN)) + { + return (Boolean) value.getValue(); + } + else + { + return null; + } + } + + public Byte getByte(String string) + { + return getByte(new AMQShortString(string)); + } + + public Byte getByte(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.BYTE)) + { + return (Byte) value.getValue(); + } + else + { + return null; + } + } + + public Short getShort(String string) + { + return getShort(new AMQShortString(string)); + } + + public Short getShort(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.SHORT)) + { + return (Short) value.getValue(); + } + else + { + return null; + } + } + + public Integer getInteger(String string) + { + return getInteger(new AMQShortString(string)); + } + + public Integer getInteger(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.INT)) + { + return (Integer) value.getValue(); + } + else + { + return null; + } + } + + public Long getLong(String string) + { + return getLong(new AMQShortString(string)); + } + + public Long getLong(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.LONG)) + { + return (Long) value.getValue(); + } + else + { + return null; + } + } + + public Float getFloat(String string) + { + return getFloat(new AMQShortString(string)); + } + + public Float getFloat(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.FLOAT)) + { + return (Float) value.getValue(); + } + else + { + return null; + } + } + + public Double getDouble(String string) + { + return getDouble(new AMQShortString(string)); + } + + public Double getDouble(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.DOUBLE)) + { + return (Double) value.getValue(); + } + else + { + return null; + } + } + + public String getString(String string) + { + return getString(new AMQShortString(string)); + } + + public String getString(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && ((value.getType() == AMQType.WIDE_STRING) || (value.getType() == AMQType.ASCII_STRING))) + { + return (String) value.getValue(); + } + else if ((value != null) && (value.getValue() != null) && !(value.getValue() instanceof byte[])) + { + return String.valueOf(value.getValue()); + } + else + { + return null; + } + + } + + public Character getCharacter(String string) + { + return getCharacter(new AMQShortString(string)); + } + + public Character getCharacter(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.ASCII_CHARACTER)) + { + return (Character) value.getValue(); + } + else + { + return null; + } + } + + public byte[] getBytes(String string) + { + return getBytes(new AMQShortString(string)); + } + + public byte[] getBytes(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.BINARY)) + { + return (byte[]) value.getValue(); + } + else + { + return null; + } + } + + public Object getObject(String string) + { + return getObject(new AMQShortString(string)); + } + + public Object getObject(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if (value != null) + { + return value.getValue(); + } + else + { + return value; + } + + } + + public Long getTimestamp(AMQShortString name) + { + AMQTypedValue value = getProperty(name); + if ((value != null) && (value.getType() == AMQType.TIMESTAMP)) + { + return (Long) value.getValue(); + } + else + { + return null; + } + } + + public BigDecimal getDecimal(AMQShortString propertyName) + { + AMQTypedValue value = getProperty(propertyName); + if ((value != null) && (value.getType() == AMQType.DECIMAL)) + { + return (BigDecimal) value.getValue(); + } + else + { + return null; + } + } + + // ************ Setters + public Object setBoolean(String string, Boolean b) + { + return setBoolean(new AMQShortString(string), b); + } + + public Object setBoolean(AMQShortString string, Boolean b) + { + return setProperty(string, AMQType.BOOLEAN.asTypedValue(b)); + } + + public Object setByte(String string, Byte b) + { + return setByte(new AMQShortString(string), b); + } + + public Object setByte(AMQShortString string, Byte b) + { + return setProperty(string, AMQType.BYTE.asTypedValue(b)); + } + + public Object setShort(String string, Short i) + { + return setShort(new AMQShortString(string), i); + } + + public Object setShort(AMQShortString string, Short i) + { + return setProperty(string, AMQType.SHORT.asTypedValue(i)); + } + + public Object setInteger(String string, Integer i) + { + return setInteger(new AMQShortString(string), i); + } + + public Object setInteger(AMQShortString string, Integer i) + { + return setProperty(string, AMQType.INT.asTypedValue(i)); + } + + public Object setLong(String string, Long l) + { + return setLong(new AMQShortString(string), l); + } + + public Object setLong(AMQShortString string, Long l) + { + return setProperty(string, AMQType.LONG.asTypedValue(l)); + } + + public Object setFloat(String string, Float f) + { + return setFloat(new AMQShortString(string), f); + } + + public Object setFloat(AMQShortString string, Float v) + { + return setProperty(string, AMQType.FLOAT.asTypedValue(v)); + } + + public Object setDouble(String string, Double d) + { + return setDouble(new AMQShortString(string), d); + } + + public Object setDouble(AMQShortString string, Double v) + { + return setProperty(string, AMQType.DOUBLE.asTypedValue(v)); + } + + public Object setString(String string, String s) + { + return setString(new AMQShortString(string), s); + } + + public Object setAsciiString(AMQShortString string, String value) + { + if (value == null) + { + return setProperty(string, AMQType.VOID.asTypedValue(null)); + } + else + { + return setProperty(string, AMQType.ASCII_STRING.asTypedValue(value)); + } + } + + public Object setString(AMQShortString string, String value) + { + if (value == null) + { + return setProperty(string, AMQType.VOID.asTypedValue(null)); + } + else + { + return setProperty(string, AMQType.LONG_STRING.asTypedValue(value)); + } + } + + public Object setChar(String string, char c) + { + return setChar(new AMQShortString(string), c); + } + + public Object setChar(AMQShortString string, char c) + { + return setProperty(string, AMQType.ASCII_CHARACTER.asTypedValue(c)); + } + + public Object setBytes(String string, byte[] b) + { + return setBytes(new AMQShortString(string), b); + } + + public Object setBytes(AMQShortString string, byte[] bytes) + { + return setProperty(string, AMQType.BINARY.asTypedValue(bytes)); + } + + public Object setBytes(String string, byte[] bytes, int start, int length) + { + return setBytes(new AMQShortString(string), bytes, start, length); + } + + public Object setBytes(AMQShortString string, byte[] bytes, int start, int length) + { + byte[] newBytes = new byte[length]; + System.arraycopy(bytes, start, newBytes, 0, length); + + return setBytes(string, bytes); + } + + public Object setObject(String string, Object o) + { + return setObject(new AMQShortString(string), o); + } + + public Object setTimestamp(AMQShortString string, long datetime) + { + return setProperty(string, AMQType.TIMESTAMP.asTypedValue(datetime)); + } + + public Object setDecimal(AMQShortString string, BigDecimal decimal) + { + if (decimal.longValue() > Integer.MAX_VALUE) + { + throw new UnsupportedOperationException("AMQP doesnot support decimals larger than " + Integer.MAX_VALUE); + } + + if (decimal.scale() > Byte.MAX_VALUE) + { + throw new UnsupportedOperationException("AMQP doesnot support decimal scales larger than " + Byte.MAX_VALUE); + } + + return setProperty(string, AMQType.DECIMAL.asTypedValue(decimal)); + } + + public Object setVoid(AMQShortString string) + { + return setProperty(string, AMQType.VOID.asTypedValue(null)); + } + + public Object setObject(AMQShortString string, Object object) + { + if (object instanceof Boolean) + { + return setBoolean(string, (Boolean) object); + } + else if (object instanceof Byte) + { + return setByte(string, (Byte) object); + } + else if (object instanceof Short) + { + return setShort(string, (Short) object); + } + else if (object instanceof Integer) + { + return setInteger(string, (Integer) object); + } + else if (object instanceof Long) + { + return setLong(string, (Long) object); + } + else if (object instanceof Float) + { + return setFloat(string, (Float) object); + } + else if (object instanceof Double) + { + return setDouble(string, (Double) object); + } + else if (object instanceof String) + { + return setString(string, (String) object); + } + else if (object instanceof Character) + { + return setChar(string, (Character) object); + } + else if (object instanceof byte[]) + { + return setBytes(string, (byte[]) object); + } + + throw new AMQPInvalidClassException("Only Primatives objects allowed Object is:" + object.getClass()); + } + + public boolean isNullStringValue(String name) + { + AMQTypedValue value = getProperty(new AMQShortString(name)); + + return (value != null) && (value.getType() == AMQType.VOID); + } + + // ***** Methods + + public Enumeration getPropertyNames() + { + return Collections.enumeration(keys()); + } + + public boolean propertyExists(AMQShortString propertyName) + { + return itemExists(propertyName); + } + + public boolean propertyExists(String propertyName) + { + return itemExists(propertyName); + } + + public boolean itemExists(AMQShortString propertyName) + { + checkPropertyName(propertyName); + initMapIfNecessary(); + + return _properties.containsKey(propertyName); + } + + public boolean itemExists(String string) + { + return itemExists(new AMQShortString(string)); + } + + public String toString() + { + initMapIfNecessary(); + + return _properties.toString(); + } + + private void checkPropertyName(AMQShortString propertyName) + { + if (propertyName == null) + { + throw new IllegalArgumentException("Property name must not be null"); + } + else if (propertyName.length() == 0) + { + throw new IllegalArgumentException("Property name must not be the empty string"); + } + + if (_strictAMQP) + { + checkIdentiferFormat(propertyName); + } + } + + protected static void checkIdentiferFormat(AMQShortString propertyName) + { + // AMQP Spec: 4.2.5.5 Field Tables + // Guidelines for implementers: + // * Field names MUST start with a letter, '$' or '#' and may continue with + // letters, '$' or '#', digits, or underlines, to a maximum length of 128 + // characters. + // * The server SHOULD validate field names and upon receiving an invalid + // field name, it SHOULD signal a connection exception with reply code + // 503 (syntax error). Conformance test: amq_wlp_table_01. + // * A peer MUST handle duplicate fields by using only the first instance. + + // AMQP length limit + if (propertyName.length() > 128) + { + throw new IllegalArgumentException("AMQP limits property names to 128 characters"); + } + + // AMQ start character + if (!(Character.isLetter(propertyName.charAt(0)) || (propertyName.charAt(0) == '$') + || (propertyName.charAt(0) == '#') || (propertyName.charAt(0) == '_'))) // Not official AMQP added for JMS. + { + throw new IllegalArgumentException("Identifier '" + propertyName + + "' does not start with a valid AMQP start character"); + } + } + + // ************************* Byte Buffer Processing + + public void writeToBuffer(ByteBuffer buffer) + { + final boolean trace = _logger.isTraceEnabled(); + + if (trace) + { + _logger.trace("FieldTable::writeToBuffer: Writing encoded length of " + getEncodedSize() + "..."); + if (_properties != null) + { + _logger.trace(_properties.toString()); + } + } + + EncodingUtils.writeUnsignedInteger(buffer, getEncodedSize()); + + putDataInBuffer(buffer); + } + + public byte[] getDataAsBytes() + { + final int encodedSize = (int) getEncodedSize(); + final ByteBuffer buffer = ByteBuffer.allocate(encodedSize); // FIXME XXX: Is cast a problem? + + putDataInBuffer(buffer); + + final byte[] result = new byte[encodedSize]; + buffer.flip(); + buffer.get(result); + buffer.release(); + + return result; + } + + public long getEncodedSize() + { + return _encodedSize; + } + + private void recalculateEncodedSize() + { + + int encodedSize = 0; + if (_properties != null) + { + for (Map.Entry<AMQShortString, AMQTypedValue> e : _properties.entrySet()) + { + encodedSize += EncodingUtils.encodedShortStringLength(e.getKey()); + encodedSize++; // the byte for the encoding Type + encodedSize += e.getValue().getEncodingSize(); + + } + } + + _encodedSize = encodedSize; + } + + public void addAll(FieldTable fieldTable) + { + initMapIfNecessary(); + _encodedForm = null; + _properties.putAll(fieldTable._properties); + recalculateEncodedSize(); + } + + public static interface FieldTableElementProcessor + { + public boolean processElement(String propertyName, AMQTypedValue value); + + public Object getResult(); + } + + public Object processOverElements(FieldTableElementProcessor processor) + { + initMapIfNecessary(); + if (_properties != null) + { + for (Map.Entry<AMQShortString, AMQTypedValue> e : _properties.entrySet()) + { + boolean result = processor.processElement(e.getKey().toString(), e.getValue()); + if (!result) + { + break; + } + } + } + + return processor.getResult(); + + } + + public int size() + { + initMapIfNecessary(); + + return _properties.size(); + + } + + public boolean isEmpty() + { + return size() == 0; + } + + public boolean containsKey(AMQShortString key) + { + initMapIfNecessary(); + + return _properties.containsKey(key); + } + + public boolean containsKey(String key) + { + return containsKey(new AMQShortString(key)); + } + + public Set<String> keys() + { + initMapIfNecessary(); + Set<String> keys = new LinkedHashSet<String>(); + for (AMQShortString key : _properties.keySet()) + { + keys.add(key.toString()); + } + + return keys; + } + + public Object get(AMQShortString key) + { + + return getObject(key); + } + + public Object put(AMQShortString key, Object value) + { + return setObject(key, value); + } + + public Object remove(String key) + { + + return remove(new AMQShortString(key)); + + } + + public Object remove(AMQShortString key) + { + AMQTypedValue val = removeKey(key); + + return (val == null) ? null : val.getValue(); + + } + + public AMQTypedValue removeKey(AMQShortString key) + { + initMapIfNecessary(); + _encodedForm = null; + AMQTypedValue value = _properties.remove(key); + if (value == null) + { + return null; + } + else + { + _encodedSize -= EncodingUtils.encodedShortStringLength(key); + _encodedSize--; + _encodedSize -= value.getEncodingSize(); + + return value; + } + + } + + public void clear() + { + initMapIfNecessary(); + _encodedForm = null; + _properties.clear(); + _encodedSize = 0; + } + + public Set<AMQShortString> keySet() + { + initMapIfNecessary(); + + return _properties.keySet(); + } + + private void putDataInBuffer(ByteBuffer buffer) + { + + if (_encodedForm != null) + { + + if (_encodedForm.position() != 0) + { + _encodedForm.flip(); + } + // _encodedForm.limit((int)getEncodedSize()); + + buffer.put(_encodedForm); + } + else if (_properties != null) + { + final Iterator<Map.Entry<AMQShortString, AMQTypedValue>> it = _properties.entrySet().iterator(); + + // If there are values then write out the encoded Size... could check _encodedSize != 0 + // write out the total length, which we have kept up to date as data is added + + while (it.hasNext()) + { + final Map.Entry<AMQShortString, AMQTypedValue> me = it.next(); + try + { + if (_logger.isTraceEnabled()) + { + _logger.trace("Writing Property:" + me.getKey() + " Type:" + me.getValue().getType() + " Value:" + + me.getValue().getValue()); + _logger.trace("Buffer Position:" + buffer.position() + " Remaining:" + buffer.remaining()); + } + + // Write the actual parameter name + EncodingUtils.writeShortStringBytes(buffer, me.getKey()); + me.getValue().writeToBuffer(buffer); + } + catch (Exception e) + { + if (_logger.isTraceEnabled()) + { + _logger.trace("Exception thrown:" + e); + _logger.trace("Writing Property:" + me.getKey() + " Type:" + me.getValue().getType() + " Value:" + + me.getValue().getValue()); + _logger.trace("Buffer Position:" + buffer.position() + " Remaining:" + buffer.remaining()); + } + + throw new RuntimeException(e); + } + } + } + } + + private void setFromBuffer(ByteBuffer buffer, long length) throws AMQFrameDecodingException + { + + final boolean trace = _logger.isTraceEnabled(); + if (length > 0) + { + + final int expectedRemaining = buffer.remaining() - (int) length; + + _properties = new LinkedHashMap<AMQShortString, AMQTypedValue>(INITIAL_HASHMAP_CAPACITY); + + do + { + + final AMQShortString key = EncodingUtils.readAMQShortString(buffer); + AMQTypedValue value = AMQTypedValue.readFromBuffer(buffer); + + if (trace) + { + _logger.trace("FieldTable::PropFieldTable(buffer," + length + "): Read type '" + value.getType() + + "', key '" + key + "', value '" + value.getValue() + "'"); + } + + _properties.put(key, value); + + } + while (buffer.remaining() > expectedRemaining); + + } + + _encodedSize = length; + + if (trace) + { + _logger.trace("FieldTable::FieldTable(buffer," + length + "): Done."); + } + } + + public int hashCode() + { + initMapIfNecessary(); + + return _properties.hashCode(); + } + + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (o == null) + { + return false; + } + + if (!(o instanceof FieldTable)) + { + return false; + } + + initMapIfNecessary(); + + FieldTable f = (FieldTable) o; + f.initMapIfNecessary(); + + return _properties.equals(f._properties); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/FieldTableFactory.java b/Final/java/common/src/main/java/org/apache/qpid/framing/FieldTableFactory.java new file mode 100644 index 0000000000..e9d75137ef --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/FieldTableFactory.java @@ -0,0 +1,38 @@ +/* + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +public class FieldTableFactory +{ + public static FieldTable newFieldTable() + { + return new FieldTable(); + } + + public static FieldTable newFieldTable(ByteBuffer byteBuffer, long length) throws AMQFrameDecodingException + { + return new FieldTable(byteBuffer, length); + } + + +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java b/Final/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java new file mode 100644 index 0000000000..7246c4a1cf --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java @@ -0,0 +1,71 @@ +/* + * + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +public class HeartbeatBody extends AMQBody +{ + public static final byte TYPE = 8; + public static AMQFrame FRAME = new HeartbeatBody().toFrame(); + + public HeartbeatBody() + { + + } + + public HeartbeatBody(ByteBuffer buffer, long size) + { + if(size > 0) + { + //allow other implementations to have a payload, but ignore it: + buffer.skip((int) size); + } + } + + public byte getFrameType() + { + return TYPE; + } + + protected int getSize() + { + return 0;//heartbeats we generate have no payload + } + + protected void writePayload(ByteBuffer buffer) + { + } + + protected void populateFromBuffer(ByteBuffer buffer, long size) throws AMQFrameDecodingException + { + if(size > 0) + { + //allow other implementations to have a payload, but ignore it: + buffer.skip((int) size); + } + } + + public AMQFrame toFrame() + { + return new AMQFrame(0, this); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBodyFactory.java b/Final/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBodyFactory.java new file mode 100644 index 0000000000..c7ada708dc --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBodyFactory.java @@ -0,0 +1,31 @@ +/* + * + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +public class HeartbeatBodyFactory implements BodyFactory +{ + public AMQBody createBody(ByteBuffer in, long bodySize) throws AMQFrameDecodingException + { + return new HeartbeatBody(); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/MethodConverter_8_0.java b/Final/java/common/src/main/java/org/apache/qpid/framing/MethodConverter_8_0.java new file mode 100644 index 0000000000..9a113f452b --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/MethodConverter_8_0.java @@ -0,0 +1,125 @@ +/*
+ *
+ * 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.qpid.framing;
+
+import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.framing.abstraction.AbstractMethodConverter;
+
+import org.apache.mina.common.ByteBuffer;
+
+public class MethodConverter_8_0 extends AbstractMethodConverter implements ProtocolVersionMethodConverter
+{
+ private int _basicPublishClassId;
+ private int _basicPublishMethodId;
+
+ public MethodConverter_8_0()
+ {
+ super((byte)8,(byte)0);
+
+
+ }
+
+ public AMQBody convertToBody(ContentChunk contentChunk)
+ {
+ return new ContentBody(contentChunk.getData());
+ }
+
+ public ContentChunk convertToContentChunk(AMQBody body)
+ {
+ final ContentBody contentBodyChunk = (ContentBody) body;
+
+ return new ContentChunk()
+ {
+
+ public int getSize()
+ {
+ return contentBodyChunk.getSize();
+ }
+
+ public ByteBuffer getData()
+ {
+ return contentBodyChunk.payload;
+ }
+
+ public void reduceToFit()
+ {
+ contentBodyChunk.reduceBufferToFit();
+ }
+ };
+
+ }
+
+ public void configure()
+ {
+
+ _basicPublishClassId = BasicPublishBody.getClazz(getProtocolMajorVersion(),getProtocolMinorVersion());
+ _basicPublishMethodId = BasicPublishBody.getMethod(getProtocolMajorVersion(),getProtocolMinorVersion());
+
+ }
+
+ public MessagePublishInfo convertToInfo(AMQMethodBody methodBody)
+ {
+ final BasicPublishBody body = (BasicPublishBody) methodBody;
+
+ return new MessagePublishInfo()
+ {
+
+ public AMQShortString getExchange()
+ {
+ return body.getExchange();
+ }
+
+ public boolean isImmediate()
+ {
+ return body.getImmediate();
+ }
+
+ public boolean isMandatory()
+ {
+ return body.getMandatory();
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return body.getRoutingKey();
+ }
+ };
+
+ }
+
+ public AMQMethodBody convertToBody(MessagePublishInfo info)
+ {
+
+ return new BasicPublishBody(getProtocolMajorVersion(),
+ getProtocolMinorVersion(),
+ _basicPublishClassId,
+ _basicPublishMethodId,
+ info.getExchange(),
+ info.isImmediate(),
+ info.isMandatory(),
+ info.getRoutingKey(),
+ 0) ; // ticket
+
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java b/Final/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java new file mode 100644 index 0000000000..8b40fe72eb --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java @@ -0,0 +1,194 @@ +/* + * + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; +import org.apache.mina.common.IoSession; +import org.apache.mina.filter.codec.ProtocolDecoderOutput; +import org.apache.qpid.AMQException; + +import java.io.UnsupportedEncodingException; + +public class ProtocolInitiation extends AMQDataBlock implements EncodableAMQDataBlock +{ + + // TODO: generate these constants automatically from the xml protocol spec file + public static final byte[] AMQP_HEADER = new byte[]{(byte)'A',(byte)'M',(byte)'Q',(byte)'P'}; + + private static final byte CURRENT_PROTOCOL_CLASS = 1; + private static final byte TCP_PROTOCOL_INSTANCE = 1; + + public final byte[] _protocolHeader; + public final byte _protocolClass; + public final byte _protocolInstance; + public final byte _protocolMajor; + public final byte _protocolMinor; + + +// public ProtocolInitiation() {} + + public ProtocolInitiation(byte[] protocolHeader, byte protocolClass, byte protocolInstance, byte protocolMajor, byte protocolMinor) + { + _protocolHeader = protocolHeader; + _protocolClass = protocolClass; + _protocolInstance = protocolInstance; + _protocolMajor = protocolMajor; + _protocolMinor = protocolMinor; + } + + public ProtocolInitiation(ProtocolVersion pv) + { + this(AMQP_HEADER, CURRENT_PROTOCOL_CLASS, TCP_PROTOCOL_INSTANCE, pv.getMajorVersion(), pv.getMinorVersion()); + } + + + public ProtocolInitiation(ByteBuffer in) + { + _protocolHeader = new byte[4]; + in.get(_protocolHeader); + + _protocolClass = in.get(); + _protocolInstance = in.get(); + _protocolMajor = in.get(); + _protocolMinor = in.get(); + } + + public long getSize() + { + return 4 + 1 + 1 + 1 + 1; + } + + public void writePayload(ByteBuffer buffer) + { + + buffer.put(_protocolHeader); + buffer.put(_protocolClass); + buffer.put(_protocolInstance); + buffer.put(_protocolMajor); + buffer.put(_protocolMinor); + } + + public boolean equals(Object o) + { + if (!(o instanceof ProtocolInitiation)) + { + return false; + } + + ProtocolInitiation pi = (ProtocolInitiation) o; + if (pi._protocolHeader == null) + { + return false; + } + + if (_protocolHeader.length != pi._protocolHeader.length) + { + return false; + } + + for (int i = 0; i < _protocolHeader.length; i++) + { + if (_protocolHeader[i] != pi._protocolHeader[i]) + { + return false; + } + } + + return (_protocolClass == pi._protocolClass && + _protocolInstance == pi._protocolInstance && + _protocolMajor == pi._protocolMajor && + _protocolMinor == pi._protocolMinor); + } + + public static class Decoder //implements MessageDecoder + { + /** + * + * @param session the session + * @param in input buffer + * @return true if we have enough data to decode the PI frame fully, false if more + * data is required + */ + public boolean decodable(IoSession session, ByteBuffer in) + { + return (in.remaining() >= 8); + } + + public void decode(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) + { + ProtocolInitiation pi = new ProtocolInitiation(in); + out.write(pi); + } + } + + public void checkVersion() throws AMQException + { + + if(_protocolHeader.length != 4) + { + throw new AMQProtocolHeaderException("Protocol header should have exactly four octets"); + } + for(int i = 0; i < 4; i++) + { + if(_protocolHeader[i] != AMQP_HEADER[i]) + { + try + { + throw new AMQProtocolHeaderException("Protocol header is not correct: Got " + new String(_protocolHeader,"ISO-8859-1") + " should be: " + new String(AMQP_HEADER, "ISO-8859-1")); + } + catch (UnsupportedEncodingException e) + { + + } + } + } + if (_protocolClass != CURRENT_PROTOCOL_CLASS) + { + throw new AMQProtocolClassException("Protocol class " + CURRENT_PROTOCOL_CLASS + " was expected; received " + + _protocolClass); + } + if (_protocolInstance != TCP_PROTOCOL_INSTANCE) + { + throw new AMQProtocolInstanceException("Protocol instance " + TCP_PROTOCOL_INSTANCE + " was expected; received " + + _protocolInstance); + } + + ProtocolVersion pv = new ProtocolVersion(_protocolMajor, _protocolMinor); + + + if (!pv.isSupported()) + { + // TODO: add list of available versions in list to msg... + throw new AMQProtocolVersionException("Protocol version " + + _protocolMajor + "." + _protocolMinor + " not suppoerted by this version of the Qpid broker."); + } + } + + public String toString() + { + StringBuffer buffer = new StringBuffer(new String(_protocolHeader)); + buffer.append(Integer.toHexString(_protocolClass)); + buffer.append(Integer.toHexString(_protocolInstance)); + buffer.append(Integer.toHexString(_protocolMajor)); + buffer.append(Integer.toHexString(_protocolMinor)); + return buffer.toString(); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java b/Final/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java new file mode 100644 index 0000000000..26c048e34a --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java @@ -0,0 +1,98 @@ +/*
+ *
+ * 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.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+public class SmallCompositeAMQDataBlock extends AMQDataBlock implements EncodableAMQDataBlock
+{
+ private ByteBuffer _encodedBlock;
+
+ private AMQDataBlock _block;
+
+ public SmallCompositeAMQDataBlock(AMQDataBlock block)
+ {
+ _block = block;
+ }
+
+ /**
+ * The encoded block will be logically first before the AMQDataBlocks which are encoded
+ * into the buffer afterwards.
+ * @param encodedBlock already-encoded data
+ * @param block a block to be encoded.
+ */
+ public SmallCompositeAMQDataBlock(ByteBuffer encodedBlock, AMQDataBlock block)
+ {
+ this(block);
+ _encodedBlock = encodedBlock;
+ }
+
+ public AMQDataBlock getBlock()
+ {
+ return _block;
+ }
+
+ public ByteBuffer getEncodedBlock()
+ {
+ return _encodedBlock;
+ }
+
+ public long getSize()
+ {
+ long frameSize = _block.getSize();
+
+ if (_encodedBlock != null)
+ {
+ _encodedBlock.rewind();
+ frameSize += _encodedBlock.remaining();
+ }
+ return frameSize;
+ }
+
+ public void writePayload(ByteBuffer buffer)
+ {
+ if (_encodedBlock != null)
+ {
+ buffer.put(_encodedBlock);
+ }
+ _block.writePayload(buffer);
+
+ }
+
+ public String toString()
+ {
+ if (_block == null)
+ {
+ return "No blocks contained in composite frame";
+ }
+ else
+ {
+ StringBuilder buf = new StringBuilder(this.getClass().getName());
+ buf.append("{encodedBlock=").append(_encodedBlock);
+
+ buf.append(" _block=[").append(_block.toString()).append("]");
+
+ buf.append("}");
+ return buf.toString();
+ }
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java b/Final/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java new file mode 100644 index 0000000000..6006e9793c --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java @@ -0,0 +1,198 @@ +/*
+ *
+ * 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.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class VersionSpecificRegistry
+{
+ private static final Logger _log = LoggerFactory.getLogger(VersionSpecificRegistry.class);
+
+ private final byte _protocolMajorVersion;
+ private final byte _protocolMinorVersion;
+
+ private static final int DEFAULT_MAX_CLASS_ID = 200;
+ private static final int DEFAULT_MAX_METHOD_ID = 50;
+
+ private AMQMethodBodyInstanceFactory[][] _registry = new AMQMethodBodyInstanceFactory[DEFAULT_MAX_CLASS_ID][];
+
+ private ProtocolVersionMethodConverter _protocolVersionConverter;
+
+ public VersionSpecificRegistry(byte major, byte minor)
+ {
+ _protocolMajorVersion = major;
+ _protocolMinorVersion = minor;
+
+ _protocolVersionConverter = loadProtocolVersionConverters(major, minor);
+ }
+
+ private static ProtocolVersionMethodConverter loadProtocolVersionConverters(byte protocolMajorVersion,
+ byte protocolMinorVersion)
+ {
+ try
+ {
+ Class<ProtocolVersionMethodConverter> versionMethodConverterClass =
+ (Class<ProtocolVersionMethodConverter>) Class.forName("org.apache.qpid.framing.MethodConverter_"
+ + protocolMajorVersion + "_" + protocolMinorVersion);
+
+ return versionMethodConverterClass.newInstance();
+
+ }
+ catch (ClassNotFoundException e)
+ {
+ _log.warn("Could not find protocol conversion classes for " + protocolMajorVersion + "-" + protocolMinorVersion);
+ if (protocolMinorVersion != 0)
+ {
+ protocolMinorVersion--;
+
+ return loadProtocolVersionConverters(protocolMajorVersion, protocolMinorVersion);
+ }
+ else if (protocolMajorVersion != 0)
+ {
+ protocolMajorVersion--;
+
+ return loadProtocolVersionConverters(protocolMajorVersion, protocolMinorVersion);
+ }
+ else
+ {
+ return null;
+ }
+
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new IllegalStateException("Unable to load protocol version converter: ", e);
+ }
+ catch (InstantiationException e)
+ {
+ throw new IllegalStateException("Unable to load protocol version converter: ", e);
+ }
+ }
+
+ public byte getProtocolMajorVersion()
+ {
+ return _protocolMajorVersion;
+ }
+
+ public byte getProtocolMinorVersion()
+ {
+ return _protocolMinorVersion;
+ }
+
+ public AMQMethodBodyInstanceFactory getMethodBody(final short classID, final short methodID)
+ {
+ try
+ {
+ return _registry[classID][methodID];
+ }
+ catch (IndexOutOfBoundsException e)
+ {
+ return null;
+ }
+ catch (NullPointerException e)
+ {
+ return null;
+ }
+ }
+
+ public void registerMethod(final short classID, final short methodID, final AMQMethodBodyInstanceFactory instanceFactory)
+ {
+ if (_registry.length <= classID)
+ {
+ AMQMethodBodyInstanceFactory[][] oldRegistry = _registry;
+ _registry = new AMQMethodBodyInstanceFactory[classID + 1][];
+ System.arraycopy(oldRegistry, 0, _registry, 0, oldRegistry.length);
+ }
+
+ if (_registry[classID] == null)
+ {
+ _registry[classID] =
+ new AMQMethodBodyInstanceFactory[(methodID > DEFAULT_MAX_METHOD_ID) ? (methodID + 1)
+ : (DEFAULT_MAX_METHOD_ID + 1)];
+ }
+ else if (_registry[classID].length <= methodID)
+ {
+ AMQMethodBodyInstanceFactory[] oldMethods = _registry[classID];
+ _registry[classID] = new AMQMethodBodyInstanceFactory[methodID + 1];
+ System.arraycopy(oldMethods, 0, _registry[classID], 0, oldMethods.length);
+ }
+
+ _registry[classID][methodID] = instanceFactory;
+
+ }
+
+ public AMQMethodBody get(short classID, short methodID, ByteBuffer in, long size) throws AMQFrameDecodingException
+ {
+ AMQMethodBodyInstanceFactory bodyFactory;
+ try
+ {
+ bodyFactory = _registry[classID][methodID];
+ }
+ catch (NullPointerException e)
+ {
+ throw new AMQFrameDecodingException(null, "Class " + classID + " unknown in AMQP version "
+ + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID
+ + " method " + methodID + ".", e);
+ }
+ catch (IndexOutOfBoundsException e)
+ {
+ if (classID >= _registry.length)
+ {
+ throw new AMQFrameDecodingException(null, "Class " + classID + " unknown in AMQP version "
+ + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID
+ + " method " + methodID + ".", e);
+
+ }
+ else
+ {
+ throw new AMQFrameDecodingException(null, "Method " + methodID + " unknown in AMQP version "
+ + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID
+ + " method " + methodID + ".", e);
+
+ }
+ }
+
+ if (bodyFactory == null)
+ {
+ throw new AMQFrameDecodingException(null, "Method " + methodID + " unknown in AMQP version "
+ + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID
+ + " method " + methodID + ".", null);
+ }
+
+ return bodyFactory.newInstance(_protocolMajorVersion, _protocolMinorVersion, classID, methodID, in, size);
+
+ }
+
+ public ProtocolVersionMethodConverter getProtocolVersionMethodConverter()
+ {
+ return _protocolVersionConverter;
+ }
+
+ public void configure()
+ {
+ _protocolVersionConverter.configure();
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java b/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java new file mode 100644 index 0000000000..1c335f3036 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java @@ -0,0 +1,47 @@ +/*
+ *
+ * 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.qpid.framing.abstraction;
+
+public abstract class AbstractMethodConverter implements ProtocolVersionMethodConverter
+{
+ private final byte _protocolMajorVersion;
+
+
+ private final byte _protocolMinorVersion;
+
+ public AbstractMethodConverter(byte major, byte minor)
+ {
+ _protocolMajorVersion = major;
+ _protocolMinorVersion = minor;
+ }
+
+
+ public final byte getProtocolMajorVersion()
+ {
+ return _protocolMajorVersion;
+ }
+
+ public final byte getProtocolMinorVersion()
+ {
+ return _protocolMinorVersion;
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java b/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java new file mode 100644 index 0000000000..6312e478a8 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java @@ -0,0 +1,32 @@ +/*
+ *
+ * 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.qpid.framing.abstraction;
+
+import org.apache.mina.common.ByteBuffer;
+
+public interface ContentChunk
+{
+ int getSize();
+ ByteBuffer getData();
+
+ void reduceToFit();
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java b/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java new file mode 100644 index 0000000000..706499c1b0 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java @@ -0,0 +1,36 @@ +/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.framing.abstraction;
+
+import org.apache.qpid.framing.AMQShortString;
+
+public interface MessagePublishInfo
+{
+
+ public AMQShortString getExchange();
+
+ public boolean isImmediate();
+
+ public boolean isMandatory();
+
+ public AMQShortString getRoutingKey();
+
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java b/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java new file mode 100644 index 0000000000..42e2f7ad97 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java @@ -0,0 +1,32 @@ +/*
+ *
+ * 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.qpid.framing.abstraction;
+
+import org.apache.qpid.framing.AMQMethodBody;
+
+
+public interface MessagePublishInfoConverter
+{
+ public MessagePublishInfo convertToInfo(AMQMethodBody body);
+ public AMQMethodBody convertToBody(MessagePublishInfo info);
+
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java b/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java new file mode 100644 index 0000000000..99588a0908 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java @@ -0,0 +1,32 @@ +/*
+ *
+ * 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.qpid.framing.abstraction;
+
+import org.apache.qpid.framing.AMQBody;
+
+public interface ProtocolVersionMethodConverter extends MessagePublishInfoConverter
+{
+ AMQBody convertToBody(ContentChunk contentBody);
+ ContentChunk convertToContentChunk(AMQBody body);
+
+ void configure();
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/pool/Event.java b/Final/java/common/src/main/java/org/apache/qpid/pool/Event.java new file mode 100644 index 0000000000..5996cbf89c --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/pool/Event.java @@ -0,0 +1,155 @@ +/* + * + * 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.qpid.pool; + +import org.apache.mina.common.IoFilter; +import org.apache.mina.common.IoSession; + +/** + * An Event is a continuation, which is used to break a Mina filter chain and save the current point in the chain + * for later processing. It is an abstract class, with different implementations for continuations of different kinds + * of Mina events. + * + * <p/>These continuations are typically batched by {@link Job} for processing by a worker thread pool. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Process a continuation in the context of a Mina session. + * </table> + * + * @todo Pull up _nextFilter and getNextFilter into Event, as all events use it. Inner classes need to be non-static + * to use instance variables in the parent. Consequently they need to be non-inner to be instantiable outside of + * the context of the outer Event class. The inner class construction used here is preventing common code re-use + * (though not by a huge amount), but makes for an inelegent way of handling inheritance and doesn't seem like + * a justifiable use of inner classes. Move the inner classes out into their own files. + * + * @todo Could make Event implement Runnable, FutureTask, or a custom Continuation interface, to clarify its status as + * a continuation. Job is also a continuation, as is the job completion handler. Or, as Event is totally abstract, + * it is really an interface, so could just drop it and use the continuation interface instead. + */ +public abstract class Event +{ + /** + * Creates a continuation. + */ + public Event() + { } + + /** + * Processes the continuation in the context of a Mina session. + * + * @param session The Mina session. + */ + public abstract void process(IoSession session); + + /** + * A continuation ({@link Event}) that takes a Mina messageReceived event, and passes it to a NextFilter. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Pass a Mina messageReceived event to a NextFilter. <td> {@link IoFilter.NextFilter}, {@link IoSession} + * </table> + */ + public static final class ReceivedEvent extends Event + { + private final Object _data; + + private final IoFilter.NextFilter _nextFilter; + + public ReceivedEvent(final IoFilter.NextFilter nextFilter, final Object data) + { + super(); + _nextFilter = nextFilter; + _data = data; + } + + public void process(IoSession session) + { + _nextFilter.messageReceived(session, _data); + } + + public IoFilter.NextFilter getNextFilter() + { + return _nextFilter; + } + } + + /** + * A continuation ({@link Event}) that takes a Mina filterWrite event, and passes it to a NextFilter. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Pass a Mina filterWrite event to a NextFilter. + * <td> {@link IoFilter.NextFilter}, {@link IoFilter.WriteRequest}, {@link IoSession} + * </table> + */ + public static final class WriteEvent extends Event + { + private final IoFilter.WriteRequest _data; + private final IoFilter.NextFilter _nextFilter; + + public WriteEvent(final IoFilter.NextFilter nextFilter, final IoFilter.WriteRequest data) + { + super(); + _nextFilter = nextFilter; + _data = data; + } + + public void process(IoSession session) + { + _nextFilter.filterWrite(session, _data); + } + + public IoFilter.NextFilter getNextFilter() + { + return _nextFilter; + } + } + + /** + * A continuation ({@link Event}) that takes a Mina sessionClosed event, and passes it to a NextFilter. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Pass a Mina sessionClosed event to a NextFilter. <td> {@link IoFilter.NextFilter}, {@link IoSession} + * </table> + */ + public static final class CloseEvent extends Event + { + private final IoFilter.NextFilter _nextFilter; + + public CloseEvent(final IoFilter.NextFilter nextFilter) + { + super(); + _nextFilter = nextFilter; + } + + public void process(IoSession session) + { + _nextFilter.sessionClosed(session); + } + + public IoFilter.NextFilter getNextFilter() + { + return _nextFilter; + } + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/pool/Job.java b/Final/java/common/src/main/java/org/apache/qpid/pool/Job.java new file mode 100644 index 0000000000..ba3c5d03fa --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/pool/Job.java @@ -0,0 +1,162 @@ +/* + * + * 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.qpid.pool; + +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.mina.common.IoSession; + +/** + * A Job is a continuation that batches together other continuations, specifically {@link Event}s, into one continuation. + * The {@link Event}s themselves provide methods to process themselves, so processing a job simply consists of sequentially + * processing all of its aggregated events. + * + * The constructor accepts a maximum number of events for the job, and only runs up to that maximum number when + * processing the job, but the add method does not enforce this maximum. In other words, not all the enqueued events + * may be processed in each run of the job, several runs may be required to clear the queue. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Aggregate many coninuations together into a single continuation. + * <tr><td> Sequentially process aggregated continuations. <td> {@link Event} + * <tr><td> Provide running and completion status of the aggregate continuation. + * <tr><td> Execute a terminal continuation upon job completion. <td> {@link JobCompletionHandler} + * </table> + * + * @todo Could make Job implement Runnable, FutureTask, or a custom Continuation interface, to clarify its status as a + * continuation. Job is a continuation that aggregates other continuations and as such is a usefull re-usable + * piece of code. There may be other palces than the mina filter chain where continuation batching is used within + * qpid, so abstracting this out could provide a usefull building block. This also opens the way to different + * kinds of job with a common interface, e.g. parallel or sequential jobs etc. + * + * @todo For better re-usability could make the completion handler optional. Only run it when one is set. + */ +public class Job implements Runnable +{ + /** The maximum number of events to process per run of the job. More events than this may be queued in the job. */ + private final int _maxEvents; + + /** The Mina session. */ + private final IoSession _session; + + /** Holds the queue of events that make up the job. */ + private final java.util.Queue<Event> _eventQueue = new ConcurrentLinkedQueue<Event>(); + + /** Holds a status flag, that indicates when the job is actively running. */ + private final AtomicBoolean _active = new AtomicBoolean(); + + /** Holds the completion continuation, called upon completion of a run of the job. */ + private final JobCompletionHandler _completionHandler; + + /** + * Creates a new job that aggregates many continuations together. + * + * @param session The Mina session. + * @param completionHandler The per job run, terminal continuation. + * @param maxEvents The maximum number of aggregated continuations to process per run of the job. + */ + Job(IoSession session, JobCompletionHandler completionHandler, int maxEvents) + { + _session = session; + _completionHandler = completionHandler; + _maxEvents = maxEvents; + } + + /** + * Enqueus a continuation for sequential processing by this job. + * + * @param evt The continuation to enqueue. + */ + void add(Event evt) + { + _eventQueue.add(evt); + } + + /** + * Sequentially processes, up to the maximum number per job, the aggregated continuations in enqueued in this job. + */ + void processAll() + { + // limit the number of events processed in one run + for (int i = 0; i < _maxEvents; i++) + { + Event e = _eventQueue.poll(); + if (e == null) + { + break; + } + else + { + e.process(_session); + } + } + } + + /** + * Tests if there are no more enqueued continuations to process. + * + * @return <tt>true</tt> if there are no enqueued continuations in this job, <tt>false</tt> otherwise. + */ + public boolean isComplete() + { + return _eventQueue.peek() == null; + } + + /** + * Marks this job as active if it is inactive. This method is thread safe. + * + * @return <tt>true</tt> if this job was inactive and has now been marked as active, <tt>false</tt> otherwise. + */ + public boolean activate() + { + return _active.compareAndSet(false, true); + } + + /** + * Marks this job as inactive. This method is thread safe. + */ + public void deactivate() + { + _active.set(false); + } + + /** + * Processes a batch of aggregated continuations, marks this job as inactive and call the terminal continuation. + */ + public void run() + { + processAll(); + deactivate(); + _completionHandler.completed(_session, this); + } + + /** + * Another interface for a continuation. + * + * @todo Get rid of this interface as there are other interfaces that could be used instead, such as FutureTask, + * Runnable or a custom Continuation interface. + */ + static interface JobCompletionHandler + { + public void completed(IoSession session, Job job); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java b/Final/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java new file mode 100644 index 0000000000..cbe08a192e --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java @@ -0,0 +1,471 @@ +/* + * + * 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.qpid.pool; + +import org.apache.mina.common.IdleStatus; +import org.apache.mina.common.IoFilterAdapter; +import org.apache.mina.common.IoSession; + +import org.apache.qpid.pool.Event.CloseEvent; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * PoolingFilter, is a no-op pass through filter that hands all events down the Mina filter chain by default. As it + * adds no behaviour by default to the filter chain, it is abstract. + * + * <p/>PoolingFilter provides a capability, available to sub-classes, to handle events in the chain asynchronously, by + * adding them to a job. If a job is not active, adding an event to it activates it. If it is active, the event is + * added to the job, which will run to completion and eventually process the event. The queue on the job itself acts as + * a buffer between stages of the pipeline. + * + * <p/>There are two convenience methods, {@link #createAynschReadPoolingFilter} and + * {@link #createAynschWritePoolingFilter}, for obtaining pooling filters that handle 'messageReceived' and + * 'filterWrite' events, making it possible to process these event streams seperately. + * + * <p/>Pooling filters have a name, in order to distinguish different filter types. They set up a {@link Job} on the + * Mina session they are working with, and store it in the session against their identifying name. This allows different + * filters with different names to be set up on the same filter chain, on the same Mina session, that batch their + * workloads in different jobs. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Implement default, pass through filter. + * <tr><td> Create pooling filters and a specific thread pool. <td> {@link ReferenceCountingExecutorService} + * <tr><td> Provide the ability to batch Mina events for asynchronous processing. <td> {@link Job}, {@link Event} + * <tr><td> Provide a terminal continuation to keep jobs running till empty. + * <td> {@link Job}, {@link Job.JobCompletionHandler} + * </table> + * + * @todo This seems a bit bizarre. ReadWriteThreadModel creates seperate pooling filters for read and write events. + * The pooling filters themselves batch read and write events into jobs, but hand these jobs to a common thread + * pool for execution. So the same thread pool ends up handling read and write events, albeit with many threads + * so there is concurrency. But why go to the trouble of seperating out the read and write events in that case? + * Why not just batch them into jobs together? Perhaps its so that seperate thread pools could be used for these + * stages. + * + * @todo Why set an event limit of 10 on the Job? This also seems bizarre, as the job can have more than 10 events in + * it. Its just that it runs them 10 at a time, but the completion hander here checks if there are more to run + * and trips off another batch of 10 until they are all done. Why not just have a straight forward + * consumer/producer queue scenario without the batches of 10? So instead of having many jobs with batches of 10 + * in them, just have one queue of events and worker threads taking the next event. There will be coordination + * between worker threads and new events arriving on the job anyway, so the simpler scenario may have the same + * amount of contention. I can see that the batches of 10 is done, so that no job is allowed to hog the worker + * pool for too long. I'm not convinced this fairly complex scheme will actually add anything, and it might be + * better to encapsulate it under a Queue interface anyway, so that different queue implementations can easily + * be substituted in. + * + * @todo The static helper methods are pointless. Could just call new. + */ +public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCompletionHandler +{ + /** Used for debugging purposes. */ + private static final Logger _logger = LoggerFactory.getLogger(PoolingFilter.class); + + /** Holds a mapping from Mina sessions to batched jobs for execution. */ + private final ConcurrentMap<IoSession, Job> _jobs = new ConcurrentHashMap<IoSession, Job>(); + + /** Holds the managed reference to obtain the executor for the batched jobs. */ + private final ReferenceCountingExecutorService _poolReference; + + /** Used to hold a name for identifying differeny pooling filter types. */ + private final String _name; + + /** Defines the maximum number of events that will be batched into a single job. */ + private final int _maxEvents = Integer.getInteger("amqj.server.read_write_pool.max_events", 10); + + /** + * Creates a named pooling filter, on the specified shared thread pool. + * + * @param refCountingPool The thread pool reference. + * @param name The identifying name of the filter type. + */ + public PoolingFilter(ReferenceCountingExecutorService refCountingPool, String name) + { + _poolReference = refCountingPool; + _name = name; + } + + /** + * Helper method to get an instance of a pooling filter that handles read events asynchronously. + * + * @param refCountingPool A managed reference to the thread pool. + * @param name The filter types identifying name. + * + * @return A pooling filter for asynchronous read events. + */ + public static PoolingFilter createAynschReadPoolingFilter(ReferenceCountingExecutorService refCountingPool, String name) + { + return new AsynchReadPoolingFilter(refCountingPool, name); + } + + /** + * Helper method to get an instance of a pooling filter that handles write events asynchronously. + * + * @param refCountingPool A managed reference to the thread pool. + * @param name The filter types identifying name. + * + * @return A pooling filter for asynchronous write events. + */ + public static PoolingFilter createAynschWritePoolingFilter(ReferenceCountingExecutorService refCountingPool, String name) + { + return new AsynchWritePoolingFilter(refCountingPool, name); + } + + /** + * Called by Mina to initialize this filter. Takes a reference to the thread pool. + */ + public void init() + { + _logger.debug("Init called on PoolingFilter " + toString()); + + // Called when the filter is initialised in the chain. If the reference count is + // zero this acquire will initialise the pool. + _poolReference.acquireExecutorService(); + } + + /** + * Called by Mina to clean up this filter. Releases the reference to the thread pool. + */ + public void destroy() + { + _logger.debug("Destroy called on PoolingFilter " + toString()); + + // When the reference count gets to zero we release the executor service. + _poolReference.releaseExecutorService(); + } + + /** + * Adds an {@link Event} to a {@link Job}, triggering the execution of the job if it is not already running. + * + * @param session The Mina session to work in. + * @param event The event to hand off asynchronously. + */ + void fireAsynchEvent(IoSession session, Event event) + { + Job job = getJobForSession(session); + // job.acquire(); //prevents this job being removed from _jobs + job.add(event); + + // Additional checks on pool to check that it hasn't shutdown. + // The alternative is to catch the RejectedExecutionException that will result from executing on a shutdown pool + if (job.activate() && (_poolReference.getPool() != null) && !_poolReference.getPool().isShutdown()) + { + _poolReference.getPool().execute(job); + } + + } + + /** + * Creates a Job on the Mina session, identified by this filters name, in which this filter places asynchronously + * handled events. + * + * @param session The Mina session. + */ + public void createNewJobForSession(IoSession session) + { + Job job = new Job(session, this, _maxEvents); + session.setAttribute(_name, job); + } + + /** + * Retrieves this filters Job, by this filters name, from the Mina session. + * + * @param session The Mina session. + * + * @return The Job for this filter to place asynchronous events into. + */ + private Job getJobForSession(IoSession session) + { + return (Job) session.getAttribute(_name); + } + + /*private Job createJobForSession(IoSession session) + { + return addJobForSession(session, new Job(session, this, _maxEvents)); + }*/ + + /*private Job addJobForSession(IoSession session, Job job) + { + // atomic so ensures all threads agree on the same job + Job existing = _jobs.putIfAbsent(session, job); + + return (existing == null) ? job : existing; + }*/ + + /** + * Implements a terminal continuation for the {@link Job} for this filter. Whenever the Job completes its processing + * of a batch of events this is called. This method simply re-activates the job, if it has more events to process. + * + * @param session The Mina session to work in. + * @param job The job that completed. + */ + public void completed(IoSession session, Job job) + { + // if (job.isComplete()) + // { + // job.release(); + // if (!job.isReferenced()) + // { + // _jobs.remove(session); + // } + // } + // else + if (!job.isComplete()) + { + // ritchiem : 2006-12-13 Do we need to perform the additional checks here? + // Can the pool be shutdown at this point? + if (job.activate() && (_poolReference.getPool() != null) && !_poolReference.getPool().isShutdown()) + { + _poolReference.getPool().execute(job); + } + } + } + + /** + * No-op pass through filter to the next filter in the chain. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + * + * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow + * overriding sub-classes the ability to. + */ + public void sessionOpened(final NextFilter nextFilter, final IoSession session) throws Exception + { + nextFilter.sessionOpened(session); + } + + /** + * No-op pass through filter to the next filter in the chain. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + * + * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow + * overriding sub-classes the ability to. + */ + public void sessionClosed(final NextFilter nextFilter, final IoSession session) throws Exception + { + nextFilter.sessionClosed(session); + } + + /** + * No-op pass through filter to the next filter in the chain. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + * @param status The session idle status. + * + * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow + * overriding sub-classes the ability to. + */ + public void sessionIdle(final NextFilter nextFilter, final IoSession session, final IdleStatus status) throws Exception + { + nextFilter.sessionIdle(session, status); + } + + /** + * No-op pass through filter to the next filter in the chain. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + * @param cause The underlying exception. + * + * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow + * overriding sub-classes the ability to. + */ + public void exceptionCaught(final NextFilter nextFilter, final IoSession session, final Throwable cause) throws Exception + { + nextFilter.exceptionCaught(session, cause); + } + + /** + * No-op pass through filter to the next filter in the chain. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + * @param message The message received. + * + * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow + * overriding sub-classes the ability to. + */ + public void messageReceived(final NextFilter nextFilter, final IoSession session, final Object message) throws Exception + { + nextFilter.messageReceived(session, message); + } + + /** + * No-op pass through filter to the next filter in the chain. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + * @param message The message sent. + * + * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow + * overriding sub-classes the ability to. + */ + public void messageSent(final NextFilter nextFilter, final IoSession session, final Object message) throws Exception + { + nextFilter.messageSent(session, message); + } + + /** + * No-op pass through filter to the next filter in the chain. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + * @param writeRequest The write request event. + * + * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow + * overriding sub-classes the ability to. + */ + public void filterWrite(final NextFilter nextFilter, final IoSession session, final WriteRequest writeRequest) + throws Exception + { + nextFilter.filterWrite(session, writeRequest); + } + + /** + * No-op pass through filter to the next filter in the chain. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + * + * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow + * overriding sub-classes the ability to. + */ + public void filterClose(NextFilter nextFilter, IoSession session) throws Exception + { + nextFilter.filterClose(session); + } + + /** + * No-op pass through filter to the next filter in the chain. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + * + * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow + * overriding sub-classes the ability to. + */ + public void sessionCreated(NextFilter nextFilter, IoSession session) throws Exception + { + nextFilter.sessionCreated(session); + } + + /** + * Prints the filter types identifying name to a string, mainly for debugging purposes. + * + * @return The filter types identifying name. + */ + public String toString() + { + return _name; + } + + /** + * AsynchReadPoolingFilter is a pooling filter that handles 'messageReceived' and 'sessionClosed' events + * asynchronously. + */ + public static class AsynchReadPoolingFilter extends PoolingFilter + { + /** + * Creates a pooling filter that handles read events asynchronously. + * + * @param refCountingPool A managed reference to the thread pool. + * @param name The filter types identifying name. + */ + public AsynchReadPoolingFilter(ReferenceCountingExecutorService refCountingPool, String name) + { + super(refCountingPool, name); + } + + /** + * Hands off this event for asynchronous execution. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + * @param message The message received. + */ + public void messageReceived(NextFilter nextFilter, final IoSession session, Object message) + { + + fireAsynchEvent(session, new Event.ReceivedEvent(nextFilter, message)); + } + + /** + * Hands off this event for asynchronous execution. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + */ + public void sessionClosed(final NextFilter nextFilter, final IoSession session) + { + fireAsynchEvent(session, new CloseEvent(nextFilter)); + } + } + + /** + * AsynchWritePoolingFilter is a pooling filter that handles 'filterWrite' and 'sessionClosed' events + * asynchronously. + */ + public static class AsynchWritePoolingFilter extends PoolingFilter + { + /** + * Creates a pooling filter that handles write events asynchronously. + * + * @param refCountingPool A managed reference to the thread pool. + * @param name The filter types identifying name. + */ + public AsynchWritePoolingFilter(ReferenceCountingExecutorService refCountingPool, String name) + { + super(refCountingPool, name); + } + + /** + * Hands off this event for asynchronous execution. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + * @param writeRequest The write request event. + */ + public void filterWrite(final NextFilter nextFilter, final IoSession session, final WriteRequest writeRequest) + { + fireAsynchEvent(session, new Event.WriteEvent(nextFilter, writeRequest)); + } + + /** + * Hands off this event for asynchronous execution. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + */ + public void sessionClosed(final NextFilter nextFilter, final IoSession session) + { + fireAsynchEvent(session, new CloseEvent(nextFilter)); + } + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.java b/Final/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.java new file mode 100644 index 0000000000..8cea70e597 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.java @@ -0,0 +1,102 @@ +/* + * + * 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.qpid.pool; + +import org.apache.mina.common.IoFilterChain; +import org.apache.mina.common.ThreadModel; +import org.apache.mina.filter.ReferenceCountingIoFilter; + +/** + * ReadWriteThreadModel is a Mina i/o filter chain factory, which creates a filter chain with seperate filters to + * handle read and write events. The seperate filters are {@link PoolingFilter}s, which have thread pools to handle + * these events. The effect of this is that reading and writing may happen concurrently. + * + * <p/>Socket i/o will only happen with concurrent reads and writes if Mina has seperate selector threads for each. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Create a filter chain with seperate read and write thread pools for read/write Mina events. + * <td> {@link PoolingFilter} + * </table> + */ +public class ReadWriteThreadModel implements ThreadModel +{ + /** Holds the singleton instance of this factory. */ + private static final ReadWriteThreadModel _instance = new ReadWriteThreadModel(); + + /** Holds the thread pooling filter for reads. */ + private final PoolingFilter _asynchronousReadFilter; + + /** Holds the thread pooloing filter for writes. */ + private final PoolingFilter _asynchronousWriteFilter; + + /** + * Creates a new factory for concurrent i/o, thread pooling filter chain construction. This is private, so that + * only a singleton instance of the factory is ever created. + */ + private ReadWriteThreadModel() + { + final ReferenceCountingExecutorService executor = ReferenceCountingExecutorService.getInstance(); + _asynchronousReadFilter = PoolingFilter.createAynschReadPoolingFilter(executor, "AsynchronousReadFilter"); + _asynchronousWriteFilter = PoolingFilter.createAynschWritePoolingFilter(executor, "AsynchronousWriteFilter"); + } + + /** + * Gets the singleton instance of this filter chain factory. + * + * @return The singleton instance of this filter chain factory. + */ + public static ReadWriteThreadModel getInstance() + { + return _instance; + } + + /** + * Gets the read filter. + * + * @return The read filter. + */ + public PoolingFilter getAsynchronousReadFilter() + { + return _asynchronousReadFilter; + } + + /** + * Gets the write filter. + * + * @return The write filter. + */ + public PoolingFilter getAsynchronousWriteFilter() + { + return _asynchronousWriteFilter; + } + + /** + * Adds the concurrent read and write filters to a filter chain. + * + * @param chain The Mina filter chain to add to. + */ + public void buildFilterChain(IoFilterChain chain) + { + chain.addFirst("AsynchronousReadFilter", new ReferenceCountingIoFilter(_asynchronousReadFilter)); + chain.addLast("AsynchronousWriteFilter", new ReferenceCountingIoFilter(_asynchronousWriteFilter)); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java b/Final/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java new file mode 100644 index 0000000000..84c9e1f465 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java @@ -0,0 +1,145 @@ +/* + * + * 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.qpid.pool; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * ReferenceCountingExecutorService wraps an ExecutorService in order to provide shared reference to it. It counts + * the references taken, instantiating the service on the first reference, and shutting it down when the last + * reference is released. + * + * <p/>It is important to ensure that an executor service is correctly shut down as failing to do so prevents the JVM + * from terminating due to the existence of non-daemon threads. + * + * <p/><table id="crc><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Provide a shared exector service. <td> {@link Executors} + * <tr><td> Shutdown the executor service when not needed. <td> {@link ExecutorService} + * <tr><td> Track references to the executor service. + * <tr><td> Provide configuration of the executor service. + * </table> + * + * @todo Might be more elegant to make this actually implement ExecutorService, providing better hiding of the + * implementation details. Also this class introduces a pattern (albeit specific to this usage) that could be + * generalized to reference count anything. That is, on first instance call a create method, on release of last + * instance call a destroy method. This could definitely be abstracted out as a re-usable piece of code; a + * reference counting factory. It could then be re-used to do reference counting in other places (such as + * messages). Countable objects have a simple create/destroy life cycle, capturable by an interface that the + * ref counting factory can call to manage the lifecycle. + * + * @todo {@link #_poolSize} should be static? + * + * @todo The {@link #getPool()} method breaks the encapsulation of the reference counter. Generally when getPool is used + * further checks are applied to ensure that the exector service has not been shutdown. This passes responsibility + * for managing the lifecycle of the reference counted object onto the caller rather than neatly encapsulating it + * here. Could think about adding more state to the lifecycle, to mark ref counted objects as invalid, and have an + * isValid method, or could make calling code deal with RejectedExecutionException raised by shutdown executors. + */ +public class ReferenceCountingExecutorService +{ + /** Defines the smallest thread pool that will be allocated, irrespective of the number of processors. */ + private static final int MINIMUM_POOL_SIZE = 4; + + /** Holds the number of processors on the machine. */ + private static final int NUM_CPUS = Runtime.getRuntime().availableProcessors(); + + /** Defines the thread pool size to use, which is the larger of the number of CPUs or the minimum size. */ + private static final int DEFAULT_POOL_SIZE = Math.max(NUM_CPUS, MINIMUM_POOL_SIZE); + + /** + * Holds the singleton instance of this reference counter. This is only created once, statically, so the + * {@link #getInstance()} method does not need to be synchronized. + */ + private static final ReferenceCountingExecutorService _instance = new ReferenceCountingExecutorService(); + + /** This lock is used to ensure that reference counts are updated atomically with create/destroy operations. */ + private final Object _lock = new Object(); + + /** The shared executor service that is reference counted. */ + private ExecutorService _pool; + + /** Holds the number of references given out to the executor service. */ + private int _refCount = 0; + + /** Holds the number of executor threads to create. */ + private int _poolSize = Integer.getInteger("amqj.read_write_pool_size", DEFAULT_POOL_SIZE); + + /** + * Retrieves the singleton instance of this reference counter. + * + * @return The singleton instance of this reference counter. + */ + public static ReferenceCountingExecutorService getInstance() + { + return _instance; + } + + /** + * Private constructor to ensure that only a singleton instance can be created. + */ + private ReferenceCountingExecutorService() + { } + + /** + * Provides a reference to a shared executor service, incrementing the reference count. + * + * @return An executor service. + */ + ExecutorService acquireExecutorService() + { + synchronized (_lock) + { + if (_refCount++ == 0) + { + _pool = Executors.newFixedThreadPool(_poolSize); + } + + return _pool; + } + } + + /** + * Releases a reference to a shared executor service, decrementing the reference count. If the refence count falls + * to zero, the executor service is shut down. + */ + void releaseExecutorService() + { + synchronized (_lock) + { + if (--_refCount == 0) + { + _pool.shutdownNow(); + } + } + } + + /** + * Provides access to the executor service, without touching the reference count. + * + * @return The shared executor service, or <tt>null</tt> if none has been instantiated yet. + */ + public ExecutorService getPool() + { + return _pool; + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQConstant.java b/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQConstant.java new file mode 100644 index 0000000000..375df2a45d --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQConstant.java @@ -0,0 +1,227 @@ +/* + * + * 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.qpid.protocol; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.qpid.framing.AMQShortString; + +/** + * Defines constants for AMQP codes and also acts as a factory for creating such constants from the raw codes. Each + * constant also defines a short human readable description of the constant. + * + * @todo Why would a constant be defined that is not in the map? Seems more natural that getConstant should raise an + * exception for an unknown constant. Or else provide an explanation of why this is so. Also, there is no way for + * callers to determine the unknown status of a code except by comparing its name to "unknown code", which would + * seem to render this scheme a little bit pointless? + * + * @todo Java has a nice enum construct for doing this sort of thing. Maybe this is done in the old style for Java 1.4 + * backward compatability? Now that is handled through retrotranslater it may be time to use enum. + * + * <p/><tabld id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Define the set of AMQP status codes. + * <tr><td> Provide a factory to lookup constants by their code. + * <tr><td> + */ +public final class AMQConstant +{ + /** Defines a map from codes to constants. */ + private static Map _codeMap = new HashMap(); + + /** Indicates that the method completed successfully. */ + public static final AMQConstant REPLY_SUCCESS = new AMQConstant(200, "reply success", true); + + public static final AMQConstant FRAME_END = new AMQConstant(206, "frame end", true); + + /** + * The client asked for a specific message that is no longer available. The message was delivered to another + * client, or was purged from the queue for some other reason. + */ + public static final AMQConstant NOT_DELIVERED = new AMQConstant(310, "not delivered", true); + + /** + * The client attempted to transfer content larger than the server could accept at the present time. The client + * may retry at a later time. + */ + public static final AMQConstant MESSAGE_TOO_LARGE = new AMQConstant(311, "message too large", true); + + /** + * When the exchange cannot route the result of a .Publish, most likely due to an invalid routing key. Only when + * the mandatory flag is set. + */ + public static final AMQConstant NO_ROUTE = new AMQConstant(312, "no route", true); + + /** + * When the exchange cannot deliver to a consumer when the immediate flag is set. As a result of pending data on + * the queue or the absence of any consumers of the queue. + */ + public static final AMQConstant NO_CONSUMERS = new AMQConstant(313, "no consumers", true); + + /** + * An operator intervened to close the connection for some reason. The client may retry at some later date. + */ + public static final AMQConstant CONTEXT_IN_USE = new AMQConstant(320, "context in use", true); + + /** The client tried to work with an unknown virtual host or cluster. */ + public static final AMQConstant INVALID_PATH = new AMQConstant(402, "invalid path", true); + + /** The client attempted to work with a server entity to which it has no access due to security settings. */ + public static final AMQConstant ACCESS_REFUSED = new AMQConstant(403, "access refused", true); + + /** The client attempted to work with a server entity that does not exist. */ + public static final AMQConstant NOT_FOUND = new AMQConstant(404, "not found", true); + + /** + * The client attempted to work with a server entity to which it has no access because another client is + * working with it. + */ + public static final AMQConstant ALREADY_EXISTS = new AMQConstant(405, "Already exists", true); + + /** The client requested a method that was not allowed because some precondition failed. */ + public static final AMQConstant IN_USE = new AMQConstant(406, "In use", true); + + public static final AMQConstant INVALID_ROUTING_KEY = new AMQConstant(407, "routing key invalid", true); + + public static final AMQConstant REQUEST_TIMEOUT = new AMQConstant(408, "Request Timeout", true); + + public static final AMQConstant INVALID_ARGUMENT = new AMQConstant(409, "argument invalid", true); + + /** + * The client sent a malformed frame that the server could not decode. This strongly implies a programming error + * in the client. + */ + public static final AMQConstant FRAME_ERROR = new AMQConstant(501, "frame error", true); + + /** + * The client sent a frame that contained illegal values for one or more fields. This strongly implies a + * programming error in the client. + */ + public static final AMQConstant SYNTAX_ERROR = new AMQConstant(502, "syntax error", true); + + /** + * The client sent an invalid sequence of frames, attempting to perform an operation that was considered invalid + * by the server. This usually implies a programming error in the client. + */ + public static final AMQConstant COMMAND_INVALID = new AMQConstant(503, "command invalid", true); + + /** + * The client attempted to work with a channel that had not been correctly opened. This most likely indicates a + * fault in the client layer. + */ + public static final AMQConstant CHANNEL_ERROR = new AMQConstant(504, "channel error", true); + + /** + * The server could not complete the method because it lacked sufficient resources. This may be due to the client + * creating too many of some type of entity. + */ + public static final AMQConstant RESOURCE_ERROR = new AMQConstant(506, "resource error", true); + + /** + * The client tried to work with some entity in a manner that is prohibited by the server, due to security settings + * or by some other criteria. + */ + public static final AMQConstant NOT_ALLOWED = new AMQConstant(530, "not allowed", true); + + /** The client tried to use functionality that is not implemented in the server. */ + public static final AMQConstant NOT_IMPLEMENTED = new AMQConstant(540, "not implemented", true); + + /** + * The server could not complete the method because of an internal error. The server may require intervention by + * an operator in order to resume normal operations. + */ + public static final AMQConstant INTERNAL_ERROR = new AMQConstant(541, "internal error", true); + + public static final AMQConstant FRAME_MIN_SIZE = new AMQConstant(4096, "frame min size", true); + + /** The AMQP status code. */ + private int _code; + + /** A short description of the status code. */ + private AMQShortString _name; + + /** + * Creates a new AMQP status code. + * + * @param code The code. + * @param name A short description of the code. + * @param map <tt>true</tt> to register the code as a known code, <tt>false</tt> otherwise. + */ + private AMQConstant(int code, String name, boolean map) + { + _code = code; + _name = new AMQShortString(name); + if (map) + { + _codeMap.put(new Integer(code), this); + } + } + + /** + * Creates a constant for a status code by looking up the code in the map of known codes. If the code is not known + * a constant is still created for it, but it is marked as unknown. + * + * @param code The AMQP status code. + * + * @return The AMQP status code encapsulated as a constant. + */ + public static AMQConstant getConstant(int code) + { + AMQConstant c = (AMQConstant) _codeMap.get(new Integer(code)); + if (c == null) + { + c = new AMQConstant(code, "unknown code", false); + } + + return c; + } + + /** + * Gets the underlying AMQP status code. + * + * @return The AMQP status code. + */ + public int getCode() + { + return _code; + } + + /** + * Gets a short description of the status code. + * + * @return A short description of the status code. + */ + public AMQShortString getName() + { + return _name; + } + + /** + * Renders the constant as a string, mainly for debugging purposes. + * + * @return The status code and its description. + */ + public String toString() + { + return _code + ": " + _name; + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodEvent.java b/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodEvent.java new file mode 100644 index 0000000000..fd6907a152 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodEvent.java @@ -0,0 +1,95 @@ +/* + * + * 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.qpid.protocol; + +import org.apache.qpid.framing.AMQMethodBody; + +/** + * AMQMethodEvent encapsulates an AMQP method call, and the channel on which that method call occurred. + * + * <p/>Supplies the: + * <ul> + * <li>channel id</li> + * <li>protocol method</li> + * </ul> + * + * <p/>As the event contains the context in which it occurred, event listeners do not need to be statefull. + * to listeners. Events are often handled by {@link AMQMethodListener}s. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Encapsulate an AMQP method call and the channel as the context for the method call. + * </table> + */ +public class AMQMethodEvent<M extends AMQMethodBody> +{ + /** Holds the method call. */ + private final M _method; + + /** Holds the channel handle for the method call. */ + private final int _channelId; + + /** + * Creates a method event to encasulate a method call and channel. + * + * @param channelId The channel on which the method call occurred. + * @param method The method call. + */ + public AMQMethodEvent(int channelId, M method) + { + _channelId = channelId; + _method = method; + } + + /** + * Gets the method call. + * + * @return The method call. + */ + public M getMethod() + { + return _method; + } + + /** + * Gets the channel handle for the method call. + * + * @return The channel handle for the method call. + */ + public int getChannelId() + { + return _channelId; + } + + /** + * Prints the method call as a string, mainly for debugging purposes. + * + * @return The method call as a string, mainly for debugging purposes. + */ + public String toString() + { + StringBuilder buf = new StringBuilder("Method event: "); + buf.append("\nChannel id: ").append(_channelId); + buf.append("\nMethod: ").append(_method); + + return buf.toString(); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodListener.java b/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodListener.java new file mode 100644 index 0000000000..2fbeeda1d4 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodListener.java @@ -0,0 +1,69 @@ +/* + * + * 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.qpid.protocol; + +import org.apache.qpid.framing.AMQMethodBody; + +/** + * AMQMethodListener is a listener that receives notifications of AMQP methods. The methods are packaged as events in + * {@link AMQMethodEvent}. + * + * <p/>An event listener may be associated with a particular context, usually an AMQP channel, and in addition to + * receiving method events will be notified of errors on that context. This enables listeners to perform any clean + * up that they need to do before the context is closed or retried. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities + * <tr><td> Accept notification of AMQP method events. <td> {@link AMQMethodEvent} + * <tr><td> Accept notification of errors on the event context. + * </table> + * + * @todo Document why the exception is passed to the error method. Is it so that the exception can be passed + * from the event handling thread to another thread and rethown from there? It is unusual to pass exceptions as + * method arguments, because they have their own mechanism for propagating through the call stack, so some + * explanation ought to be provided. + */ +public interface AMQMethodListener +{ + /** + * Notifies the listener that an AMQP method event has occurred. + * + * @param evt The AMQP method event (contains the method and channel). + * + * @return <tt>true</tt> if the handler processes the method frame, <tt>false<tt> otherwise. Note that this does + * not prohibit the method event being delivered to subsequent listeners but can be used to determine if + * nobody has dealt with an incoming method frame. + * + * @throws Exception if an error has occurred. This exception may be delivered to all registered listeners using + * the error() method (see below) allowing them to perform cleanup if necessary. + * + * @todo Consider narrowing the exception. + */ + <B extends AMQMethodBody> boolean methodReceived(AMQMethodEvent<B> evt) throws Exception; + + /** + * Notifies the listener of an error on the event context to which it is listening. The listener should perform + * any necessary clean-up for the context. + * + * @param e The underlying exception that is the source of the error. + */ + void error(Exception e); +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQProtocolWriter.java b/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQProtocolWriter.java new file mode 100644 index 0000000000..65884e4950 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQProtocolWriter.java @@ -0,0 +1,43 @@ +/* + * + * 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.qpid.protocol; + +import org.apache.qpid.framing.AMQDataBlock; + +/** + * AMQProtocolWriter provides a method to write a frame of data 'to the wire', in the context of the object + * that implements the method, usually some sort of session. The block of data, encapsulated by {@link AMQDataBlock}, + * will be encoded as it is written. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities + * <tr><td> Write an encoded block of data to the write, in the context of a session. + * </table> + */ +public interface AMQProtocolWriter +{ + /** + * Writes a frame to the wire, encoding it as necessary, for example, into a sequence of bytes. + * + * @param frame The frame to be encoded and written. + */ + public void writeFrame(AMQDataBlock frame); +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java b/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java new file mode 100644 index 0000000000..7c1d6fdaa0 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java @@ -0,0 +1,46 @@ +/*
+ *
+ * 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.qpid.protocol;
+
+import org.apache.qpid.framing.VersionSpecificRegistry;
+
+/**
+ * AMQVersionAwareProtocolSession is implemented by all AMQP session classes, that need to provide an awareness to
+ * callers of the version of the AMQP protocol that they are able to work with.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Provide the method registry for a specific version of the AMQP.
+ * </table>
+ *
+ * @todo Why is this a seperate interface to {@link ProtocolVersionAware}, could they be combined into a single
+ * interface and one of them eliminated? Move getRegistry method to ProtocolVersionAware, make the sessions
+ * implement AMQProtocolWriter directly and drop this interface.
+ */
+public interface AMQVersionAwareProtocolSession extends AMQProtocolWriter, ProtocolVersionAware
+{
+ /**
+ * Gets the method registry for a specific version of the AMQP.
+ *
+ * @return The method registry for a specific version of the AMQP.
+ */
+ public VersionSpecificRegistry getRegistry();
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java b/Final/java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java new file mode 100644 index 0000000000..60a7f30185 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java @@ -0,0 +1,47 @@ +/*
+ *
+ * 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.qpid.protocol;
+
+/**
+ * ProtocolVersionAware is implemented by all AMQP handling classes, that need to provide an awareness to callers of
+ * the version of the AMQP protocol that they are able to handle.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Report the major and minor AMQP version handled.
+ * </table>
+ */
+public interface ProtocolVersionAware
+{
+ /**
+ * Reports the AMQP minor version, that the implementer can handle.
+ *
+ * @return The AMQP minor version.
+ */
+ public byte getProtocolMinorVersion();
+
+ /**
+ * Reports the AMQP major version, that the implementer can handle.
+ *
+ * @return The AMQP major version.
+ */
+ public byte getProtocolMajorVersion();
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java b/Final/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java new file mode 100644 index 0000000000..950279fff1 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java @@ -0,0 +1,157 @@ +/* + * + * 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.qpid.ssl; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.GeneralSecurityException; +import java.security.KeyStore; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; + +/** + * Factory used to create SSLContexts. SSL needs to be configured + * before this will work. + * + */ +public class SSLContextFactory { + + /** + * Path to the Java keystore file + */ + private String _keystorePath; + + /** + * Password for the keystore + */ + private String _keystorePassword; + + /** + * Cert type to use + */ + private String _certType; + + /** + * Create a factory instance + * @param keystorePath path to the Java keystore file + * @param keystorePassword password for the Java keystore + * @param certType certificate type + */ + public SSLContextFactory(String keystorePath, String keystorePassword, + String certType) + { + _keystorePath = keystorePath; + _keystorePassword = keystorePassword; + if (_keystorePassword.equals("none")) + { + _keystorePassword = null; + } + _certType = certType; + if (keystorePath == null) { + throw new IllegalArgumentException("Keystore path must be specified"); + } + if (certType == null) { + throw new IllegalArgumentException("Cert type must be specified"); + } + } + + /** + * Builds a SSLContext appropriate for use with a server + * @return SSLContext + * @throws GeneralSecurityException + * @throws IOException + */ + public SSLContext buildServerContext() throws GeneralSecurityException, IOException + { + // Create keystore + KeyStore ks = getInitializedKeyStore(); + + // Set up key manager factory to use our key store + KeyManagerFactory kmf = KeyManagerFactory.getInstance(_certType); + kmf.init(ks, _keystorePassword.toCharArray()); + + // Initialize the SSLContext to work with our key managers. + SSLContext sslContext = SSLContext.getInstance("TLS"); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(_certType); + tmf.init(ks); + sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + + return sslContext; + } + + /** + * Creates a SSLContext factory appropriate for use with a client + * @return SSLContext + * @throws GeneralSecurityException + * @throws IOException + */ + public SSLContext buildClientContext() throws GeneralSecurityException, IOException + { + KeyStore ks = getInitializedKeyStore(); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(_certType); + tmf.init(ks); + SSLContext context = SSLContext.getInstance("TLS"); + context.init(null, tmf.getTrustManagers(), null); + return context; + } + + private KeyStore getInitializedKeyStore() throws GeneralSecurityException, IOException + { + KeyStore ks = KeyStore.getInstance("JKS"); + InputStream in = null; + try + { + File f = new File(_keystorePath); + if (f.exists()) + { + in = new FileInputStream(f); + } + else + { + in = Thread.currentThread().getContextClassLoader().getResourceAsStream(_keystorePath); + } + if (in == null) + { + throw new IOException("Unable to load keystore resource: " + _keystorePath); + } + ks.load(in, _keystorePassword.toCharArray()); + } + finally + { + if (in != null) + { + //noinspection EmptyCatchBlock + try + { + in.close(); + } + catch (IOException ignored) + { + } + } + } + return ks; + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/url/AMQBindingURL.java b/Final/java/common/src/main/java/org/apache/qpid/url/AMQBindingURL.java new file mode 100644 index 0000000000..1774fa1194 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/url/AMQBindingURL.java @@ -0,0 +1,294 @@ +/* + * + * 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.qpid.url; + +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; + +public class AMQBindingURL implements BindingURL +{ + private static final Logger _logger = LoggerFactory.getLogger(AMQBindingURL.class); + + String _url; + AMQShortString _exchangeClass; + AMQShortString _exchangeName; + AMQShortString _destinationName; + AMQShortString _queueName; + private HashMap<String, String> _options; + + public AMQBindingURL(String url) throws URLSyntaxException + { + // format: + // <exch_class>://<exch_name>/[<destination>]/[<queue>]?<option>='<value>'[,<option>='<value>']* + _logger.debug("Parsing URL: " + url); + _url = url; + _options = new HashMap<String, String>(); + + parseBindingURL(); + } + + private void parseBindingURL() throws URLSyntaxException + { + try + { + URI connection = new URI(_url); + + String exchangeClass = connection.getScheme(); + + if (exchangeClass == null) + { + _url = ExchangeDefaults.DIRECT_EXCHANGE_CLASS + "://" + "" + "//" + _url; + // URLHelper.parseError(-1, "Exchange Class not specified.", _url); + parseBindingURL(); + + return; + } + else + { + setExchangeClass(exchangeClass); + } + + String exchangeName = connection.getHost(); + + if (exchangeName == null) + { + if (getExchangeClass().equals(ExchangeDefaults.DIRECT_EXCHANGE_CLASS)) + { + setExchangeName(""); + } + else + { + throw URLHelper.parseError(-1, "Exchange Name not specified.", _url); + } + } + else + { + setExchangeName(exchangeName); + } + + String queueName; + + if ((connection.getPath() == null) || connection.getPath().equals("")) + { + throw URLHelper.parseError(_url.indexOf(_exchangeName.toString()) + _exchangeName.length(), + "Destination or Queue requried", _url); + } + else + { + int slash = connection.getPath().indexOf("/", 1); + if (slash == -1) + { + throw URLHelper.parseError(_url.indexOf(_exchangeName.toString()) + _exchangeName.length(), + "Destination requried", _url); + } + else + { + String path = connection.getPath(); + setDestinationName(path.substring(1, slash)); + + // We don't set queueName yet as the actual value we use depends on options set + // when we are dealing with durable subscriptions + + queueName = path.substring(slash + 1); + + } + } + + URLHelper.parseOptions(_options, connection.getQuery()); + + processOptions(); + + // We can now call setQueueName as the URL is full parsed. + + setQueueName(queueName); + + // Fragment is #string (not used) + _logger.debug("URL Parsed: " + this); + + } + catch (URISyntaxException uris) + { + + throw URLHelper.parseError(uris.getIndex(), uris.getReason(), uris.getInput()); + + } + } + + private void setExchangeClass(String exchangeClass) + { + setExchangeClass(new AMQShortString(exchangeClass)); + } + + private void setQueueName(String name) throws URLSyntaxException + { + setQueueName(new AMQShortString(name)); + } + + private void setDestinationName(String name) + { + setDestinationName(new AMQShortString(name)); + } + + private void setExchangeName(String exchangeName) + { + setExchangeName(new AMQShortString(exchangeName)); + } + + private void processOptions() + { + // this is where we would parse any options that needed more than just storage. + } + + public String getURL() + { + return _url; + } + + public AMQShortString getExchangeClass() + { + return _exchangeClass; + } + + private void setExchangeClass(AMQShortString exchangeClass) + { + + _exchangeClass = exchangeClass; + if (exchangeClass.equals(ExchangeDefaults.TOPIC_EXCHANGE_CLASS)) + { + setOption(BindingURL.OPTION_EXCLUSIVE, "true"); + } + + } + + public AMQShortString getExchangeName() + { + return _exchangeName; + } + + private void setExchangeName(AMQShortString name) + { + _exchangeName = name; + } + + public AMQShortString getDestinationName() + { + return _destinationName; + } + + private void setDestinationName(AMQShortString name) + { + _destinationName = name; + } + + public AMQShortString getQueueName() + { + return _queueName; + } + + public void setQueueName(AMQShortString name) throws URLSyntaxException + { + if (_exchangeClass.equals(ExchangeDefaults.TOPIC_EXCHANGE_CLASS)) + { + if (Boolean.parseBoolean(getOption(OPTION_DURABLE))) + { + if (containsOption(BindingURL.OPTION_CLIENTID) && containsOption(BindingURL.OPTION_SUBSCRIPTION)) + { + _queueName = + new AMQShortString(getOption(BindingURL.OPTION_CLIENTID + ":" + BindingURL.OPTION_SUBSCRIPTION)); + } + else + { + throw URLHelper.parseError(-1, "Durable subscription must have values for " + BindingURL.OPTION_CLIENTID + + " and " + BindingURL.OPTION_SUBSCRIPTION + ".", _url); + + } + } + else + { + _queueName = null; + } + } + else + { + _queueName = name; + } + + } + + public String getOption(String key) + { + return _options.get(key); + } + + public void setOption(String key, String value) + { + _options.put(key, value); + } + + public boolean containsOption(String key) + { + return _options.containsKey(key); + } + + public AMQShortString getRoutingKey() + { + if (_exchangeClass.equals(ExchangeDefaults.DIRECT_EXCHANGE_CLASS)) + { + return getQueueName(); + } + + if (containsOption(BindingURL.OPTION_ROUTING_KEY)) + { + return new AMQShortString(getOption(OPTION_ROUTING_KEY)); + } + + return getDestinationName(); + } + + public void setRoutingKey(AMQShortString key) + { + setOption(OPTION_ROUTING_KEY, key.toString()); + } + + public String toString() + { + StringBuffer sb = new StringBuffer(); + + sb.append(_exchangeClass); + sb.append("://"); + sb.append(_exchangeName); + sb.append('/'); + sb.append(_destinationName); + sb.append('/'); + sb.append(_queueName); + + sb.append(URLHelper.printOptions(_options)); + + return sb.toString(); + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/url/BindingURL.java b/Final/java/common/src/main/java/org/apache/qpid/url/BindingURL.java new file mode 100644 index 0000000000..67be2db86f --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/url/BindingURL.java @@ -0,0 +1,56 @@ +/* + * + * 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.qpid.url; + +import org.apache.qpid.framing.AMQShortString; + +/* + Binding URL format: + <exch_class>://<exch_name>/[<destination>]/[<queue>]?<option>='<value>'[,<option>='<value>']* +*/ +public interface BindingURL +{ + public static final String OPTION_EXCLUSIVE = "exclusive"; + public static final String OPTION_AUTODELETE = "autodelete"; + public static final String OPTION_DURABLE = "durable"; + public static final String OPTION_CLIENTID = "clientid"; + public static final String OPTION_SUBSCRIPTION = "subscription"; + public static final String OPTION_ROUTING_KEY = "routingkey"; + + + String getURL(); + + AMQShortString getExchangeClass(); + + AMQShortString getExchangeName(); + + AMQShortString getDestinationName(); + + AMQShortString getQueueName(); + + String getOption(String key); + + boolean containsOption(String key); + + AMQShortString getRoutingKey(); + + String toString(); +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/url/URLHelper.java b/Final/java/common/src/main/java/org/apache/qpid/url/URLHelper.java new file mode 100644 index 0000000000..c08b443acf --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/url/URLHelper.java @@ -0,0 +1,172 @@ +/* + * + * 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.qpid.url; + +import java.util.HashMap; + +public class URLHelper +{ + public static char DEFAULT_OPTION_SEPERATOR = '&'; + public static char ALTERNATIVE_OPTION_SEPARATOR = ','; + public static char BROKER_SEPARATOR = ';'; + + public static void parseOptions(HashMap<String, String> optionMap, String options) throws URLSyntaxException + { + // options looks like this + // brokerlist='tcp://host:port?option='value',option='value';vm://:3/virtualpath?option='value'',failover='method?option='value',option='value'' + + if ((options == null) || (options.indexOf('=') == -1)) + { + return; + } + + int optionIndex = options.indexOf('='); + + String option = options.substring(0, optionIndex); + + int length = options.length(); + + int nestedQuotes = 0; + + // to store index of final "'" + int valueIndex = optionIndex; + + // Walk remainder of url. + while ((nestedQuotes > 0) || (valueIndex < length)) + { + valueIndex++; + + if (valueIndex >= length) + { + break; + } + + if (options.charAt(valueIndex) == '\'') + { + if ((valueIndex + 1) < options.length()) + { + if ((options.charAt(valueIndex + 1) == DEFAULT_OPTION_SEPERATOR) + || (options.charAt(valueIndex + 1) == ALTERNATIVE_OPTION_SEPARATOR) + || (options.charAt(valueIndex + 1) == BROKER_SEPARATOR) + || (options.charAt(valueIndex + 1) == '\'')) + { + nestedQuotes--; + + if (nestedQuotes == 0) + { + // We've found the value of an option + break; + } + } + else + { + nestedQuotes++; + } + } + else + { + // We are at the end of the string + // Check to see if we are corectly closing quotes + if (options.charAt(valueIndex) == '\'') + { + nestedQuotes--; + } + + break; + } + } + } + + if ((nestedQuotes != 0) || (valueIndex < (optionIndex + 2))) + { + int sepIndex = 0; + + // Try and identify illegal separator character + if (nestedQuotes > 1) + { + for (int i = 0; i < nestedQuotes; i++) + { + sepIndex = options.indexOf('\'', sepIndex); + sepIndex++; + } + } + + if ((sepIndex >= options.length()) || (sepIndex == 0)) + { + throw parseError(valueIndex, "Unterminated option", options); + } + else + { + throw parseError(sepIndex, "Unterminated option. Possible illegal option separator:'" + + options.charAt(sepIndex) + "'", options); + } + } + + // optionIndex +2 to skip "='" + String value = options.substring(optionIndex + 2, valueIndex); + + optionMap.put(option, value); + + if (valueIndex < (options.length() - 1)) + { + // Recurse to get remaining options + parseOptions(optionMap, options.substring(valueIndex + 2)); + } + } + + public static URLSyntaxException parseError(int index, String error, String url) + { + return parseError(index, 1, error, url); + } + + public static URLSyntaxException parseError(int index, int length, String error, String url) + { + return new URLSyntaxException(url, error, index, length); + } + + public static String printOptions(HashMap<String, String> options) + { + if (options.isEmpty()) + { + return ""; + } + else + { + StringBuffer sb = new StringBuffer(); + sb.append('?'); + for (String key : options.keySet()) + { + sb.append(key); + + sb.append("='"); + + sb.append(options.get(key)); + + sb.append("'"); + sb.append(DEFAULT_OPTION_SEPERATOR); + } + + sb.deleteCharAt(sb.length() - 1); + + return sb.toString(); + } + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/url/URLSyntaxException.java b/Final/java/common/src/main/java/org/apache/qpid/url/URLSyntaxException.java new file mode 100644 index 0000000000..3ff7195794 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/url/URLSyntaxException.java @@ -0,0 +1,97 @@ +/* + * + * 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.qpid.url; + +import java.net.URISyntaxException; + +public class URLSyntaxException extends URISyntaxException +{ + private int _length; + + public URLSyntaxException(String url, String error, int index, int length) + { + super(url, error, index); + + _length = length; + } + + private static String getPositionString(int index, int length) + { + StringBuffer sb = new StringBuffer(index + 1); + + for (int i = 0; i < index; i++) + { + sb.append(" "); + } + + if (length > -1) + { + for (int i = 0; i < length; i++) + { + sb.append('^'); + } + } + + return sb.toString(); + } + + + public String toString() + { + StringBuffer sb = new StringBuffer(); + + sb.append(getReason()); + + if (getIndex() > -1) + { + if (_length != -1) + { + sb.append(" between indicies "); + sb.append(getIndex()); + sb.append(" and "); + sb.append(_length); + } + else + { + sb.append(" at index "); + sb.append(getIndex()); + } + } + + sb.append(" "); + if (getIndex() != -1) + { + sb.append("\n"); + } + + sb.append(getInput()); + + if (getIndex() != -1) + { + sb.append("\n"); + sb.append(getPositionString(getIndex(), _length)); + } + + return sb.toString(); + } + + +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/util/CommandLineParser.java b/Final/java/common/src/main/java/org/apache/qpid/util/CommandLineParser.java new file mode 100644 index 0000000000..dc73bce28f --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/util/CommandLineParser.java @@ -0,0 +1,689 @@ +/*
+ *
+ * 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.qpid.util;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.regex.*;
+
+/**
+ * CommandLineParser provides a utility for specifying the format of a command line and parsing command lines to ensure
+ * that they fit their specified format. A command line is made up of flags and options, both may be refered to as
+ * options. A flag is an option that does not take an argument (specifying it means it has the value 'true' and not
+ * specifying it means it has the value 'false'). Options must take arguments but they can be set up with defaults so
+ * that they take a default value when not set. Options may be mandatory in wich case it is an error not to specify
+ * them on the command line. Flags are never mandatory because they are implicitly set to false when not specified.
+ *
+ * <p/>Some example command lines are:
+ *
+ * <ul>
+ * <li>This one has two options that expect arguments:
+ * <pre>
+ * cruisecontrol -configfile cruisecontrol.xml -port 9000
+ * </pre>
+ * <li>This has one no-arg flag and two 'free' arguments:
+ * <pre>
+ * zip -r project.zip project/*
+ * </pre>
+ * <li>This one concatenates multiple flags into a single block with only one '-':
+ * <pre>
+ * jar -tvf mytar.tar
+ * </pre>
+ *
+ * <p/>The parsing rules are:
+ *
+ * <ol>
+ * <li>Flags may be combined after a single '-' because they never take arguments. Normally such flags are single letter
+ * flags but this is only a convention and not enforced. Flags of more than one letter are usually specified on their own.
+ * <li>Options expecting arguments must always be on their own.
+ * <li>The argument to an option may be seperated from it by whitespace or appended directly onto the option.
+ * <li>The argument to an option may never begin with a '-' character.
+ * <li>All other arguments not beginning with a '-' character are free arguments that do not belong to any option.
+ * <li>The second or later of a set of duplicate or repeated flags are ignored.
+ * <li>Options are matched up to the shortest matching option. This is because of the possibility of having no space
+ * between an option and its argument. This rules out the possibility of using two options where one is an opening
+ * substring of the other. For example, the options "foo" and "foobar" cannot be used on the same command line because
+ * it is not possible to distinguish the argument "-foobar" from being the "foobar" option or the "foo" option with
+ * the "bar" argument.
+ * </ol>
+ *
+ * <p/>By default, unknown options are simply ignored if specified on the command line. This behaviour may be changed
+ * so that the parser reports all unknowns as errors by using the {@link #setErrorsOnUnknowns} method.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Accept a command line specification.
+ * <tr><td> Parse a command line into properties, validating it against its specification.
+ * <tr><td> Report all errors between a command line and its specification.
+ * <tr><td> Provide a formatted usage string for a command line.
+ * <tr><td> Provide a formatted options in force string for a command line.
+ * <tr><td> Allow errors on unknowns behaviour to be turned on or off.
+ * </table>
+ */
+public class CommandLineParser
+{
+ /** Holds a mapping from command line option names to detailed information about those options. */
+ private Map<String, CommandLineOption> optionMap = new HashMap<String, CommandLineOption>();
+
+ /** Holds a list of parsing errors. */
+ private List<String> parsingErrors = new ArrayList<String>();
+
+ /** Holds the regular expression matcher to match command line options with. */
+ private Matcher optionMatcher = null;
+
+ /** Holds the parsed command line properties after parsing. */
+ private Properties parsedProperties = null;
+
+ /** Flag used to indicate that errors should be created for unknown options. False by default. */
+ private boolean errorsOnUnknowns = false;
+
+ /**
+ * Creates a command line options parser from a command line specification. This is passed to this constructor
+ * as an array of arrays of strings. Each array of strings specifies the command line for a single option. A static
+ * array may therefore easily be used to configure the command line parser in a single method call with an easily
+ * readable format.
+ *
+ * <p/>Each array of strings must be 2, 3, 4 or 5 elements long. If any of the last three elements are missing they
+ * are assumed to be null. The elements specify the following parameters:
+ * <ol>
+ * <li>The name of the option without the leading '-'. For example, "file". To specify the format of the 'free'
+ * arguments use the option names "1", "2", ... and so on.
+ * <li>The option comment. A line of text describing the usage of the option. For example, "The file to be processed."
+ * <li>The options argument. This is a very short description of the argument to the option, often a single word
+ * or a reminder as to the arguments format. When this element is null the option is a flag and does not
+ * accept any arguments. For example, "filename" or "(unix | windows)" or null. The actual text specified
+ * is only used to print in the usage message to remind the user of the usage of the option.
+ * <li>The mandatory flag. When set to "true" an option must always be specified. Any other value, including null,
+ * means that the option is mandatory. Flags are always mandatory (see class javadoc for explanation of why) so
+ * this is ignored for flags.
+ * <li>A regular expression describing the format that the argument must take. Ignored if null.
+ * </ol>
+ * <p/>An example call to this constructor is:
+ *
+ * <pre>
+ * CommandLineParser commandLine = new CommandLineParser(
+ * new String[][] {{"file", "The file to be processed. ", "filename", "true"},
+ * {"dir", "Directory to store results in. Current dir used if not set.", "out dir"},
+ * {"os", "Operating system EOL format to use.", "(windows | unix)", null, "windows\|unix"},
+ * {"v", "Verbose mode. Prints information about the processing as it goes."},
+ * {"1", "The processing command to run.", "command", "true", "add\|remove\|list"}});
+ * </pre>
+ *
+ * @param config The configuration as an array of arrays of strings.
+ */
+ public CommandLineParser(String[][] config)
+ {
+ // Loop through all the command line option specifications creating details for each in the options map.
+ for (int i = 0; i < config.length; i++)
+ {
+ String[] nextOptionSpec = config[i];
+
+ addOption(nextOptionSpec[0], nextOptionSpec[1], (nextOptionSpec.length > 2) ? nextOptionSpec[2] : null,
+ (nextOptionSpec.length > 3) ? ("true".equals(nextOptionSpec[3]) ? true : false) : false,
+ (nextOptionSpec.length > 4) ? nextOptionSpec[4] : null);
+ }
+ }
+
+ /**
+ * Lists all the parsing errors from the most recent parsing in a string.
+ *
+ * @return All the parsing errors from the most recent parsing.
+ */
+ public String getErrors()
+ {
+ // Return the empty string if there are no errors.
+ if (parsingErrors.isEmpty())
+ {
+ return "";
+ }
+
+ // Concatenate all the parsing errors together.
+ String result = "";
+
+ for (String s : parsingErrors)
+ {
+ result += s;
+ }
+
+ return result;
+ }
+
+ /**
+ * Lists the properties set from the most recent parsing or an empty string if no parsing has been done yet.
+ *
+ * @return The properties set from the most recent parsing or an empty string if no parsing has been done yet.
+ */
+ public String getOptionsInForce()
+ {
+ // Check if there are no properties to report and return and empty string if so.
+ if (parsedProperties == null)
+ {
+ return "";
+ }
+
+ // List all the properties.
+ String result = "Options in force:\n";
+
+ for (Map.Entry<Object, Object> property : parsedProperties.entrySet())
+ {
+ result += property.getKey() + " = " + property.getValue() + "\n";
+ }
+
+ return result;
+ }
+
+ /**
+ * Generates a usage string consisting of the name of each option and each options argument description and
+ * comment.
+ *
+ * @return A usage string for all the options.
+ */
+ public String getUsage()
+ {
+ String result = "Options:\n";
+
+ // Print usage on each of the command line options.
+ for (CommandLineOption optionInfo : optionMap.values())
+ {
+ result +=
+ optionInfo.option + " " + ((optionInfo.argument != null) ? (optionInfo.argument + " ") : "")
+ + optionInfo.comment + "\n";
+ }
+
+ return result;
+ }
+
+ /**
+ * Control the behaviour of the errors on unkowns reporting. When turned on this reports all unkowns options
+ * as errors. When turned off, all unknowns are simply ignored.
+ *
+ * @param errors The setting of the errors on unkown flag. True to turn it on.
+ */
+ public void setErrorsOnUnknowns(boolean errors)
+ {
+ errorsOnUnknowns = errors;
+ }
+
+ /**
+ * Parses a set of command line arguments into a set of properties, keyed by the argument flag. The free arguments
+ * are keyed by integers as strings starting at "1" and then "2", ... and so on.
+ *
+ * <p/>See the class level comment for a description of the parsing rules.
+ *
+ * @param args The command line arguments.
+ *
+ * @return The arguments as a set of properties.
+ *
+ * @throws IllegalArgumentException If the command line cannot be parsed against its specification. If this exception
+ * is thrown a call to {@link #getErrors} will provide a diagnostic of the command
+ * line errors.
+ */
+ public Properties parseCommandLine(String[] args) throws IllegalArgumentException
+ {
+ Properties options = new Properties();
+
+ // Used to keep count of the current 'free' argument.
+ int free = 1;
+
+ // Used to indicate that the most recently parsed option is expecting arguments.
+ boolean expectingArgs = false;
+
+ // The option that is expecting arguments from the next element of the command line.
+ String optionExpectingArgs = null;
+
+ // Used to indicate that the most recently parsed option is a duplicate and should be ignored.
+ boolean ignore = false;
+
+ // Create the regular expression matcher for the command line options.
+ String regexp = "^(";
+ int optionsAdded = 0;
+
+ for (Iterator<String> i = optionMap.keySet().iterator(); i.hasNext();)
+ {
+ String nextOption = i.next();
+
+ // Check that the option is not a free argument definition.
+ boolean notFree = false;
+
+ try
+ {
+ Integer.parseInt(nextOption);
+ }
+ catch (NumberFormatException e)
+ {
+ notFree = true;
+ }
+
+ // Add the option to the regular expression matcher if it is not a free argument definition.
+ if (notFree)
+ {
+ regexp += nextOption + (i.hasNext() ? "|" : "");
+ optionsAdded++;
+ }
+ }
+
+ // There has to be more that one option in the regular expression or else the compiler complains that the close
+ // cannot be nullable if the '?' token is used to make the matched option string optional.
+ regexp += ")" + ((optionsAdded > 0) ? "?" : "") + "(.*)";
+ Pattern pattern = Pattern.compile(regexp);
+
+ // Loop through all the command line arguments.
+ for (int i = 0; i < args.length; i++)
+ {
+ // Check if the next command line argument begins with a '-' character and is therefore the start of
+ // an option.
+ if (args[i].startsWith("-"))
+ {
+ // Extract the value of the option without the leading '-'.
+ String arg = args[i].substring(1);
+
+ // Match up to the longest matching option.
+ optionMatcher = pattern.matcher(arg);
+ optionMatcher.matches();
+
+ String matchedOption = optionMatcher.group(1);
+
+ // Match any argument directly appended onto the longest matching option.
+ String matchedArg = optionMatcher.group(2);
+
+ // Check that a known option was matched.
+ if ((matchedOption != null) && !"".equals(matchedOption))
+ {
+ // Get the command line option information for the matched option.
+ CommandLineOption optionInfo = optionMap.get(matchedOption);
+
+ // Check if this option is expecting arguments.
+ if (optionInfo.expectsArgs)
+ {
+ // The option is expecting arguments so swallow the next command line argument as an
+ // argument to this option.
+ expectingArgs = true;
+ optionExpectingArgs = matchedOption;
+
+ // In the mean time set this options argument to the empty string in case no argument is ever
+ // supplied.
+ // options.put(matchedOption, "");
+ }
+
+ // Check if the option was matched on its own and is a flag in which case set that flag.
+ if ("".equals(matchedArg) && !optionInfo.expectsArgs)
+ {
+ options.put(matchedOption, "true");
+ }
+ // The option was matched as a substring with its argument appended to it or is a flag that is
+ // condensed together with other flags.
+ else if (!"".equals(matchedArg))
+ {
+ // Check if the option is a flag and therefore is allowed to be condensed together
+ // with other flags.
+ if (!optionInfo.expectsArgs)
+ {
+ // Set the first matched flag.
+ options.put(matchedOption, "true");
+
+ // Repeat the longest matching process on the remainder but ensure that the remainder
+ // consists only of flags as only flags may be condensed together in this fashion.
+ do
+ {
+ // Match the remainder against the options.
+ optionMatcher = pattern.matcher(matchedArg);
+ optionMatcher.matches();
+
+ matchedOption = optionMatcher.group(1);
+ matchedArg = optionMatcher.group(2);
+
+ // Check that an option was matched.
+ if (matchedOption != null)
+ {
+ // Get the command line option information for the next matched option.
+ optionInfo = optionMap.get(matchedOption);
+
+ // Ensure that the next option is a flag or raise an error if not.
+ if (optionInfo.expectsArgs == true)
+ {
+ parsingErrors.add("Option " + matchedOption + " cannot be combined with flags.\n");
+ }
+
+ options.put(matchedOption, "true");
+ }
+ // The remainder could not be matched against a flag it is either an unknown flag
+ // or an illegal argument to a flag.
+ else
+ {
+ parsingErrors.add("Illegal argument to a flag in the option " + arg + "\n");
+
+ break;
+ }
+ }
+ // Continue until the remainder of the argument has all been matched with flags.
+ while (!"".equals(matchedArg));
+ }
+ // The option is expecting an argument, so store the unmatched portion against it
+ // as its argument.
+ else
+ {
+ // Check the arguments format is correct against any specified format.
+ checkArgumentFormat(optionInfo, matchedArg);
+
+ // Store the argument against its option (regardless of its format).
+ options.put(matchedOption, matchedArg);
+
+ // The argument to this flag has already been supplied to it. Do not swallow the
+ // next command line argument as an argument to this flag.
+ expectingArgs = false;
+ }
+ }
+ }
+ else // No matching option was found.
+ {
+ // Add this to the list of parsing errors if errors on unkowns is being used.
+ if (errorsOnUnknowns)
+ {
+ parsingErrors.add("Option " + matchedOption + " is not a recognized option.\n");
+ }
+ }
+ }
+ // The command line argument did not being with a '-' so it is an argument to the previous flag or it
+ // is a free argument.
+ else
+ {
+ // Check if a previous flag is expecting to swallow this next argument as its argument.
+ if (expectingArgs)
+ {
+ // Get the option info for the option waiting for arguments.
+ CommandLineOption optionInfo = optionMap.get(optionExpectingArgs);
+
+ // Check the arguments format is correct against any specified format.
+ checkArgumentFormat(optionInfo, args[i]);
+
+ // Store the argument against its option (regardless of its format).
+ options.put(optionExpectingArgs, args[i]);
+
+ // Clear the expecting args flag now that the argument has been swallowed.
+ expectingArgs = false;
+ optionExpectingArgs = null;
+ }
+ // This command line option is not an argument to any option. Add it to the set of 'free' options.
+ else
+ {
+ // Get the option info for the free option, if there is any.
+ CommandLineOption optionInfo = optionMap.get(Integer.toString(free));
+
+ if (optionInfo != null)
+ {
+ // Check the arguments format is correct against any specified format.
+ checkArgumentFormat(optionInfo, args[i]);
+ }
+
+ // Add to the list of free options.
+ options.put(Integer.toString(free), args[i]);
+
+ // Move on to the next free argument.
+ free++;
+ }
+ }
+ }
+
+ // Scan through all the specified options to check that all mandatory options have been set and that all flags
+ // that were not set are set to false in the set of properties.
+ for (CommandLineOption optionInfo : optionMap.values())
+ {
+ // Check if this is a flag.
+ if (!optionInfo.expectsArgs)
+ {
+ // Check if the flag is not set in the properties and set it to false if so.
+ if (!options.containsKey(optionInfo.option))
+ {
+ options.put(optionInfo.option, "false");
+ }
+ }
+ // Check if this is a mandatory option and was not set.
+ else if (optionInfo.mandatory && !options.containsKey(optionInfo.option))
+ {
+ // Create an error for the missing option.
+ parsingErrors.add("Option " + optionInfo.option + " is mandatory but not was not specified.\n");
+ }
+ }
+
+ // Check if there were any errors.
+ if (!parsingErrors.isEmpty())
+ {
+ // Throw an illegal argument exception to signify that there were parsing errors.
+ throw new IllegalArgumentException();
+ }
+
+ // Convert any name/value pairs in the free arguments into properties in the parsed options.
+ options = takeFreeArgsAsProperties(options, 1);
+
+ parsedProperties = options;
+
+ return options;
+ }
+
+ /**
+ * If a command line has been parsed, calling this method sets all of its parsed options into the specified properties.
+ */
+ public void addCommandLineToProperties(Properties properties)
+ {
+ if (parsedProperties != null)
+ {
+ for (Object propKey : parsedProperties.keySet())
+ {
+ String name = (String) propKey;
+ String value = parsedProperties.getProperty(name);
+
+ properties.setProperty(name, value);
+ }
+ }
+ }
+
+ /**
+ * Resets this command line parser after it has been used to parse a command line. This method will only need
+ * to be called to use this parser a second time which is not likely seeing as a command line is usually only
+ * specified once. However, it is exposed as a public method for the rare case where this may be done.
+ *
+ * <p/>Cleans the internal state of this parser, removing all stored errors and information about the options in
+ * force.
+ */
+ public void reset()
+ {
+ parsingErrors = new ArrayList<String>();
+ parsedProperties = null;
+ }
+
+ /**
+ * Adds the option to list of available command line options.
+ *
+ * @param option The option to add as an available command line option.
+ * @param comment A comment for the option.
+ * @param argument The text that appears after the option in the usage string.
+ * @param mandatory When true, indicates that this option is mandatory.
+ * @param formatRegexp The format that the argument must take, defined as a regular expression.
+ */
+ protected void addOption(String option, String comment, String argument, boolean mandatory, String formatRegexp)
+ {
+ // Check if usage text has been set in which case this option is expecting arguments.
+ boolean expectsArgs = ((argument == null) || argument.equals("")) ? false : true;
+
+ // Add the option to the map of command line options.
+ CommandLineOption opt = new CommandLineOption(option, expectsArgs, comment, argument, mandatory, formatRegexp);
+ optionMap.put(option, opt);
+ }
+
+ /**
+ * Converts the free arguments into property declarations. After parsing the command line the free arguments
+ * are numbered from 1, such that the parsed properties contain values for the keys "1", "2", ... This method
+ * converts any free arguments declared using the 'name=value' syntax into properties with key 'name', value
+ * 'value'.
+ *
+ * <p/>For example the comand line:
+ * <pre>
+ * ... debug=true
+ * </pre>
+ *
+ * <p/>After parsing has properties:
+ * <pre>[[1, debug=true]]</pre>
+ *
+ * <p/>After applying this method the properties are:
+ * <pre>[[1, debug=true], [debug, true]]</pre>
+ *
+ * @param properties The parsed command line properties.
+ * @param from The free argument index to convert to properties from.
+ *
+ * @return The parsed command line properties, with free argument name value pairs too.
+ */
+ private Properties takeFreeArgsAsProperties(Properties properties, int from)
+ {
+ for (int i = from; true; i++)
+ {
+ String nextFreeArg = properties.getProperty(Integer.toString(i));
+
+ // Terminate the loop once all free arguments have been consumed.
+ if (nextFreeArg == null)
+ {
+ break;
+ }
+
+ // Split it on the =, strip any whitespace and set it as a system property.
+ String[] nameValuePair = nextFreeArg.split("=");
+
+ if (nameValuePair.length == 2)
+ {
+ properties.setProperty(nameValuePair[0], nameValuePair[1]);
+ }
+ }
+
+ return properties;
+ }
+
+ /**
+ * Checks the format of an argument to an option against its specified regular expression format if one has
+ * been set. Any errors are added to the list of parsing errors.
+ *
+ * @param optionInfo The command line option information for the option which is havings its argument checked.
+ * @param matchedArg The string argument to the option.
+ */
+ private void checkArgumentFormat(CommandLineOption optionInfo, String matchedArg)
+ {
+ // Check if this option enforces a format for its argument.
+ if (optionInfo.argumentFormatRegexp != null)
+ {
+ Pattern pattern = Pattern.compile(optionInfo.argumentFormatRegexp);
+ Matcher argumentMatcher = pattern.matcher(matchedArg);
+
+ // Check if the argument does not meet its required format.
+ if (!argumentMatcher.matches())
+ {
+ // Create an error for this badly formed argument.
+ parsingErrors.add("The argument to option " + optionInfo.option + " does not meet its required format.\n");
+ }
+ }
+ }
+
+ /**
+ * Extracts all name=value pairs from the command line, sets them all as system properties and also returns
+ * a map of properties containing them.
+ *
+ * @param args The command line.
+ * @param commandLine The command line parser.
+ * @param properties The properties object to inject all parsed properties into (optional may be <tt>null</tt>).
+ *
+ * @return A set of properties containing all name=value pairs from the command line.
+ */
+ public static Properties processCommandLine(String[] args, CommandLineParser commandLine, Properties properties)
+ {
+ // Capture the command line arguments or display errors and correct usage and then exit.
+ Properties options = null;
+
+ try
+ {
+ options = commandLine.parseCommandLine(args);
+
+ // Add all the trailing command line options (name=value pairs) to system properties. They may be picked up
+ // from there.
+ commandLine.addCommandLineToProperties(properties);
+ }
+ catch (IllegalArgumentException e)
+ {
+ System.out.println(commandLine.getErrors());
+ System.out.println(commandLine.getUsage());
+ System.exit(1);
+ }
+
+ return options;
+ }
+
+ /**
+ * Holds information about a command line options. This includes what its name is, whether or not it is a flag,
+ * whether or not it is mandatory, what its user comment is, what its argument reminder text is and what its
+ * regular expression format is.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Hold details of a command line option.
+ * </table>
+ */
+ protected class CommandLineOption
+ {
+ /** Holds the text for the flag to match this argument with. */
+ public String option = null;
+
+ /** Holds a string describing how to use this command line argument. */
+ public String argument = null;
+
+ /** Flag that determines whether or not this command line argument can take arguments. */
+ public boolean expectsArgs = false;
+
+ /** Holds a short comment describing what this command line argument is for. */
+ public String comment = null;
+
+ /** Flag that determines whether or not this is an mandatory command line argument. */
+ public boolean mandatory = false;
+
+ /** A regular expression describing what format the argument to this option muist have. */
+ public String argumentFormatRegexp = null;
+
+ /**
+ * Create a command line option object that holds specific information about a command line option.
+ *
+ * @param option The text that matches the option.
+ * @param expectsArgs Whether or not the option expects arguments. It is a flag if this is false.
+ * @param comment A comment explaining how to use this option.
+ * @param argument A short reminder of the format of the argument to this option/
+ * @param mandatory Set to true if this option is mandatory.
+ * @param formatRegexp The regular expression that the argument to this option must meet to be valid.
+ */
+ public CommandLineOption(String option, boolean expectsArgs, String comment, String argument, boolean mandatory,
+ String formatRegexp)
+ {
+ this.option = option;
+ this.expectsArgs = expectsArgs;
+ this.comment = comment;
+ this.argument = argument;
+ this.mandatory = mandatory;
+ this.argumentFormatRegexp = formatRegexp;
+ }
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedMessageQueueAtomicSize.java b/Final/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedMessageQueueAtomicSize.java new file mode 100644 index 0000000000..461cf9591d --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedMessageQueueAtomicSize.java @@ -0,0 +1,250 @@ +/* + * 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.qpid.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Queue; +import java.util.concurrent.atomic.AtomicInteger; + +public class ConcurrentLinkedMessageQueueAtomicSize<E> extends ConcurrentLinkedQueueAtomicSize<E> implements MessageQueue<E> +{ + private static final Logger _logger = LoggerFactory.getLogger(ConcurrentLinkedMessageQueueAtomicSize.class); + + protected Queue<E> _messageHead = new ConcurrentLinkedQueueAtomicSize<E>(); + + protected AtomicInteger _messageHeadSize = new AtomicInteger(0); + + @Override + public int size() + { + return super.size() + _messageHeadSize.get(); + } + + public int headSize() + { + return _messageHeadSize.get(); + } + + @Override + public E poll() + { + if (_messageHead.isEmpty()) + { + return super.poll(); + } + else + { + E e = _messageHead.poll(); + + if (_logger.isDebugEnabled()) + { + _logger.debug("Providing item(" + e + ")from message head"); + } + + if (e != null) + { + _messageHeadSize.decrementAndGet(); + } + + return e; + } + } + + @Override + public boolean remove(Object o) + { + + if (_messageHead.isEmpty()) + { + return super.remove(o); + } + else + { + if (_messageHead.remove(o)) + { + _messageHeadSize.decrementAndGet(); + + return true; + } + + return super.remove(o); + } + } + + @Override + public boolean removeAll(Collection<?> c) + { + if (_messageHead.isEmpty()) + { + return super.removeAll(c); + } + else + { + // fixme this is super.removeAll but iterator here doesn't work + // we need to be able to correctly decrement _messageHeadSize + // boolean modified = false; + // Iterator<?> e = iterator(); + // while (e.hasNext()) + // { + // if (c.contains(e.next())) + // { + // e.remove(); + // modified = true; + // _size.decrementAndGet(); + // } + // } + // return modified; + + throw new RuntimeException("Not implemented"); + } + } + + @Override + public boolean isEmpty() + { + return (_messageHead.isEmpty() && super.isEmpty()); + } + + @Override + public void clear() + { + super.clear(); + _messageHead.clear(); + } + + @Override + public boolean contains(Object o) + { + return _messageHead.contains(o) || super.contains(o); + } + + @Override + public boolean containsAll(Collection<?> o) + { + return _messageHead.containsAll(o) || super.containsAll(o); + } + + @Override + public E element() + { + if (_messageHead.isEmpty()) + { + return super.element(); + } + else + { + return _messageHead.element(); + } + } + + @Override + public E peek() + { + if (_messageHead.isEmpty()) + { + return super.peek(); + } + else + { + E o = _messageHead.peek(); + if (_logger.isDebugEnabled()) + { + _logger.debug("Peeking item (" + o + ") from message head"); + } + + return o; + } + + } + + @Override + public Iterator<E> iterator() + { + final Iterator<E> mainMessageIterator = super.iterator(); + + return new Iterator<E>() + { + final Iterator<E> _headIterator = _messageHead.iterator(); + final Iterator<E> _mainIterator = mainMessageIterator; + + Iterator<E> last; + + public boolean hasNext() + { + return _headIterator.hasNext() || _mainIterator.hasNext(); + } + + public E next() + { + if (_headIterator.hasNext()) + { + last = _headIterator; + + return _headIterator.next(); + } + else + { + last = _mainIterator; + + return _mainIterator.next(); + } + } + + public void remove() + { + last.remove(); + } + }; + } + + @Override + public boolean retainAll(Collection<?> c) + { + throw new RuntimeException("Not Implemented"); + } + + @Override + public Object[] toArray() + { + throw new RuntimeException("Not Implemented"); + } + + public boolean pushHead(E o) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Adding item(" + o + ") to head of queue"); + } + + if (_messageHead.offer(o)) + { + _messageHeadSize.incrementAndGet(); + + return true; + } + + return false; + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueAtomicSize.java b/Final/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueAtomicSize.java new file mode 100644 index 0000000000..c4d7683a02 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueAtomicSize.java @@ -0,0 +1,70 @@ +/* + * + * 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.qpid.util; + +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; + +public class ConcurrentLinkedQueueAtomicSize<E> extends ConcurrentLinkedQueue<E> +{ + AtomicInteger _size = new AtomicInteger(0); + + public int size() + { + return _size.get(); + } + + public boolean offer(E o) + { + + if (super.offer(o)) + { + _size.incrementAndGet(); + return true; + } + + return false; + } + + public E poll() + { + E e = super.poll(); + + if (e != null) + { + _size.decrementAndGet(); + } + + return e; + } + + @Override + public boolean remove(Object o) + { + if (super.remove(o)) + { + _size.decrementAndGet(); + return true; + } + + return false; + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueNoSize.java b/Final/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueNoSize.java new file mode 100644 index 0000000000..1f168345a1 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueNoSize.java @@ -0,0 +1,38 @@ +/* + * + * 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.qpid.util; + +import java.util.concurrent.ConcurrentLinkedQueue; + +public class ConcurrentLinkedQueueNoSize<E> extends ConcurrentLinkedQueue<E> +{ + public int size() + { + if (isEmpty()) + { + return 0; + } + else + { + return 1; + } + } +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/util/FileUtils.java b/Final/java/common/src/main/java/org/apache/qpid/util/FileUtils.java new file mode 100644 index 0000000000..3b8ebc1666 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/util/FileUtils.java @@ -0,0 +1,195 @@ +/*
+ *
+ * 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.qpid.util;
+
+import java.io.*;
+
+/**
+ * FileUtils provides some simple helper methods for working with files. It follows the convention of wrapping all
+ * checked exceptions as runtimes, so code using these methods is free of try-catch blocks but does not expect to
+ * recover from errors.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Read a text file as a string.
+ * <tr><td> Open a file or default resource as an input stream.
+ * </table>
+ */
+public class FileUtils
+{
+ /**
+ * Reads a text file as a string.
+ *
+ * @param filename The name of the file.
+ *
+ * @return The contents of the file.
+ */
+ public static String readFileAsString(String filename)
+ {
+ BufferedInputStream is = null;
+
+ try
+ {
+ is = new BufferedInputStream(new FileInputStream(filename));
+ }
+ catch (FileNotFoundException e)
+ {
+ throw new RuntimeException(e);
+ }
+
+ return readStreamAsString(is);
+ }
+
+ /**
+ * Reads a text file as a string.
+ *
+ * @param file The file.
+ *
+ * @return The contents of the file.
+ */
+ public static String readFileAsString(File file)
+ {
+ BufferedInputStream is = null;
+
+ try
+ {
+ is = new BufferedInputStream(new FileInputStream(file));
+ }
+ catch (FileNotFoundException e)
+ {
+ throw new RuntimeException(e);
+ }
+
+ return readStreamAsString(is);
+ }
+
+ /**
+ * Reads the contents of a reader, one line at a time until the end of stream is encountered, and returns all
+ * together as a string.
+ *
+ * @param is The reader.
+ *
+ * @return The contents of the reader.
+ */
+ private static String readStreamAsString(BufferedInputStream is)
+ {
+ try
+ {
+ byte[] data = new byte[4096];
+
+ StringBuffer inBuffer = new StringBuffer();
+
+ String line;
+ int read;
+
+ while ((read = is.read(data)) != -1)
+ {
+ String s = new String(data, 0, read);
+ inBuffer.append(s);
+ }
+
+ return inBuffer.toString();
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Either opens the specified filename as an input stream, or uses the default resource loaded using the
+ * specified class loader, if opening the file fails or no file name is specified.
+ *
+ * @param filename The name of the file to open.
+ * @param defaultResource The name of the default resource on the classpath if the file cannot be opened.
+ * @param cl The classloader to load the default resource with.
+ *
+ * @return An input stream for the file or resource, or null if one could not be opened.
+ */
+ public static InputStream openFileOrDefaultResource(String filename, String defaultResource, ClassLoader cl)
+ {
+ InputStream is = null;
+
+ // Flag to indicate whether the default resource should be used. By default this is true, so that the default
+ // is used when opening the file fails.
+ boolean useDefault = true;
+
+ // Try to open the file if one was specified.
+ if (filename != null)
+ {
+ try
+ {
+ is = new BufferedInputStream(new FileInputStream(new File(filename)));
+
+ // Clear the default flag because the file was succesfully opened.
+ useDefault = false;
+ }
+ catch (FileNotFoundException e)
+ {
+ // Ignore this exception, the default will be used instead.
+ }
+ }
+
+ // Load the default resource if a file was not specified, or if opening the file failed.
+ if (useDefault)
+ {
+ is = cl.getResourceAsStream(defaultResource);
+ }
+
+ return is;
+ }
+
+ /**
+ * Copies the specified source file to the specified destintaion file. If the destinationst file does not exist,
+ * it is created.
+ *
+ * @param src The source file name.
+ * @param dst The destination file name.
+ */
+ public static void copy(File src, File dst)
+ {
+ try
+ {
+ InputStream in = new FileInputStream(src);
+ if (!dst.exists())
+ {
+ dst.createNewFile();
+ }
+
+ OutputStream out = new FileOutputStream(dst);
+
+ // Transfer bytes from in to out
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = in.read(buf)) > 0)
+ {
+ out.write(buf, 0, len);
+ }
+
+ in.close();
+ out.close();
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/util/MessageQueue.java b/Final/java/common/src/main/java/org/apache/qpid/util/MessageQueue.java new file mode 100644 index 0000000000..b5efaa61b6 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/util/MessageQueue.java @@ -0,0 +1,43 @@ +/* + * 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.qpid.util; + +import java.util.Queue; + +/** + * Defines a queue that has a push operation to add an element to the head of the queue. + * + * @todo Seems like this may be pointless, the implementation uses this method to increment the message count + * then calls offer. Why not simply override offer and drop this interface? + */ +public interface MessageQueue<E> extends Queue<E> +{ + /** + * Inserts the specified element into this queue, if possible. When using queues that may impose insertion + * restrictions (for example capacity bounds), method offer is generally preferable to method Collection.add(E), + * which can fail to insert an element only by throwing an exception. + * + * @param o The element to insert. + * + * @return <tt>true</tt> if it was possible to add the element to this queue, else <tt>false</tt> + */ + boolean pushHead(E o); +} diff --git a/Final/java/common/src/main/java/org/apache/qpid/util/PrettyPrintingUtils.java b/Final/java/common/src/main/java/org/apache/qpid/util/PrettyPrintingUtils.java new file mode 100644 index 0000000000..10f6a27293 --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/util/PrettyPrintingUtils.java @@ -0,0 +1,75 @@ +/*
+ *
+ * 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.qpid.util;
+
+/**
+ * Contains pretty printing convenienve methods for producing formatted logging output, mostly for debugging purposes.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * </table>
+ *
+ * @todo Drop this. There are already array pretty printing methods it java.utils.Arrays.
+ */
+public class PrettyPrintingUtils
+{
+ /**
+ * Pretty prints an array of ints as a string.
+ *
+ * @param array The array to pretty print.
+ *
+ * @return The pretty printed string.
+ */
+ public static String printArray(int[] array)
+ {
+ String result = "[";
+ for (int i = 0; i < array.length; i++)
+ {
+ result += array[i];
+ result += (i < (array.length - 1)) ? ", " : "";
+ }
+
+ result += "]";
+
+ return result;
+ }
+
+ /**
+ * Pretty prints an array of strings as a string.
+ *
+ * @param array The array to pretty print.
+ *
+ * @return The pretty printed string.
+ */
+ public static String printArray(String[] array)
+ {
+ String result = "[";
+ for (int i = 0; i < array.length; i++)
+ {
+ result += array[i];
+ result += (i < (array.length - 1)) ? ", " : "";
+ }
+
+ result += "]";
+
+ return result;
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java b/Final/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java new file mode 100644 index 0000000000..63cf6f252b --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java @@ -0,0 +1,200 @@ +/*
+ *
+ * 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.qpid.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.Properties;
+
+/**
+ * PropertiesHelper defines some static methods which are useful when working with properties
+ * files.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Read properties from an input stream
+ * <tr><td> Read properties from a file
+ * <tr><td> Read properties from a URL
+ * <tr><td> Read properties given a path to a file
+ * <tr><td> Trim any whitespace from property values
+ * </table>
+ */
+public class PropertiesUtils
+{
+ /** Used for logging. */
+ private static final Logger log = LoggerFactory.getLogger(PropertiesUtils.class);
+
+ /**
+ * Get properties from an input stream.
+ *
+ * @param is The input stream.
+ *
+ * @return The properties loaded from the input stream.
+ *
+ * @throws IOException If the is an I/O error reading from the stream.
+ */
+ public static Properties getProperties(InputStream is) throws IOException
+ {
+ log.debug("getProperties(InputStream): called");
+
+ // Create properties object laoded from input stream
+ Properties properties = new Properties();
+
+ properties.load(is);
+
+ return properties;
+ }
+
+ /**
+ * Get properties from a file.
+ *
+ * @param file The file.
+ *
+ * @return The properties loaded from the file.
+ *
+ * @throws IOException If there is an I/O error reading from the file.
+ */
+ public static Properties getProperties(File file) throws IOException
+ {
+ log.debug("getProperties(File): called");
+
+ // Open the file as an input stream
+ InputStream is = new FileInputStream(file);
+
+ // Create properties object loaded from the stream
+ Properties properties = getProperties(is);
+
+ // Close the file
+ is.close();
+
+ return properties;
+ }
+
+ /**
+ * Get properties from a url.
+ *
+ * @param url The URL.
+ *
+ * @return The properties loaded from the url.
+ *
+ * @throws IOException If there is an I/O error reading from the URL.
+ */
+ public static Properties getProperties(URL url) throws IOException
+ {
+ log.debug("getProperties(URL): called");
+
+ // Open the URL as an input stream
+ InputStream is = url.openStream();
+
+ // Create properties object loaded from the stream
+ Properties properties = getProperties(is);
+
+ // Close the url
+ is.close();
+
+ return properties;
+ }
+
+ /**
+ * Get properties from a path name. The path name may refer to either a file or a URL.
+ *
+ * @param pathname The path name.
+ *
+ * @return The properties loaded from the file or URL.
+ *
+ * @throws IOException If there is an I/O error reading from the URL or file named by the path.
+ */
+ public static Properties getProperties(String pathname) throws IOException
+ {
+ log.debug("getProperties(String): called");
+
+ // Check that the path is not null
+ if (pathname == null)
+ {
+ return null;
+ }
+
+ // Check if the path is a URL
+ if (isURL(pathname))
+ {
+ // The path is a URL
+ return getProperties(new URL(pathname));
+ }
+ else
+ {
+ // Assume the path is a file name
+ return getProperties(new File(pathname));
+ }
+ }
+
+ /**
+ * Trims whitespace from property values. This method returns a new set of properties
+ * the same as the properties specified as an argument but with any white space removed by
+ * the {@link java.lang.String#trim} method.
+ *
+ * @param properties The properties to trim whitespace from.
+ *
+ * @return The white space trimmed properties.
+ */
+ public static Properties trim(Properties properties)
+ {
+ Properties trimmedProperties = new Properties();
+
+ // Loop over all the properties
+ for (Iterator i = properties.keySet().iterator(); i.hasNext();)
+ {
+ String next = (String) i.next();
+ String nextValue = properties.getProperty(next);
+
+ // Trim the value if it is not null
+ if (nextValue != null)
+ {
+ nextValue.trim();
+ }
+
+ // Store the trimmed value in the trimmed properties
+ trimmedProperties.setProperty(next, nextValue);
+ }
+
+ return trimmedProperties;
+ }
+
+ /**
+ * Helper method. Guesses whether a string is a URL or not. A String is considered to be a url if it begins with
+ * http:, ftp:, or uucp:.
+ *
+ * @param name The string to test for being a URL.
+ *
+ * @return True if the string is a URL and false if not.
+ */
+ private static boolean isURL(String name)
+ {
+ return (name.toLowerCase().startsWith("http:") || name.toLowerCase().startsWith("ftp:")
+ || name.toLowerCase().startsWith("uucp:"));
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/util/ReflectionUtils.java b/Final/java/common/src/main/java/org/apache/qpid/util/ReflectionUtils.java new file mode 100644 index 0000000000..495918911a --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/util/ReflectionUtils.java @@ -0,0 +1,228 @@ +/*
+ *
+ * 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.qpid.util;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Provides helper methods for operating on classes and methods using reflection. Reflection methods tend to return
+ * a lot of checked exception so writing code to use them can be tedious and harder to read, especially when such errors
+ * are not expected to occur. This class always works with {@link ReflectionUtilsException}, which is a runtime exception,
+ * to wrap the checked exceptions raised by the standard Java reflection methods. Code using it does not normally
+ * expect these errors to occur, usually does not have a recovery mechanism for them when they do, but is cleaner,
+ * quicker to write and easier to read in the majority of cases.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Look up Classes by name.
+ * <tr><td> Instantiate Classes by no-arg constructor.
+ * </table>
+ */
+public class ReflectionUtils
+{
+ /**
+ * Gets the Class object for a named class.
+ *
+ * @param className The class to get the Class object for.
+ *
+ * @return The Class object for the named class.
+ */
+ public static Class<?> forName(String className)
+ {
+ try
+ {
+ return Class.forName(className);
+ }
+ catch (ClassNotFoundException e)
+ {
+ throw new ReflectionUtilsException("ClassNotFoundException whilst finding class.", e);
+ }
+ }
+
+ /**
+ * Creates an instance of a Class, instantiated through its no-args constructor.
+ *
+ * @param cls The Class to instantiate.
+ * @param <T> The Class type.
+ *
+ * @return An instance of the class.
+ */
+ public static <T> T newInstance(Class<? extends T> cls)
+ {
+ try
+ {
+ return cls.newInstance();
+ }
+ catch (InstantiationException e)
+ {
+ throw new ReflectionUtilsException("InstantiationException whilst instantiating class.", e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new ReflectionUtilsException("IllegalAccessException whilst instantiating class.", e);
+ }
+ }
+
+ /**
+ * Calls a named method on an object with a specified set of parameters, any Java access modifier are overridden.
+ *
+ * @param o The object to call.
+ * @param method The method name to call.
+ * @param params The parameters to pass.
+ * @param paramClasses The argument types.
+ *
+ * @return The return value from the method call.
+ */
+ public static Object callMethodOverridingIllegalAccess(Object o, String method, Object[] params, Class[] paramClasses)
+ {
+ // Get the objects class.
+ Class cls = o.getClass();
+
+ // Get the classes of the parameters.
+ /*Class[] paramClasses = new Class[params.length];
+
+ for (int i = 0; i < params.length; i++)
+ {
+ paramClasses[i] = params[i].getClass();
+ }*/
+
+ try
+ {
+ // Try to find the matching method on the class.
+ Method m = cls.getDeclaredMethod(method, paramClasses);
+
+ // Make it accessible.
+ m.setAccessible(true);
+
+ // Invoke it with the parameters.
+ return m.invoke(o, params);
+ }
+ catch (NoSuchMethodException e)
+ {
+ throw new ReflectionUtilsException("NoSuchMethodException.", e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new ReflectionUtilsException("IllegalAccessException.", e);
+ }
+ catch (InvocationTargetException e)
+ {
+ throw new ReflectionUtilsException("InvocationTargetException", e);
+ }
+ }
+
+ /**
+ * Calls a named method on an object with a specified set of parameters.
+ *
+ * @param o The object to call.
+ * @param method The method name to call.
+ * @param params The parameters to pass.
+ *
+ * @return The return value from the method call.
+ */
+ public static Object callMethod(Object o, String method, Object[] params)
+ {
+ // Get the objects class.
+ Class cls = o.getClass();
+
+ // Get the classes of the parameters.
+ Class[] paramClasses = new Class[params.length];
+
+ for (int i = 0; i < params.length; i++)
+ {
+ paramClasses[i] = params[i].getClass();
+ }
+
+ try
+ {
+ // Try to find the matching method on the class.
+ Method m = cls.getMethod(method, paramClasses);
+
+ // Invoke it with the parameters.
+ return m.invoke(o, params);
+ }
+ catch (NoSuchMethodException e)
+ {
+ throw new ReflectionUtilsException("NoSuchMethodException.", e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new ReflectionUtilsException("IllegalAccessException", e);
+ }
+ catch (InvocationTargetException e)
+ {
+ throw new ReflectionUtilsException("InvocationTargetException", e);
+ }
+ }
+
+ /**
+ * Calls a constuctor witht the specified arguments.
+ *
+ * @param constructor The constructor.
+ * @param args The arguments.
+ * @param <T> The Class type.
+ *
+ * @return An instance of the class that the constructor is for.
+ */
+ public static <T> T newInstance(Constructor<T> constructor, Object[] args)
+ {
+ try
+ {
+ return constructor.newInstance(args);
+ }
+ catch (InstantiationException e)
+ {
+ throw new ReflectionUtilsException("InstantiationException", e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new ReflectionUtilsException("IllegalAccessException", e);
+ }
+ catch (InvocationTargetException e)
+ {
+ throw new ReflectionUtilsException("InvocationTargetException", e);
+ }
+ }
+
+ /**
+ * Gets the constructor of a class that takes the specified set of arguments if any matches. If no matching
+ * constructor is found then a runtime exception is raised.
+ *
+ * @param cls The class to get a constructor from.
+ * @param args The arguments to match.
+ * @param <T> The class type.
+ *
+ * @return The constructor.
+ */
+ public static <T> Constructor<T> getConstructor(Class<T> cls, Class[] args)
+ {
+ try
+ {
+ return cls.getConstructor(args);
+ }
+ catch (NoSuchMethodException e)
+ {
+ throw new ReflectionUtilsException("NoSuchMethodException", e);
+ }
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/util/ReflectionUtilsException.java b/Final/java/common/src/main/java/org/apache/qpid/util/ReflectionUtilsException.java new file mode 100644 index 0000000000..20499641ac --- /dev/null +++ b/Final/java/common/src/main/java/org/apache/qpid/util/ReflectionUtilsException.java @@ -0,0 +1,44 @@ +/*
+ *
+ * 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.qpid.util;
+
+/**
+ * Wraps a checked exception that occurs when {@link ReflectionUtils} encounters checked exceptions using standard
+ * Java reflection methods.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Wrap a checked reflection exception.
+ * </table>
+ */
+public class ReflectionUtilsException extends RuntimeException
+{
+ /**
+ * Creates a runtime reflection exception, from a checked one.
+ *
+ * @param message The message.
+ * @param cause The causing exception.
+ */
+ public ReflectionUtilsException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/Final/java/common/src/main/resources/org/apache/qpid/ssl/qpid.cert b/Final/java/common/src/main/resources/org/apache/qpid/ssl/qpid.cert Binary files differnew file mode 100644 index 0000000000..e6702108e6 --- /dev/null +++ b/Final/java/common/src/main/resources/org/apache/qpid/ssl/qpid.cert diff --git a/Final/java/common/src/test/java/org/apache/qpid/framing/BasicContentHeaderPropertiesTest.java b/Final/java/common/src/test/java/org/apache/qpid/framing/BasicContentHeaderPropertiesTest.java new file mode 100644 index 0000000000..4fd1f60d69 --- /dev/null +++ b/Final/java/common/src/test/java/org/apache/qpid/framing/BasicContentHeaderPropertiesTest.java @@ -0,0 +1,188 @@ +/* + * + * 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.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +import junit.framework.TestCase; + + +public class BasicContentHeaderPropertiesTest extends TestCase +{ + + BasicContentHeaderProperties _testProperties; + FieldTable _testTable; + String _testString = "This is a test string"; + int _testint = 666; + + /** + * Currently only test setting/getting String, int and boolean props + */ + public BasicContentHeaderPropertiesTest() + { + _testProperties = new BasicContentHeaderProperties(); + } + + public void setUp() + { + _testTable = new FieldTable(); + _testTable.setString("TestString", _testString); + _testTable.setInteger("Testint", _testint); + _testProperties = new BasicContentHeaderProperties(); + _testProperties.setHeaders(_testTable); + } + + public void testGetPropertyListSize() + { + //needs a better test but at least we're exercising the code ! + // FT length is encoded in an int + int expectedSize = EncodingUtils.encodedIntegerLength(); + + expectedSize += EncodingUtils.encodedShortStringLength("TestInt"); + // 1 is for the Encoding Letter. here an 'i' + expectedSize += 1 + EncodingUtils.encodedIntegerLength(); + + expectedSize += EncodingUtils.encodedShortStringLength("TestString"); + // 1 is for the Encoding Letter. here an 'S' + expectedSize += 1 + EncodingUtils.encodedLongStringLength(_testString); + + + int size = _testProperties.getPropertyListSize(); + + assertEquals(expectedSize, size); + } + + public void testGetSetPropertyFlags() + { + _testProperties.setPropertyFlags(99); + assertEquals(99, _testProperties.getPropertyFlags()); + } + + public void testWritePropertyListPayload() + { + ByteBuffer buf = ByteBuffer.allocate(300); + _testProperties.writePropertyListPayload(buf); + } + + public void testPopulatePropertiesFromBuffer() throws Exception + { + ByteBuffer buf = ByteBuffer.allocate(300); + _testProperties.populatePropertiesFromBuffer(buf, 99, 99); + } + + public void testSetGetContentType() + { + String contentType = "contentType"; + _testProperties.setContentType(contentType); + assertEquals(contentType, _testProperties.getContentTypeAsString()); + } + + public void testSetGetEncoding() + { + String encoding = "encoding"; + _testProperties.setEncoding(encoding); + assertEquals(encoding, _testProperties.getEncodingAsString()); + } + + public void testSetGetHeaders() + { + _testProperties.setHeaders(_testTable); + assertEquals(_testTable, _testProperties.getHeaders()); + } + + public void testSetGetDeliveryMode() + { + byte deliveryMode = 1; + _testProperties.setDeliveryMode(deliveryMode); + assertEquals(deliveryMode, _testProperties.getDeliveryMode()); + } + + public void testSetGetPriority() + { + byte priority = 1; + _testProperties.setPriority(priority); + assertEquals(priority, _testProperties.getPriority()); + } + + public void testSetGetCorrelationId() + { + String correlationId = "correlationId"; + _testProperties.setCorrelationId(correlationId); + assertEquals(correlationId, _testProperties.getCorrelationIdAsString()); + } + + public void testSetGetReplyTo() + { + String replyTo = "replyTo"; + _testProperties.setReplyTo(replyTo); + assertEquals(replyTo, _testProperties.getReplyToAsString()); + } + + public void testSetGetExpiration() + { + long expiration = 999999999; + _testProperties.setExpiration(expiration); + assertEquals(expiration, _testProperties.getExpiration()); + } + + public void testSetGetMessageId() + { + String messageId = "messageId"; + _testProperties.setMessageId(messageId); + assertEquals(messageId, _testProperties.getMessageIdAsString()); + } + + public void testSetGetTimestamp() + { + long timestamp = System.currentTimeMillis(); + _testProperties.setTimestamp(timestamp); + assertEquals(timestamp, _testProperties.getTimestamp()); + } + + public void testSetGetType() + { + String type = "type"; + _testProperties.setType(type); + assertEquals(type, _testProperties.getTypeAsString()); + } + + public void testSetGetUserId() + { + String userId = "userId"; + _testProperties.setUserId(userId); + assertEquals(userId, _testProperties.getUserIdAsString()); + } + + public void testSetGetAppId() + { + String appId = "appId"; + _testProperties.setAppId(appId); + assertEquals(appId, _testProperties.getAppIdAsString()); + } + + public void testSetGetClusterId() + { + String clusterId = "clusterId"; + _testProperties.setClusterId(clusterId); + assertEquals(clusterId, _testProperties.getClusterIdAsString()); + } + +} diff --git a/Final/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java b/Final/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java new file mode 100644 index 0000000000..e63b0df770 --- /dev/null +++ b/Final/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java @@ -0,0 +1,906 @@ +/* + * 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.qpid.framing; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import org.apache.mina.common.ByteBuffer; + +import org.apache.qpid.AMQPInvalidClassException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PropertyFieldTableTest extends TestCase +{ + private static final Logger _logger = LoggerFactory.getLogger(PropertyFieldTableTest.class); + + /** + * Test that setting a similar named value replaces any previous value set on that name + */ + public void testReplacement() + { + FieldTable table1 = new FieldTable(); + // Set a boolean value + table1.setBoolean("value", true); + // Check length of table is correct (<Value length> + <type> + <Boolean length>) + int size = EncodingUtils.encodedShortStringLength("value") + 1 + EncodingUtils.encodedBooleanLength(); + Assert.assertEquals(size, table1.getEncodedSize()); + + // reset value to an integer + table1.setInteger("value", Integer.MAX_VALUE); + + // Check the length has changed accordingly (<Value length> + <type> + <Integer length>) + size = EncodingUtils.encodedShortStringLength("value") + 1 + EncodingUtils.encodedIntegerLength(); + Assert.assertEquals(size, table1.getEncodedSize()); + + // Check boolean value is null + Assert.assertEquals(null, table1.getBoolean("value")); + // ... and integer value is good + Assert.assertEquals((Integer) Integer.MAX_VALUE, table1.getInteger("value")); + } + + /** + * Set a boolean and check that we can only get it back as a boolean and a string + * Check that attempting to lookup a non existent value returns null + */ + public void testBoolean() + { + FieldTable table1 = new FieldTable(); + table1.setBoolean("value", true); + Assert.assertTrue(table1.propertyExists("value")); + + // Test Getting right value back + Assert.assertEquals((Boolean) true, table1.getBoolean("value")); + + // Check we don't get anything back for other gets + Assert.assertEquals(null, table1.getByte("value")); + Assert.assertEquals(null, table1.getByte("value")); + Assert.assertEquals(null, table1.getShort("value")); + Assert.assertEquals(null, table1.getCharacter("value")); + Assert.assertEquals(null, table1.getDouble("value")); + Assert.assertEquals(null, table1.getFloat("value")); + Assert.assertEquals(null, table1.getInteger("value")); + Assert.assertEquals(null, table1.getLong("value")); + Assert.assertEquals(null, table1.getBytes("value")); + + // except value as a string + Assert.assertEquals("true", table1.getString("value")); + + table1.remove("value"); + + // Table should now have zero length for encoding + checkEmpty(table1); + + // Looking up an invalid value returns null + Assert.assertEquals(null, table1.getBoolean("Rubbish")); + } + + /** + * Set a byte and check that we can only get it back as a byte and a string + * Check that attempting to lookup a non existent value returns null + */ + public void testByte() + { + FieldTable table1 = new FieldTable(); + table1.setByte("value", Byte.MAX_VALUE); + Assert.assertTrue(table1.propertyExists("value")); + + // Tets lookups we shouldn't get anything back for other gets + // we should get right value back for this type .... + Assert.assertEquals(null, table1.getBoolean("value")); + Assert.assertEquals(Byte.MAX_VALUE, (byte) table1.getByte("value")); + Assert.assertEquals(null, table1.getShort("value")); + Assert.assertEquals(null, table1.getCharacter("value")); + Assert.assertEquals(null, table1.getDouble("value")); + Assert.assertEquals(null, table1.getFloat("value")); + Assert.assertEquals(null, table1.getInteger("value")); + Assert.assertEquals(null, table1.getLong("value")); + Assert.assertEquals(null, table1.getBytes("value")); + + // ... and a the string value of it. + Assert.assertEquals("" + Byte.MAX_VALUE, table1.getString("value")); + + table1.remove("value"); + // Table should now have zero length for encoding + checkEmpty(table1); + + // Looking up an invalid value returns null + Assert.assertEquals(null, table1.getByte("Rubbish")); + } + + /** + * Set a short and check that we can only get it back as a short and a string + * Check that attempting to lookup a non existent value returns null + */ + public void testShort() + { + FieldTable table1 = new FieldTable(); + table1.setShort("value", Short.MAX_VALUE); + Assert.assertTrue(table1.propertyExists("value")); + + // Tets lookups we shouldn't get anything back for other gets + // we should get right value back for this type .... + Assert.assertEquals(null, table1.getBoolean("value")); + Assert.assertEquals(null, table1.getByte("value")); + Assert.assertEquals(Short.MAX_VALUE, (short) table1.getShort("value")); + Assert.assertEquals(null, table1.getCharacter("value")); + Assert.assertEquals(null, table1.getDouble("value")); + Assert.assertEquals(null, table1.getFloat("value")); + Assert.assertEquals(null, table1.getInteger("value")); + Assert.assertEquals(null, table1.getLong("value")); + Assert.assertEquals(null, table1.getBytes("value")); + + // ... and a the string value of it. + Assert.assertEquals("" + Short.MAX_VALUE, table1.getString("value")); + + table1.remove("value"); + // Table should now have zero length for encoding + checkEmpty(table1); + + // Looking up an invalid value returns null + Assert.assertEquals(null, table1.getShort("Rubbish")); + } + + /** + * Set a char and check that we can only get it back as a char + * Check that attempting to lookup a non existent value returns null + */ + public void testChar() + { + FieldTable table1 = new FieldTable(); + table1.setChar("value", 'c'); + Assert.assertTrue(table1.propertyExists("value")); + + // Tets lookups we shouldn't get anything back for other gets + // we should get right value back for this type .... + Assert.assertEquals(null, table1.getBoolean("value")); + Assert.assertEquals(null, table1.getByte("value")); + Assert.assertEquals(null, table1.getShort("value")); + Assert.assertEquals('c', (char) table1.getCharacter("value")); + Assert.assertEquals(null, table1.getDouble("value")); + Assert.assertEquals(null, table1.getFloat("value")); + Assert.assertEquals(null, table1.getInteger("value")); + Assert.assertEquals(null, table1.getLong("value")); + Assert.assertEquals(null, table1.getBytes("value")); + + // ... and a the string value of it. + Assert.assertEquals("c", table1.getString("value")); + + table1.remove("value"); + + // Table should now have zero length for encoding + checkEmpty(table1); + + // Looking up an invalid value returns null + Assert.assertEquals(null, table1.getCharacter("Rubbish")); + } + + /** + * Set a double and check that we can only get it back as a double + * Check that attempting to lookup a non existent value returns null + */ + public void testDouble() + { + FieldTable table1 = new FieldTable(); + table1.setDouble("value", Double.MAX_VALUE); + Assert.assertTrue(table1.propertyExists("value")); + + // Tets lookups we shouldn't get anything back for other gets + // we should get right value back for this type .... + Assert.assertEquals(null, table1.getBoolean("value")); + Assert.assertEquals(null, table1.getByte("value")); + Assert.assertEquals(null, table1.getShort("value")); + Assert.assertEquals(null, table1.getCharacter("value")); + Assert.assertEquals(Double.MAX_VALUE, (double) table1.getDouble("value")); + Assert.assertEquals(null, table1.getFloat("value")); + Assert.assertEquals(null, table1.getInteger("value")); + Assert.assertEquals(null, table1.getLong("value")); + Assert.assertEquals(null, table1.getBytes("value")); + + // ... and a the string value of it. + Assert.assertEquals("" + Double.MAX_VALUE, table1.getString("value")); + table1.remove("value"); + // but after a removeKey it doesn't + Assert.assertFalse(table1.containsKey("value")); + + // Table should now have zero length for encoding + checkEmpty(table1); + + // Looking up an invalid value returns null + Assert.assertEquals(null, table1.getDouble("Rubbish")); + } + + /** + * Set a float and check that we can only get it back as a float + * Check that attempting to lookup a non existent value returns null + */ + public void testFloat() + { + FieldTable table1 = new FieldTable(); + table1.setFloat("value", Float.MAX_VALUE); + Assert.assertTrue(table1.propertyExists("value")); + + // Tets lookups we shouldn't get anything back for other gets + // we should get right value back for this type .... + Assert.assertEquals(null, table1.getBoolean("value")); + Assert.assertEquals(null, table1.getByte("value")); + Assert.assertEquals(null, table1.getShort("value")); + Assert.assertEquals(null, table1.getCharacter("value")); + Assert.assertEquals(null, table1.getDouble("value")); + Assert.assertEquals(Float.MAX_VALUE, (float) table1.getFloat("value")); + Assert.assertEquals(null, table1.getInteger("value")); + Assert.assertEquals(null, table1.getLong("value")); + Assert.assertEquals(null, table1.getBytes("value")); + + // ... and a the string value of it. + Assert.assertEquals("" + Float.MAX_VALUE, table1.getString("value")); + + table1.remove("value"); + // but after a removeKey it doesn't + Assert.assertFalse(table1.containsKey("value")); + + // Table should now have zero length for encoding + checkEmpty(table1); + + // Looking up an invalid value returns null + Assert.assertEquals(null, table1.getFloat("Rubbish")); + } + + /** + * Set an int and check that we can only get it back as an int + * Check that attempting to lookup a non existent value returns null + */ + public void testInt() + { + FieldTable table1 = new FieldTable(); + table1.setInteger("value", Integer.MAX_VALUE); + Assert.assertTrue(table1.propertyExists("value")); + + // Tets lookups we shouldn't get anything back for other gets + // we should get right value back for this type .... + Assert.assertEquals(null, table1.getBoolean("value")); + Assert.assertEquals(null, table1.getByte("value")); + Assert.assertEquals(null, table1.getShort("value")); + Assert.assertEquals(null, table1.getCharacter("value")); + Assert.assertEquals(null, table1.getDouble("value")); + Assert.assertEquals(null, table1.getFloat("value")); + Assert.assertEquals(Integer.MAX_VALUE, (int) table1.getInteger("value")); + Assert.assertEquals(null, table1.getLong("value")); + Assert.assertEquals(null, table1.getBytes("value")); + + // ... and a the string value of it. + Assert.assertEquals("" + Integer.MAX_VALUE, table1.getString("value")); + + table1.remove("value"); + // but after a removeKey it doesn't + Assert.assertFalse(table1.containsKey("value")); + + // Table should now have zero length for encoding + checkEmpty(table1); + + // Looking up an invalid value returns null + Assert.assertEquals(null, table1.getInteger("Rubbish")); + } + + /** + * Set a long and check that we can only get it back as a long + * Check that attempting to lookup a non existent value returns null + */ + public void testLong() + { + FieldTable table1 = new FieldTable(); + table1.setLong("value", Long.MAX_VALUE); + Assert.assertTrue(table1.propertyExists("value")); + + // Tets lookups we shouldn't get anything back for other gets + // we should get right value back for this type .... + Assert.assertEquals(null, table1.getBoolean("value")); + Assert.assertEquals(null, table1.getByte("value")); + Assert.assertEquals(null, table1.getShort("value")); + Assert.assertEquals(null, table1.getCharacter("value")); + Assert.assertEquals(null, table1.getDouble("value")); + Assert.assertEquals(null, table1.getFloat("value")); + Assert.assertEquals(null, table1.getInteger("value")); + Assert.assertEquals(Long.MAX_VALUE, (long) table1.getLong("value")); + Assert.assertEquals(null, table1.getBytes("value")); + + // ... and a the string value of it. + Assert.assertEquals("" + Long.MAX_VALUE, table1.getString("value")); + + table1.remove("value"); + // but after a removeKey it doesn't + Assert.assertFalse(table1.containsKey("value")); + + // Table should now have zero length for encoding + checkEmpty(table1); + + // Looking up an invalid value returns null + Assert.assertEquals(null, table1.getLong("Rubbish")); + } + + /** + * Set a double and check that we can only get it back as a double + * Check that attempting to lookup a non existent value returns null + */ + public void testBytes() + { + byte[] bytes = { 99, 98, 97, 96, 95 }; + + FieldTable table1 = new FieldTable(); + table1.setBytes("value", bytes); + Assert.assertTrue(table1.propertyExists("value")); + + // Tets lookups we shouldn't get anything back for other gets + // we should get right value back for this type .... + Assert.assertEquals(null, table1.getBoolean("value")); + Assert.assertEquals(null, table1.getByte("value")); + Assert.assertEquals(null, table1.getShort("value")); + Assert.assertEquals(null, table1.getCharacter("value")); + Assert.assertEquals(null, table1.getDouble("value")); + Assert.assertEquals(null, table1.getFloat("value")); + Assert.assertEquals(null, table1.getInteger("value")); + Assert.assertEquals(null, table1.getLong("value")); + assertBytesEqual(bytes, table1.getBytes("value")); + + // ... and a the string value of it is null + Assert.assertEquals(null, table1.getString("value")); + + table1.remove("value"); + // but after a removeKey it doesn't + Assert.assertFalse(table1.containsKey("value")); + + // Table should now have zero length for encoding + checkEmpty(table1); + + // Looking up an invalid value returns null + Assert.assertEquals(null, table1.getBytes("Rubbish")); + } + + /** + * Calls all methods that can be used to check the table is empty + * - getEncodedSize + * - isEmpty + * - length + * + * @param table to check is empty + */ + private void checkEmpty(FieldTable table) + { + Assert.assertEquals(0, table.getEncodedSize()); + Assert.assertTrue(table.isEmpty()); + Assert.assertEquals(0, table.size()); + + Assert.assertEquals(0, table.keySet().size()); + } + + /** + * Set a String and check that we can only get it back as a String + * Check that attempting to lookup a non existent value returns null + */ + public void testString() + { + FieldTable table1 = new FieldTable(); + table1.setString("value", "Hello"); + Assert.assertTrue(table1.propertyExists("value")); + + // Tets lookups we shouldn't get anything back for other gets + // we should get right value back for this type .... + Assert.assertEquals(null, table1.getBoolean("value")); + Assert.assertEquals(null, table1.getByte("value")); + Assert.assertEquals(null, table1.getShort("value")); + Assert.assertEquals(null, table1.getCharacter("value")); + Assert.assertEquals(null, table1.getDouble("value")); + Assert.assertEquals(null, table1.getFloat("value")); + Assert.assertEquals(null, table1.getInteger("value")); + Assert.assertEquals(null, table1.getLong("value")); + Assert.assertEquals(null, table1.getBytes("value")); + Assert.assertEquals("Hello", table1.getString("value")); + + // Try setting a null value and read it back + table1.setString("value", null); + + Assert.assertEquals(null, table1.getString("value")); + + // but still contains the value + Assert.assertTrue(table1.containsKey("value")); + + table1.remove("value"); + // but after a removeKey it doesn't + Assert.assertFalse(table1.containsKey("value")); + + checkEmpty(table1); + + // Looking up an invalid value returns null + Assert.assertEquals(null, table1.getString("Rubbish")); + + // Additional Test that haven't been covered for string + table1.setObject("value", "Hello"); + // Check that it was set correctly + Assert.assertEquals("Hello", table1.getString("value")); + } + + public void testValues() + { + FieldTable table = new FieldTable(); + table.setBoolean("bool", true); + table.setByte("byte", Byte.MAX_VALUE); + byte[] bytes = { 99, 98, 97, 96, 95 }; + table.setBytes("bytes", bytes); + table.setChar("char", 'c'); + table.setDouble("double", Double.MAX_VALUE); + table.setFloat("float", Float.MAX_VALUE); + table.setInteger("int", Integer.MAX_VALUE); + table.setLong("long", Long.MAX_VALUE); + table.setShort("short", Short.MAX_VALUE); + table.setString("string", "Hello"); + table.setString("null-string", null); + + table.setObject("object-bool", true); + table.setObject("object-byte", Byte.MAX_VALUE); + table.setObject("object-bytes", bytes); + table.setObject("object-char", 'c'); + table.setObject("object-double", Double.MAX_VALUE); + table.setObject("object-float", Float.MAX_VALUE); + table.setObject("object-int", Integer.MAX_VALUE); + table.setObject("object-long", Long.MAX_VALUE); + table.setObject("object-short", Short.MAX_VALUE); + table.setObject("object-string", "Hello"); + + Assert.assertEquals((Boolean) true, table.getBoolean("bool")); + Assert.assertEquals((Byte) Byte.MAX_VALUE, table.getByte("byte")); + assertBytesEqual(bytes, table.getBytes("bytes")); + Assert.assertEquals((Character) 'c', table.getCharacter("char")); + Assert.assertEquals(Double.MAX_VALUE, table.getDouble("double")); + Assert.assertEquals(Float.MAX_VALUE, table.getFloat("float")); + Assert.assertEquals((Integer) Integer.MAX_VALUE, table.getInteger("int")); + Assert.assertEquals((Long) Long.MAX_VALUE, table.getLong("long")); + Assert.assertEquals((Short) Short.MAX_VALUE, table.getShort("short")); + Assert.assertEquals("Hello", table.getString("string")); + Assert.assertEquals(null, table.getString("null-string")); + + Assert.assertEquals(true, table.getObject("object-bool")); + Assert.assertEquals(Byte.MAX_VALUE, table.getObject("object-byte")); + assertBytesEqual(bytes, (byte[]) table.getObject("object-bytes")); + Assert.assertEquals('c', table.getObject("object-char")); + Assert.assertEquals(Double.MAX_VALUE, table.getObject("object-double")); + Assert.assertEquals(Float.MAX_VALUE, table.getObject("object-float")); + Assert.assertEquals(Integer.MAX_VALUE, table.getObject("object-int")); + Assert.assertEquals(Long.MAX_VALUE, table.getObject("object-long")); + Assert.assertEquals(Short.MAX_VALUE, table.getObject("object-short")); + Assert.assertEquals("Hello", table.getObject("object-string")); + } + + public void testwriteBuffer() + { + byte[] bytes = { 99, 98, 97, 96, 95 }; + + FieldTable table = new FieldTable(); + table.setBoolean("bool", true); + table.setByte("byte", Byte.MAX_VALUE); + + table.setBytes("bytes", bytes); + table.setChar("char", 'c'); + table.setDouble("double", Double.MAX_VALUE); + table.setFloat("float", Float.MAX_VALUE); + table.setInteger("int", Integer.MAX_VALUE); + table.setLong("long", Long.MAX_VALUE); + table.setShort("short", Short.MAX_VALUE); + table.setString("string", "hello"); + table.setString("null-string", null); + + final ByteBuffer buffer = ByteBuffer.allocate((int) table.getEncodedSize() + 4); // FIXME XXX: Is cast a problem? + + table.writeToBuffer(buffer); + + buffer.flip(); + + long length = buffer.getUnsignedInt(); + + try + { + FieldTable table2 = new FieldTable(buffer, length); + + Assert.assertEquals((Boolean) true, table2.getBoolean("bool")); + Assert.assertEquals((Byte) Byte.MAX_VALUE, table2.getByte("byte")); + assertBytesEqual(bytes, table2.getBytes("bytes")); + Assert.assertEquals((Character) 'c', table2.getCharacter("char")); + Assert.assertEquals(Double.MAX_VALUE, table2.getDouble("double")); + Assert.assertEquals(Float.MAX_VALUE, table2.getFloat("float")); + Assert.assertEquals((Integer) Integer.MAX_VALUE, table2.getInteger("int")); + Assert.assertEquals((Long) Long.MAX_VALUE, table2.getLong("long")); + Assert.assertEquals((Short) Short.MAX_VALUE, table2.getShort("short")); + Assert.assertEquals("hello", table2.getString("string")); + Assert.assertEquals(null, table2.getString("null-string")); + + } + catch (AMQFrameDecodingException e) + { + e.printStackTrace(); + fail("PFT should be instantiated from bytes." + e.getCause()); + } + } + + public void testEncodingSize() + { + FieldTable result = new FieldTable(); + int size = 0; + + result.setBoolean("boolean", true); + size += 1 + EncodingUtils.encodedShortStringLength("boolean") + EncodingUtils.encodedBooleanLength(); + Assert.assertEquals(size, result.getEncodedSize()); + + result.setByte("byte", (byte) Byte.MAX_VALUE); + size += 1 + EncodingUtils.encodedShortStringLength("byte") + EncodingUtils.encodedByteLength(); + Assert.assertEquals(size, result.getEncodedSize()); + + byte[] _bytes = { 99, 98, 97, 96, 95 }; + + result.setBytes("bytes", _bytes); + size += 1 + EncodingUtils.encodedShortStringLength("bytes") + 4 + _bytes.length; + Assert.assertEquals(size, result.getEncodedSize()); + + result.setChar("char", (char) 'c'); + size += 1 + EncodingUtils.encodedShortStringLength("char") + EncodingUtils.encodedCharLength(); + Assert.assertEquals(size, result.getEncodedSize()); + + result.setDouble("double", (double) Double.MAX_VALUE); + size += 1 + EncodingUtils.encodedShortStringLength("double") + EncodingUtils.encodedDoubleLength(); + Assert.assertEquals(size, result.getEncodedSize()); + + result.setFloat("float", (float) Float.MAX_VALUE); + size += 1 + EncodingUtils.encodedShortStringLength("float") + EncodingUtils.encodedFloatLength(); + Assert.assertEquals(size, result.getEncodedSize()); + + result.setInteger("int", (int) Integer.MAX_VALUE); + size += 1 + EncodingUtils.encodedShortStringLength("int") + EncodingUtils.encodedIntegerLength(); + Assert.assertEquals(size, result.getEncodedSize()); + + result.setLong("long", (long) Long.MAX_VALUE); + size += 1 + EncodingUtils.encodedShortStringLength("long") + EncodingUtils.encodedLongLength(); + Assert.assertEquals(size, result.getEncodedSize()); + + result.setShort("short", (short) Short.MAX_VALUE); + size += 1 + EncodingUtils.encodedShortStringLength("short") + EncodingUtils.encodedShortLength(); + Assert.assertEquals(size, result.getEncodedSize()); + + result.setString("result", "Hello"); + size += 1 + EncodingUtils.encodedShortStringLength("result") + EncodingUtils.encodedLongStringLength("Hello"); + Assert.assertEquals(size, result.getEncodedSize()); + + result.setObject("object-bool", true); + size += 1 + EncodingUtils.encodedShortStringLength("object-bool") + EncodingUtils.encodedBooleanLength(); + Assert.assertEquals(size, result.getEncodedSize()); + + result.setObject("object-byte", Byte.MAX_VALUE); + size += 1 + EncodingUtils.encodedShortStringLength("object-byte") + EncodingUtils.encodedByteLength(); + Assert.assertEquals(size, result.getEncodedSize()); + + result.setObject("object-bytes", _bytes); + size += 1 + EncodingUtils.encodedShortStringLength("object-bytes") + 4 + _bytes.length; + Assert.assertEquals(size, result.getEncodedSize()); + + result.setObject("object-char", 'c'); + size += 1 + EncodingUtils.encodedShortStringLength("object-char") + EncodingUtils.encodedCharLength(); + Assert.assertEquals(size, result.getEncodedSize()); + + result.setObject("object-double", Double.MAX_VALUE); + size += 1 + EncodingUtils.encodedShortStringLength("object-double") + EncodingUtils.encodedDoubleLength(); + Assert.assertEquals(size, result.getEncodedSize()); + + result.setObject("object-float", Float.MAX_VALUE); + size += 1 + EncodingUtils.encodedShortStringLength("object-float") + EncodingUtils.encodedFloatLength(); + Assert.assertEquals(size, result.getEncodedSize()); + + result.setObject("object-int", Integer.MAX_VALUE); + size += 1 + EncodingUtils.encodedShortStringLength("object-int") + EncodingUtils.encodedIntegerLength(); + Assert.assertEquals(size, result.getEncodedSize()); + + result.setObject("object-long", Long.MAX_VALUE); + size += 1 + EncodingUtils.encodedShortStringLength("object-long") + EncodingUtils.encodedLongLength(); + Assert.assertEquals(size, result.getEncodedSize()); + + result.setObject("object-short", Short.MAX_VALUE); + size += 1 + EncodingUtils.encodedShortStringLength("object-short") + EncodingUtils.encodedShortLength(); + Assert.assertEquals(size, result.getEncodedSize()); + + } + + // public void testEncodingSize1() + // { + // PropertyFieldTable table = new PropertyFieldTable(); + // int length = 0; + // result.put("one", 1L); + // length = EncodingUtils.encodedShortStringLength("one"); + // length += 1 + EncodingUtils.encodedLongLength(); + // assertEquals(length, result.getEncodedSize()); + // + // result.put("two", 2L); + // length += EncodingUtils.encodedShortStringLength("two"); + // length += 1 + EncodingUtils.encodedLongLength(); + // assertEquals(length, result.getEncodedSize()); + // + // result.put("three", 3L); + // length += EncodingUtils.encodedShortStringLength("three"); + // length += 1 + EncodingUtils.encodedLongLength(); + // assertEquals(length, result.getEncodedSize()); + // + // result.put("four", 4L); + // length += EncodingUtils.encodedShortStringLength("four"); + // length += 1 + EncodingUtils.encodedLongLength(); + // assertEquals(length, result.getEncodedSize()); + // + // result.put("five", 5L); + // length += EncodingUtils.encodedShortStringLength("five"); + // length += 1 + EncodingUtils.encodedLongLength(); + // assertEquals(length, result.getEncodedSize()); + // + // //fixme should perhaps be expanded to incorporate all types. + // + // final ByteBuffer buffer = ByteBuffer.allocate((int) result.getEncodedSize()); // FIXME XXX: Is cast a problem? + // + // result.writeToBuffer(buffer); + // + // buffer.flip(); + // + // long length = buffer.getUnsignedInt(); + // + // try + // { + // PropertyFieldTable table2 = new PropertyFieldTable(buffer, length); + // + // Assert.assertEquals((Long) 1L, table2.getLong("one")); + // Assert.assertEquals((Long) 2L, table2.getLong("two")); + // Assert.assertEquals((Long) 3L, table2.getLong("three")); + // Assert.assertEquals((Long) 4L, table2.getLong("four")); + // Assert.assertEquals((Long) 5L, table2.getLong("five")); + // } + // catch (AMQFrameDecodingException e) + // { + // e.printStackTrace(); + // fail("PFT should be instantiated from bytes." + e.getCause()); + // } + // + // } + + /** + * Additional test for setObject + */ + public void testSetObject() + { + FieldTable table = new FieldTable(); + + // Try setting a non primative object + + try + { + table.setObject("value", this); + fail("Only primative values allowed in setObject"); + } + catch (AMQPInvalidClassException iae) + { + // normal path + } + // so length should be zero + Assert.assertEquals(0, table.getEncodedSize()); + } + + /** + * Additional test checkPropertyName doesn't accept Null + */ + public void testCheckPropertyNameasNull() + { + FieldTable table = new FieldTable(); + + try + { + table.setObject((String) null, "String"); + fail("Null property name is not allowed"); + } + catch (IllegalArgumentException iae) + { + // normal path + } + // so length should be zero + Assert.assertEquals(0, table.getEncodedSize()); + } + + /** + * Additional test checkPropertyName doesn't accept an empty String + */ + public void testCheckPropertyNameasEmptyString() + { + FieldTable table = new FieldTable(); + + try + { + table.setObject("", "String"); + fail("empty property name is not allowed"); + } + catch (IllegalArgumentException iae) + { + // normal path + } + // so length should be zero + Assert.assertEquals(0, table.getEncodedSize()); + } + + /** + * Additional test checkPropertyName doesn't accept an empty String + */ + public void testCheckPropertyNamehasMaxLength() + { + String oldVal = System.getProperty("STRICT_AMQP"); + System.setProperty("STRICT_AMQP", "true"); + FieldTable table = new FieldTable(); + + StringBuffer longPropertyName = new StringBuffer(129); + + for (int i = 0; i < 129; i++) + { + longPropertyName.append("x"); + } + + try + { + table.setObject(longPropertyName.toString(), "String"); + fail("property name must be < 128 characters"); + } + catch (IllegalArgumentException iae) + { + // normal path + } + // so length should be zero + Assert.assertEquals(0, table.getEncodedSize()); + if (oldVal != null) + { + System.setProperty("STRICT_AMQP", oldVal); + } + else + { + System.clearProperty("STRICT_AMQP"); + } + } + + /** + * Additional test checkPropertyName starts with a letter + */ + public void testCheckPropertyNameStartCharacterIsLetter() + { + String oldVal = System.getProperty("STRICT_AMQP"); + System.setProperty("STRICT_AMQP", "true"); + FieldTable table = new FieldTable(); + + // Try a name that starts with a number + try + { + table.setObject("1", "String"); + fail("property name must start with a letter"); + } + catch (IllegalArgumentException iae) + { + // normal path + } + // so length should be zero + Assert.assertEquals(0, table.getEncodedSize()); + if (oldVal != null) + { + System.setProperty("STRICT_AMQP", oldVal); + } + else + { + System.clearProperty("STRICT_AMQP"); + } + } + + /** + * Additional test checkPropertyName starts with a hash or a dollar + */ + public void testCheckPropertyNameStartCharacterIsHashorDollar() + { + String oldVal = System.getProperty("STRICT_AMQP"); + System.setProperty("STRICT_AMQP", "true"); + FieldTable table = new FieldTable(); + + // Try a name that starts with a number + try + { + table.setObject("#", "String"); + table.setObject("$", "String"); + } + catch (IllegalArgumentException iae) + { + fail("property name are allowed to start with # and $s"); + } + + if (oldVal != null) + { + System.setProperty("STRICT_AMQP", oldVal); + } + else + { + System.clearProperty("STRICT_AMQP"); + } + } + + /** + * Additional test to test the contents of the table + */ + public void testContents() + { + FieldTable table = new FieldTable(); + + table.setObject("StringProperty", "String"); + + Assert.assertEquals("String", table.getString("StringProperty")); + + // Test Clear + + table.clear(); + + checkEmpty(table); + } + + /** + * Test the contents of the sets + */ + public void testSets() + { + + FieldTable table = new FieldTable(); + + table.setObject("n1", "1"); + table.setObject("n2", "2"); + table.setObject("n3", "3"); + + Assert.assertEquals("1", table.getObject("n1")); + Assert.assertEquals("2", table.getObject("n2")); + Assert.assertEquals("3", table.getObject("n3")); + + } + + private void assertBytesEqual(byte[] expected, byte[] actual) + { + Assert.assertEquals(expected.length, actual.length); + + for (int index = 0; index < expected.length; index++) + { + Assert.assertEquals(expected[index], actual[index]); + } + } + + private void assertBytesNotEqual(byte[] expected, byte[] actual) + { + Assert.assertEquals(expected.length, actual.length); + + for (int index = 0; index < expected.length; index++) + { + Assert.assertFalse(expected[index] == actual[index]); + } + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(PropertyFieldTableTest.class); + } + +} diff --git a/Final/java/common/src/test/java/org/apache/qpid/pool/PoolingFilterTest.java b/Final/java/common/src/test/java/org/apache/qpid/pool/PoolingFilterTest.java new file mode 100644 index 0000000000..6383d52298 --- /dev/null +++ b/Final/java/common/src/test/java/org/apache/qpid/pool/PoolingFilterTest.java @@ -0,0 +1,111 @@ +/* + * 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.qpid.pool; + +import junit.framework.TestCase; +import junit.framework.Assert; +import org.apache.qpid.session.TestSession; +import org.apache.mina.common.IoFilter; +import org.apache.mina.common.IoSession; +import org.apache.mina.common.IdleStatus; + +import java.util.concurrent.RejectedExecutionException; + +public class PoolingFilterTest extends TestCase +{ + private PoolingFilter _pool; + ReferenceCountingExecutorService _executorService; + + public void setUp() + { + + //Create Pool + _executorService = ReferenceCountingExecutorService.getInstance(); + _executorService.acquireExecutorService(); + _pool = PoolingFilter.createAynschWritePoolingFilter(_executorService, + "AsynchronousWriteFilter"); + + } + + public void testRejectedExecution() throws Exception + { + + TestSession testSession = new TestSession(); + _pool.createNewJobForSession(testSession); + _pool.filterWrite(new NoOpFilter(), testSession, new IoFilter.WriteRequest("Message")); + + //Shutdown the pool + _executorService.getPool().shutdownNow(); + + try + { + + testSession = new TestSession(); + _pool.createNewJobForSession(testSession); + //prior to fix for QPID-172 this would throw RejectedExecutionException + _pool.filterWrite(null, testSession, null); + } + catch (RejectedExecutionException rje) + { + Assert.fail("RejectedExecutionException should not occur after pool has shutdown:" + rje); + } + } + + private static class NoOpFilter implements IoFilter.NextFilter + { + + public void sessionOpened(IoSession session) + { + } + + public void sessionClosed(IoSession session) + { + } + + public void sessionIdle(IoSession session, IdleStatus status) + { + } + + public void exceptionCaught(IoSession session, Throwable cause) + { + } + + public void messageReceived(IoSession session, Object message) + { + } + + public void messageSent(IoSession session, Object message) + { + } + + public void filterWrite(IoSession session, IoFilter.WriteRequest writeRequest) + { + } + + public void filterClose(IoSession session) + { + } + + public void sessionCreated(IoSession session) + { + } + } +} diff --git a/Final/java/common/src/test/java/org/apache/qpid/session/TestSession.java b/Final/java/common/src/test/java/org/apache/qpid/session/TestSession.java new file mode 100644 index 0000000000..aafc91b03b --- /dev/null +++ b/Final/java/common/src/test/java/org/apache/qpid/session/TestSession.java @@ -0,0 +1,277 @@ +/* + * + * 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.qpid.session; + +import org.apache.mina.common.*; + +import java.net.SocketAddress; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentHashMap; + +public class TestSession implements IoSession +{ + private final ConcurrentMap attributes = new ConcurrentHashMap(); + + public TestSession() + { + } + + public IoService getService() + { + return null; //TODO + } + + public IoServiceConfig getServiceConfig() + { + return null; //TODO + } + + public IoHandler getHandler() + { + return null; //TODO + } + + public IoSessionConfig getConfig() + { + return null; //TODO + } + + public IoFilterChain getFilterChain() + { + return null; //TODO + } + + public WriteFuture write(Object message) + { + return null; //TODO + } + + public CloseFuture close() + { + return null; //TODO + } + + public Object getAttachment() + { + return getAttribute(""); + } + + public Object setAttachment(Object attachment) + { + return setAttribute("",attachment); + } + + public Object getAttribute(String key) + { + return attributes.get(key); + } + + public Object setAttribute(String key, Object value) + { + return attributes.put(key,value); + } + + public Object setAttribute(String key) + { + return attributes.put(key, Boolean.TRUE); + } + + public Object removeAttribute(String key) + { + return attributes.remove(key); + } + + public boolean containsAttribute(String key) + { + return attributes.containsKey(key); + } + + public Set getAttributeKeys() + { + return attributes.keySet(); + } + + public TransportType getTransportType() + { + return null; //TODO + } + + public boolean isConnected() + { + return false; //TODO + } + + public boolean isClosing() + { + return false; //TODO + } + + public CloseFuture getCloseFuture() + { + return null; //TODO + } + + public SocketAddress getRemoteAddress() + { + return null; //TODO + } + + public SocketAddress getLocalAddress() + { + return null; //TODO + } + + public SocketAddress getServiceAddress() + { + return null; //TODO + } + + public int getIdleTime(IdleStatus status) + { + return 0; //TODO + } + + public long getIdleTimeInMillis(IdleStatus status) + { + return 0; //TODO + } + + public void setIdleTime(IdleStatus status, int idleTime) + { + //TODO + } + + public int getWriteTimeout() + { + return 0; //TODO + } + + public long getWriteTimeoutInMillis() + { + return 0; //TODO + } + + public void setWriteTimeout(int writeTimeout) + { + //TODO + } + + public TrafficMask getTrafficMask() + { + return null; //TODO + } + + public void setTrafficMask(TrafficMask trafficMask) + { + //TODO + } + + public void suspendRead() + { + //TODO + } + + public void suspendWrite() + { + //TODO + } + + public void resumeRead() + { + //TODO + } + + public void resumeWrite() + { + //TODO + } + + public long getReadBytes() + { + return 0; //TODO + } + + public long getWrittenBytes() + { + return 0; //TODO + } + + public long getReadMessages() + { + return 0; + } + + public long getWrittenMessages() + { + return 0; + } + + public long getWrittenWriteRequests() + { + return 0; //TODO + } + + public int getScheduledWriteRequests() + { + return 0; //TODO + } + + public int getScheduledWriteBytes() + { + return 0; //TODO + } + + public long getCreationTime() + { + return 0; //TODO + } + + public long getLastIoTime() + { + return 0; //TODO + } + + public long getLastReadTime() + { + return 0; //TODO + } + + public long getLastWriteTime() + { + return 0; //TODO + } + + public boolean isIdle(IdleStatus status) + { + return false; //TODO + } + + public int getIdleCount(IdleStatus status) + { + return 0; //TODO + } + + public long getLastIdleTime(IdleStatus status) + { + return 0; //TODO + } +} diff --git a/Final/java/common/src/test/java/org/apache/qpid/util/CommandLineParserTest.java b/Final/java/common/src/test/java/org/apache/qpid/util/CommandLineParserTest.java new file mode 100644 index 0000000000..66fb3252df --- /dev/null +++ b/Final/java/common/src/test/java/org/apache/qpid/util/CommandLineParserTest.java @@ -0,0 +1,554 @@ +/*
+ *
+ * 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.qpid.util;
+
+import junit.framework.*;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Properties;
+
+/**
+ * Unit tests the {@link CommandLineParser} class.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Check that parsing a single flag works ok.
+ * <tr><td> Check that parsing multiple flags condensed together works ok.
+ * <tr><td> Check that parsing an option with a space between it and its argument works ok.
+ * <tr><td> Check that parsing an option with no space between it and its argument works ok.
+ * <tr><td> Check that parsing an option with specific argument format works ok.
+ * <tr><td> Check that parsing an option with specific argument format fails on bad argument.
+ * <tr><td> Check that parsing a flag condensed together with an option fails.
+ * <tr><td> Check that parsing a free argument works ok.
+ * <tr><td> Check that parsing a free argument with specific format works ok.
+ * <tr><td> Check that parsing a free argument with specific format fails on bad argument.
+ * <tr><td> Check that parsing a mandatory option works ok.
+ * <tr><td> Check that parsing a mandatory free argument works ok.
+ * <tr><td> Check that parsing a mandatory option fails when no option is set.
+ * <tr><td> Check that parsing a mandatory free argument fails when no argument is specified.
+ * <tr><td> Check that parsing an unknown option works when unknowns not errors.
+ * <tr><td> Check that parsing an unknown flag fails when unknowns are to be reported as errors.
+ * <tr><td> Check that parsing an unknown option fails when unknowns are to be reported as errors.
+ * <tr><td> Check that get errors returns a string on errors.
+ * <tr><td> Check that get errors returns an empty string on no errors.
+ * <tr><td> Check that get usage returns a string.
+ * <tr><td> Check that get options in force returns an empty string before parsing.
+ * <tr><td> Check that get options in force return a non-empty string after parsing.
+ * </table>
+ */
+public class CommandLineParserTest extends TestCase
+{
+ private static final Logger log = LoggerFactory.getLogger(CommandLineParserTest.class);
+
+ public CommandLineParserTest(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Compile all the tests for the default test implementation of a traversable state into a test suite.
+ */
+ public static Test suite()
+ {
+ // Build a new test suite
+ TestSuite suite = new TestSuite("CommandLineParser Tests");
+
+ // Add all the tests defined in this class (using the default constructor)
+ suite.addTestSuite(CommandLineParserTest.class);
+
+ return suite;
+ }
+
+ /** Check that get errors returns an empty string on no errors. */
+ public void testGetErrorsReturnsEmptyStringOnNoErrors() throws Exception
+ {
+ // Create a command line parser for some flags and options.
+ CommandLineParser parser =
+ new CommandLineParser(
+ new String[][]
+ {
+ { "t1", "Test Flag 1." },
+ { "t2", "Test Option 2.", "test" },
+ { "t3", "Test Option 3.", "test", "true" },
+ { "t4", "Test Option 4.", "test", null, "^test$" }
+ });
+
+ // Do some legal parsing.
+ parser.parseCommandLine(new String[] { "-t1", "-t2test", "-t3test", "-t4test" });
+
+ // Check that the get errors message returns an empty string.
+ assertTrue("The errors method did not return an empty string.", "".equals(parser.getErrors()));
+ }
+
+ /** Check that get errors returns a string on errors. */
+ public void testGetErrorsReturnsStringOnErrors() throws Exception
+ {
+ // Create a command line parser for some flags and options.
+ CommandLineParser parser =
+ new CommandLineParser(
+ new String[][]
+ {
+ { "t1", "Test Flag 1." },
+ { "t2", "Test Option 2.", "test" },
+ { "t3", "Test Option 3.", "test", "true" },
+ { "t4", "Test Option 4.", "test", null, "^test$" }
+ });
+
+ try
+ {
+ // Do some illegal parsing.
+ parser.parseCommandLine(new String[] { "-t1", "-t1t2test", "-t4fail" });
+ }
+ catch (IllegalArgumentException e)
+ { }
+
+ // Check that the get errors message returns a string.
+ assertTrue("The errors method returned an empty string.",
+ !((parser.getErrors() == null) || "".equals(parser.getErrors())));
+
+ }
+
+ /** Check that get options in force returns an empty string before parsing. */
+ public void testGetOptionsInForceReturnsEmptyStringBeforeParsing() throws Exception
+ {
+ // Create a command line parser for some flags and options.
+ CommandLineParser parser =
+ new CommandLineParser(
+ new String[][]
+ {
+ { "t1", "Test Flag 1." },
+ { "t2", "Test Option 2.", "test" },
+ { "t3", "Test Option 3.", "test", "true" },
+ { "t4", "Test Option 4.", "test", null, "^test$" }
+ });
+
+ // Check that the options in force method returns an empty string.
+ assertTrue("The options in force method did not return an empty string.", "".equals(parser.getOptionsInForce()));
+ }
+
+ /** Check that get options in force return a non-empty string after parsing. */
+ public void testGetOptionsInForceReturnsNonEmptyStringAfterParsing() throws Exception
+ {
+ // Create a command line parser for some flags and options.
+ CommandLineParser parser =
+ new CommandLineParser(
+ new String[][]
+ {
+ { "t1", "Test Flag 1." },
+ { "t2", "Test Option 2.", "test" },
+ { "t3", "Test Option 3.", "test", "true" },
+ { "t4", "Test Option 4.", "test", null, "^test$" }
+ });
+
+ // Do some parsing.
+ parser.parseCommandLine(new String[] { "-t1", "-t2test", "-t3test", "-t4test" });
+
+ // Check that the options in force method returns a string.
+ assertTrue("The options in force method did not return a non empty string.",
+ !((parser.getOptionsInForce() == null) || "".equals(parser.getOptionsInForce())));
+ }
+
+ /** Check that get usage returns a string. */
+ public void testGetUsageReturnsString() throws Exception
+ {
+ // Create a command line parser for some flags and options.
+ CommandLineParser parser =
+ new CommandLineParser(
+ new String[][]
+ {
+ { "t1", "Test Flag 1." },
+ { "t2", "Test Option 2.", "test" },
+ { "t3", "Test Option 3.", "test", "true" },
+ { "t4", "Test Option 4.", "test", null, "^test$" }
+ });
+
+ // Check that the usage method returns a string.
+ assertTrue("The usage method did not return a non empty string.",
+ !((parser.getUsage() == null) || "".equals(parser.getUsage())));
+ }
+
+ /** Check that parsing multiple flags condensed together works ok. */
+ public void testParseCondensedFlagsOk() throws Exception
+ {
+ // Create a command line parser for multiple flags.
+ CommandLineParser parser =
+ new CommandLineParser(
+ new String[][]
+ {
+ { "t1", "Test Flag 1." },
+ { "t2", "Test Flag 2." },
+ { "t3", "Test Flag 3." }
+ });
+
+ // Parse a command line with the flags set and condensed together.
+ Properties testProps = parser.parseCommandLine(new String[] { "-t1t2t3" });
+
+ // Check that the flags were set in the parsed properties.
+ assertTrue("The t1 flag was not \"true\", it was: " + testProps.get("t1"), "true".equals(testProps.get("t1")));
+ assertTrue("The t2 flag was not \"true\", it was: " + testProps.get("t2"), "true".equals(testProps.get("t2")));
+ assertTrue("The t3 flag was not \"true\", it was: " + testProps.get("t3"), "true".equals(testProps.get("t3")));
+ }
+
+ /** Check that parsing a flag condensed together with an option fails. */
+ public void testParseFlagCondensedWithOptionFails() throws Exception
+ {
+ // Create a command line parser for a flag and an option.
+ CommandLineParser parser =
+ new CommandLineParser(new String[][]
+ {
+ { "t1", "Test Flag 1." },
+ { "t2", "Test Option 2.", "test" }
+ });
+
+ // Check that the parser reports an error.
+ boolean testPassed = false;
+
+ try
+ {
+ // Parse a command line with the flag and option condensed together.
+ Properties testProps = parser.parseCommandLine(new String[] { "-t1t2" });
+ }
+ catch (IllegalArgumentException e)
+ {
+ testPassed = true;
+ }
+
+ assertTrue("IllegalArgumentException not thrown when a flag and option are condensed together.", testPassed);
+ }
+
+ /** Check that parsing a free argument with specific format fails on bad argument. */
+ public void testParseFormattedFreeArgumentFailsBadArgument() throws Exception
+ {
+ // Create a command line parser for a formatted free argument.
+ CommandLineParser parser =
+ new CommandLineParser(new String[][]
+ {
+ { "1", "Test Free Argument.", "test", null, "^test$" }
+ });
+
+ // Check that the parser signals an error for a badly formatted argument.
+ boolean testPassed = false;
+
+ try
+ {
+ // Parse a command line with this option set incorrectly.
+ Properties testProps = parser.parseCommandLine(new String[] { "fail" });
+ }
+ catch (IllegalArgumentException e)
+ {
+ testPassed = true;
+ }
+
+ assertTrue("IllegalArgumentException not thrown when a badly formatted argument was set.", testPassed);
+ }
+
+ /** Check that parsing a free argument with specific format works ok. */
+ public void testParseFormattedFreeArgumentOk() throws Exception
+ {
+ // Create a command line parser for a formatted free argument.
+ CommandLineParser parser =
+ new CommandLineParser(new String[][]
+ {
+ { "1", "Test Free Argument.", "test", null, "^test$" }
+ });
+
+ // Parse a command line with this argument set correctly.
+ Properties testProps = parser.parseCommandLine(new String[] { "test" });
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("The first free argument was not equal to \"test\" but was: " + testProps.get("1"),
+ "test".equals(testProps.get("1")));
+ }
+
+ /** Check that parsing an option with specific argument format fails on bad argument. */
+ public void testParseFormattedOptionArgumentFailsBadArgument() throws Exception
+ {
+ // Create a command line parser for a formatted option.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "t", "Test Option.", "test", null, "^test$" }
+ });
+
+ // Check that the parser signals an error for a badly formatted argument.
+ boolean testPassed = false;
+
+ try
+ {
+ // Parse a command line with this option set incorrectly.
+ Properties testProps = parser.parseCommandLine(new String[] { "-t", "fail" });
+ }
+ catch (IllegalArgumentException e)
+ {
+ testPassed = true;
+ }
+
+ assertTrue("IllegalArgumentException not thrown when a badly formatted argument was set.", testPassed);
+ }
+
+ /** Check that parsing an option with specific argument format works ok. */
+ public void testParseFormattedOptionArgumentOk() throws Exception
+ {
+ // Create a command line parser for a formatted option.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "t", "Test Option.", "test", null, "^test$" }
+ });
+
+ // Parse a command line with this option set correctly.
+ Properties testProps = parser.parseCommandLine(new String[] { "-t", "test" });
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"),
+ "test".equals(testProps.get("t")));
+ }
+
+ /** Check that parsing a free argument works ok. */
+ public void testParseFreeArgumentOk() throws Exception
+ {
+ // Create a command line parser for a free argument.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "1", "Test Free Argument.", "test" }
+ });
+
+ // Parse a command line with this argument set.
+ Properties testProps = parser.parseCommandLine(new String[] { "test" });
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("The first free argument was not equal to \"test\" but was: " + testProps.get("1"),
+ "test".equals(testProps.get("1")));
+ }
+
+ /** Check that parsing a mandatory option works ok. */
+ public void testParseMandatoryOptionOk() throws Exception
+ {
+ // Create a command line parser for a mandatory option.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "t", "Test Option.", "test", "true" }
+ });
+
+ // Parse a command line with this option set correctly.
+ Properties testProps = parser.parseCommandLine(new String[] { "-t", "test" });
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"),
+ "test".equals(testProps.get("t")));
+ }
+
+ /** Check that parsing a mandatory free argument works ok. */
+ public void testParseMandatoryFreeArgumentOk() throws Exception
+ {
+ // Create a command line parser for a mandatory free argument.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "1", "Test Option.", "test", "true" }
+ });
+
+ // Parse a command line with this argument set.
+ Properties testProps = parser.parseCommandLine(new String[] { "test" });
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("The first free argument was not equal to \"test\" but was: " + testProps.get("1"),
+ "test".equals(testProps.get("1")));
+ }
+
+ /** Check that parsing a mandatory free argument fails when no argument is specified. */
+ public void testParseManadatoryFreeArgumentFailsNoArgument() throws Exception
+ {
+ // Create a command line parser for a mandatory free argument.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "1", "Test Option.", "test", "true" }
+ });
+
+ // Check that parsing fails when this mandatory free argument is missing.
+ boolean testPassed = false;
+
+ try
+ {
+ // Parse a command line with this free argument not set.
+ Properties testProps = parser.parseCommandLine(new String[] {});
+ }
+ catch (IllegalArgumentException e)
+ {
+ testPassed = true;
+ }
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("IllegalArgumentException not thrown for a missing mandatory option.", testPassed);
+ }
+
+ /** Check that parsing a mandatory option fails when no option is set. */
+ public void testParseMandatoryFailsNoOption() throws Exception
+ {
+ // Create a command line parser for a mandatory option.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "t", "Test Option.", "test", "true" }
+ });
+
+ // Check that parsing fails when this mandatory option is missing.
+ boolean testPassed = false;
+
+ try
+ {
+ // Parse a command line with this option not set.
+ Properties testProps = parser.parseCommandLine(new String[] {});
+ }
+ catch (IllegalArgumentException e)
+ {
+ testPassed = true;
+ }
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("IllegalArgumentException not thrown for a missing mandatory option.", testPassed);
+ }
+
+ /** Check that parsing an option with no space between it and its argument works ok. */
+ public void testParseOptionWithNoSpaceOk() throws Exception
+ {
+ // Create a command line parser for an option.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "t", "Test Option.", "test" }
+ });
+
+ // Parse a command line with this option set with no space.
+ Properties testProps = parser.parseCommandLine(new String[] { "-ttest" });
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"),
+ "test".equals(testProps.get("t")));
+ }
+
+ /** Check that parsing an option with a space between it and its argument works ok. */
+ public void testParseOptionWithSpaceOk() throws Exception
+ {
+ // Create a command line parser for an option.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "t", "Test Option.", "test" }
+ });
+
+ // Parse a command line with this option set with a space.
+ Properties testProps = parser.parseCommandLine(new String[] { "-t", "test" });
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"),
+ "test".equals(testProps.get("t")));
+ }
+
+ /** Check that parsing a single flag works ok. */
+ public void testParseSingleFlagOk() throws Exception
+ {
+ // Create a command line parser for a single flag.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "t", "Test Flag." }
+ });
+
+ // Parse a command line with the single flag set.
+ Properties testProps = parser.parseCommandLine(new String[] { "-t" });
+
+ // Check that the flag is set in the parsed properties.
+ assertTrue("The t flag was not \"true\", it was: " + testProps.get("t"), "true".equals(testProps.get("t")));
+
+ // Reset the parser.
+ parser.reset();
+
+ // Parse a command line with the single flag not set.
+ testProps = parser.parseCommandLine(new String[] {});
+
+ // Check that the flag is cleared in the parsed properties.
+ assertTrue("The t flag was not \"false\", it was: " + testProps.get("t"), "false".equals(testProps.get("t")));
+ }
+
+ /** Check that parsing an unknown option works when unknowns not errors. */
+ public void testParseUnknownOptionOk() throws Exception
+ {
+ // Create a command line parser for no flags or options
+ CommandLineParser parser = new CommandLineParser(new String[][] {});
+
+ // Check that parsing does not fail on an unknown flag.
+ try
+ {
+ parser.parseCommandLine(new String[] { "-t" });
+ }
+ catch (IllegalArgumentException e)
+ {
+ fail("The parser threw an IllegalArgumentException on an unknown flag when errors on unkowns is off.");
+ }
+ }
+
+ /** Check that parsing an unknown flag fails when unknowns are to be reported as errors. */
+ public void testParseUnknownFlagFailsWhenUnknownsAreErrors() throws Exception
+ {
+ // Create a command line parser for no flags or options
+ CommandLineParser parser = new CommandLineParser(new String[][] {});
+
+ // Turn on fail on unknowns mode.
+ parser.setErrorsOnUnknowns(true);
+
+ // Check that parsing fails on an unknown flag.
+ boolean testPassed = false;
+
+ try
+ {
+ parser.parseCommandLine(new String[] { "-t" });
+ }
+ catch (IllegalArgumentException e)
+ {
+ testPassed = true;
+ }
+
+ assertTrue("IllegalArgumentException not thrown for an unknown flag when errors on unknowns mode is on.",
+ testPassed);
+ }
+
+ /** Check that parsing an unknown option fails when unknowns are to be reported as errors. */
+ public void testParseUnknownOptionFailsWhenUnknownsAreErrors() throws Exception
+ {
+ // Create a command line parser for no flags or options
+ CommandLineParser parser = new CommandLineParser(new String[][] {});
+
+ // Turn on fail on unknowns mode.
+ parser.setErrorsOnUnknowns(true);
+
+ // Check that parsing fails on an unknown flag.
+ boolean testPassed = false;
+
+ try
+ {
+ parser.parseCommandLine(new String[] { "-t", "test" });
+ }
+ catch (IllegalArgumentException e)
+ {
+ testPassed = true;
+ }
+
+ assertTrue("IllegalArgumentException not thrown for an unknown option when errors on unknowns mode is on.",
+ testPassed);
+ }
+}
|