diff options
Diffstat (limited to 'java/common')
149 files changed, 8281 insertions, 4434 deletions
diff --git a/java/common/Composite.tpl b/java/common/Composite.tpl index 4aed9b0432..97b7d01f3c 100644 --- a/java/common/Composite.tpl +++ b/java/common/Composite.tpl @@ -1,4 +1,25 @@ -package $(pkg); +package org.apache.qpid.transport; +/* + * + * 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. + * + */ + import java.nio.ByteBuffer; import java.util.ArrayList; @@ -7,17 +28,15 @@ import java.util.List; import java.util.Map; import java.util.UUID; -import org.apache.qpid.transport.Future; -import org.apache.qpid.transport.Method; -import org.apache.qpid.transport.RangeSet; -import org.apache.qpid.transport.Struct; - import org.apache.qpid.transport.codec.Decoder; import org.apache.qpid.transport.codec.Encodable; import org.apache.qpid.transport.codec.Encoder; import org.apache.qpid.transport.network.Frame; +import org.apache.qpid.util.Strings; + + ${ from genutil import * @@ -73,7 +92,7 @@ public final class $name extends $base { return $pack; } - public final boolean hasPayloadSegment() { + public final boolean hasPayload() { return $payload; } @@ -108,7 +127,12 @@ if fields: ${ for f in fields: if f.option: continue - out(" $(f.set)($(f.name));\n") + if f.ref_type != f.type: + out(" $(f.set)($(f.name));\n") + else: + out(" if($(f.name) != null) {\n") + out(" $(f.set)($(f.name));\n") + out(" }\n") if segments: out(" setHeader(header);\n") @@ -126,6 +150,7 @@ if options or base == "Method": if base == "Method": out(""" case SYNC: this.setSync(true); break; case BATCH: this.setBatch(true); break; + case UNRELIABLE: this.setUnreliable(true); break; """) out(""" case NONE: break; default: throw new IllegalArgumentException("invalid option: " + _options[i]); @@ -135,9 +160,13 @@ if options or base == "Method": } } - public final <C> void dispatch(C context, MethodDelegate<C> delegate) { +${ + +if base == "Method": + out(""" public <C> void dispatch(C context, MethodDelegate<C> delegate) { delegate.$(dromedary(name))(context, this); - } + }""") +} ${ for f in fields: @@ -175,7 +204,13 @@ if not f.empty: } ${ if pack > 0: - out(" packing_flags |= $(f.flag_mask(pack));") + if f.empty: + out(" if (value)\\n") + out(" packing_flags |= $(f.flag_mask(pack));\\n") + out(" else\\n") + out(" packing_flags &= ~$(f.flag_mask(pack));") + else: + out(" packing_flags |= $(f.flag_mask(pack));") } this.dirty = true; return this; @@ -222,6 +257,26 @@ if segments: setBody(body); return this; } + + public final byte[] getBodyBytes() { + ByteBuffer buf = getBody(); + byte[] bytes = new byte[buf.remaining()]; + buf.get(bytes); + return bytes; + } + + public final void setBody(byte[] body) + { + setBody(ByteBuffer.wrap(body)); + } + + public final String getBodyString() { + return Strings.fromUTF8(getBodyBytes()); + } + + public final void setBody(String body) { + setBody(Strings.toUTF8(body)); + } """) } diff --git a/java/common/Constant.tpl b/java/common/Constant.tpl index 9f64cc657c..da4233c847 100644 --- a/java/common/Constant.tpl +++ b/java/common/Constant.tpl @@ -1,4 +1,25 @@ -package $(pkg); +package org.apache.qpid.transport; +/* + * + * 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. + * + */ + ${from genutil import *} diff --git a/java/common/Enum.tpl b/java/common/Enum.tpl index 7ff451997a..0835d34a20 100644 --- a/java/common/Enum.tpl +++ b/java/common/Enum.tpl @@ -1,4 +1,25 @@ -package $(pkg); +package org.apache.qpid.transport; +/* + * + * 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. + * + */ + public enum $name { ${ diff --git a/java/common/Invoker.tpl b/java/common/Invoker.tpl index 43aaced780..2eed43ad28 100644 --- a/java/common/Invoker.tpl +++ b/java/common/Invoker.tpl @@ -1,23 +1,37 @@ -package $(pkg); +package org.apache.qpid.transport; +/* + * + * 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. + * + */ + import java.nio.ByteBuffer; import java.util.List; import java.util.Map; import java.util.UUID; -import org.apache.qpid.transport.Future; -import org.apache.qpid.transport.Method; -import org.apache.qpid.transport.RangeSet; -import org.apache.qpid.transport.Struct; - -public abstract class Invoker { - - protected abstract void invoke(Method method); - protected abstract <T> Future<T> invoke(Method method, Class<T> resultClass); - +public abstract class $(invoker) { ${ from genutil import * +results = False + for c in composites: name = cname(c) fields = get_fields(c) @@ -25,6 +39,7 @@ for c in composites: args = get_arguments(c, fields) result = c["result"] if result: + results = True if not result["@type"]: rname = cname(result["struct"]) else: @@ -37,11 +52,22 @@ for c in composites: jreturn = "" jclass = "" + if c.name == "command": + access = "public " + else: + access = "" + out(""" - public final $jresult $(dromedary(name))($(", ".join(params))) { + $(access)final $jresult $(dromedary(name))($(", ".join(params))) { $(jreturn)invoke(new $name($(", ".join(args)))$jclass); } """) } - + protected abstract void invoke(Method method); +${ +if results: + out(""" + protected abstract <T> Future<T> invoke(Method method, Class<T> resultClass); +""") +} } diff --git a/java/common/MethodDelegate.tpl b/java/common/MethodDelegate.tpl index d2491dcf3a..27e20a7ef2 100644 --- a/java/common/MethodDelegate.tpl +++ b/java/common/MethodDelegate.tpl @@ -1,12 +1,37 @@ -package $(pkg); +package org.apache.qpid.transport; +/* + * + * 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. + * + */ + public abstract class MethodDelegate<C> { + public abstract void handle(C context, Method method); ${ from genutil import * for c in composites: name = cname(c) - out(" public void $(dromedary(name))(C context, $name struct) {}\n") + out(""" + public void $(dromedary(name))(C context, $name method) { + handle(context, method); + }""") } } diff --git a/java/common/Option.tpl b/java/common/Option.tpl index c1e8dcbe22..c22b35b999 100644 --- a/java/common/Option.tpl +++ b/java/common/Option.tpl @@ -1,24 +1,42 @@ -package $(pkg); +package org.apache.qpid.transport; +/* + * + * 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. + * + */ + public enum Option { ${ from genutil import * -names = ["NONE", "SYNC", "BATCH"] +options = {} + for c in composites: for f in c.query["field"]: t = resolve_type(f) if t["@name"] == "bit": - names.append(scream(f["@name"])) - -options = {} - -for option in names: - if not options.has_key(option): - if options: - out(",\n ") - options[option] = None - out("$option") -} + option = scream(f["@name"]) + if not options.has_key(option): + options[option] = None + out(" $option,\n")} + BATCH, + UNRELIABLE, + NONE } diff --git a/java/common/StructFactory.tpl b/java/common/StructFactory.tpl index 533005d561..09c669f74e 100644 --- a/java/common/StructFactory.tpl +++ b/java/common/StructFactory.tpl @@ -1,6 +1,25 @@ -package $(pkg); +package org.apache.qpid.transport; +/* + * + * 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. + * + */ -import org.apache.qpid.transport.Struct; class StructFactory { diff --git a/java/common/Type.tpl b/java/common/Type.tpl index 818a53ffcf..7f9cfee268 100644 --- a/java/common/Type.tpl +++ b/java/common/Type.tpl @@ -1,4 +1,25 @@ -package $(pkg); +package org.apache.qpid.transport; +/* + * + * 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. + * + */ + ${from genutil import *} diff --git a/java/common/bin/qpid-run b/java/common/bin/qpid-run index 1de0048f48..63bb648fd8 100755 --- a/java/common/bin/qpid-run +++ b/java/common/bin/qpid-run @@ -56,6 +56,12 @@ if [ -z $AMQJ_LOGGING_LEVEL ]; then export AMQJ_LOGGING_LEVEL=info fi +#Set to help us get round the manifold problems of ps/pgrep on various +#platforms which gather up to prevent qpid_stop from working ..... +if [ -z "$QPID_PNAME" ]; then + export QPID_PNAME=" -DPNAME=QPBRKR" +fi + if [ -z "$QPID_HOME" ]; then export QPID_HOME=$(dirname $(dirname $(readlink -f $0))) export PATH=${PATH}:${QPID_HOME}/bin @@ -105,6 +111,7 @@ if [ -n "$QPID_LOG_SUFFIX" ]; then fi log $INFO System Properties set to $SYSTEM_PROPS +log $INFO QPID_OPTS set to $QPID_OPTS program=$(basename $0) sourced=${BASH_SOURCE[0]} @@ -253,6 +260,6 @@ if $cygwin; then JAVA=$(cygpath -u $JAVA) fi -COMMAND=($JAVA $JAVA_VM $JAVA_GC $JAVA_MEM $SYSTEM_PROPS $JAVA_OPTS $QPID_OPTS "${JAVA_ARGS[@]}") +COMMAND=($JAVA $JAVA_VM $QPID_PNAME $JAVA_GC $JAVA_MEM $SYSTEM_PROPS $JAVA_OPTS $QPID_OPTS "${JAVA_ARGS[@]}") DISPATCH diff --git a/java/common/build.xml b/java/common/build.xml index 13c500b24e..44cc19aa07 100644 --- a/java/common/build.xml +++ b/java/common/build.xml @@ -20,18 +20,18 @@ --> <project name="AMQ Common" default="build"> + <property name="module.genpom" value="true"/> + <import file="../module.xml"/> <property name="gentools.home" location="${project.root}/../gentools" /> <property name="generated.package" value="org/apache/qpid/framing" /> <property name="generated.dir" location="${module.precompiled}/${generated.package}" /> <property name="xml.spec.dir" location="${project.root}/../specs" /> - <property name="xml.spec.deps" value="amqp.0-8.xml amqp.0-9.xml" /> - <property name="xml.spec.list" value="${xml.spec.dir}/amqp.0-8.xml ${xml.spec.dir}/amqp.0-9.xml" /> - <property name="mllib.dir" value="${project.root}/../python" /> + <property name="xml.spec.deps" value="amqp.0-8.xml amqp.0-9.xml amqp0-9-1.stripped.xml" /> + <property name="xml.spec.list" value="${xml.spec.dir}/amqp.0-8.xml ${xml.spec.dir}/amqp.0-9.xml ${xml.spec.dir}/amqp0-9-1.stripped.xml" /> <property name="gentools.timestamp" location="${generated.dir}/gentools.timestamp" /> - <property name="jython.timestamp" location="${module.precompiled}/jython.timestamp" /> - <property name="jython_1_0.timestamp" location="${module.precompiled}/jython_1_0.timestamp" /> + <property name="jython.timestamp" location="${generated.dir}/jython.timestamp" /> <target name="check_jython_deps"> <uptodate property="jython.notRequired" targetfile="${jython.timestamp}"> @@ -40,43 +40,17 @@ </target> <target name="jython" depends="check_jython_deps" unless="jython.notRequired"> - <java classname="org.python.util.jython" fork="true" failonerror="true"> - <arg value="-Dpython.cachedir.skip=true"/> - <arg value="-Dpython.path=${basedir}/jython-lib.jar/Lib${path.separator}${mllib.dir}"/> - <arg value="${basedir}/codegen"/> - <arg value="${module.precompiled}"/> - <arg value="${xml.spec.dir}/amqp.0-10-qpid-errata.xml"/> - <arg value="${basedir}"/> - <arg value="org/apache/qpid/transport"/> - <classpath> - <pathelement location="jython-2.2-rc2.jar"/> - </classpath> - </java> + <jython path="${mllib.dir}"> + <args> + <arg value="${basedir}/codegen"/> + <arg value="${module.precompiled}"/> + <arg value="${xml.spec.dir}/amqp.0-10-qpid-errata.xml"/> + <arg value="${basedir}"/> + </args> + </jython> <touch file="${jython.timestamp}" /> </target> - <target name="check_jython_deps_1_0"> - <uptodate property="jython.notRequired_1_0" targetfile="${jython_1_0.timestamp}"> - <srcfiles dir="${xml.spec.dir}" includes="amqp.1-0-draft.xml" /> - </uptodate> - </target> - - <target name="jython_1_0" depends="check_jython_deps_1_0" unless="jython.notRequired_1_0"> - <java classname="org.python.util.jython" fork="true" failonerror="true"> - <arg value="-Dpython.cachedir.skip=true"/> - <arg value="-Dpython.path=${basedir}/jython-lib.jar/Lib${path.separator}${mllib.dir}"/> - <arg value="${basedir}/codegen"/> - <arg value="${module.precompiled}"/> - <arg value="${xml.spec.dir}/amqp.1-0-draft.xml"/> - <arg value="${basedir}"/> - <arg value="org/apache/qpid/transport/v1_0"/> - <classpath> - <pathelement location="jython-2.2-rc2.jar"/> - </classpath> - </java> - <touch file="${jython_1_0.timestamp}" /> - </target> - <target name="compile_gentools"> <ant dir="${gentools.home}" /> </target> @@ -103,30 +77,7 @@ <touch file="${gentools.timestamp}" /> </target> - <property name="version.file" location="${module.classes}/qpidversion.properties"/> - <property file="${version.file}" prefix="old."/> - - <target name="check-version"> - <exec executable="svnversion" spawn="false" failifexecutionfails="false" - dir="${project.root}" outputproperty="qpid.svnversion"> - <arg line="."/> - </exec> - <condition property="version.stale"> - <not> - <equals arg1="${qpid.svnversion}" arg2="${old.qpid.svnversion}"/> - </not> - </condition> - </target> - - <target name="version" depends="check-version" if="version.stale"> - <!-- Write the version.properties out. --> - <echo file="${version.file}" append="true"> - qpid.svnversion=${qpid.svnversion} - qpid.name=${project.name} - qpid.version=${project.version} - </echo> - </target> - - <target name="precompile" depends="gentools,jython,jython_1_0,version"/> + <target name="precompile" depends="gentools,jython,create-version"/> + <target name="bundle" depends="bundle-tasks"/> </project> diff --git a/java/common/codegen b/java/common/codegen index 07c5e5c6fb..6a1effc07b 100755 --- a/java/common/codegen +++ b/java/common/codegen @@ -1,5 +1,24 @@ #!/usr/bin/env python +# +# 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. +# + import os, sys, mllib from templating import Parser from genutil import * @@ -7,9 +26,7 @@ from genutil import * out_dir = sys.argv[1] spec_file = sys.argv[2] tpl_dir = sys.argv[3] -pkg_rel = sys.argv[4] -pkg_dir = os.path.join(out_dir, pkg_rel) -pkg = pkg_rel.replace("/", ".") +pkg_dir = os.path.join(out_dir, "org/apache/qpid/transport") if not os.path.exists(pkg_dir): os.makedirs(pkg_dir) @@ -35,8 +52,8 @@ def execute(output, template, **kwargs): f.write(p.output) f.close() -execute("Type.java", "Type.tpl", spec = spec, pkg = pkg) -execute("Constant.java", "Constant.tpl", spec = spec, pkg = pkg) +execute("Type.java", "Type.tpl", spec = spec) +execute("Constant.java", "Constant.tpl", spec = spec) structs = spec.query["amqp/struct"] + \ spec.query["amqp/class/struct", excludes] + \ @@ -45,15 +62,21 @@ controls = spec.query["amqp/class/control", excludes] commands = spec.query["amqp/class/command", excludes] composites = structs + controls + commands +actions = controls + commands +connection = [c for c in actions if c.parent["@name"] == "connection"] +session = [c for c in actions if c.parent["@name"] != "connection"] for c in composites: name = cname(c) - execute("%s.java" % name, "Composite.tpl", type = c, name = name, pkg = pkg) + execute("%s.java" % name, "Composite.tpl", type = c, name = name) -execute("MethodDelegate.java", "MethodDelegate.tpl", composites = composites, pkg = pkg) -execute("Option.java", "Option.tpl", composites = composites, pkg = pkg) -execute("Invoker.java", "Invoker.tpl", composites = controls + commands, pkg = pkg) -execute("StructFactory.java", "StructFactory.tpl", composites = composites, pkg = pkg) +execute("MethodDelegate.java", "MethodDelegate.tpl", composites = actions) +execute("Option.java", "Option.tpl", composites = composites) +execute("ConnectionInvoker.java", "Invoker.tpl", invoker = "ConnectionInvoker", + composites = connection) +execute("SessionInvoker.java", "Invoker.tpl", invoker = "SessionInvoker", + composites = session) +execute("StructFactory.java", "StructFactory.tpl", composites = composites) def is_enum(nd): return nd["enum"] is not None @@ -63,4 +86,4 @@ enums = spec.query["amqp/domain", is_enum] + \ for e in enums: name = cname(e) - execute("%s.java" % name, "Enum.tpl", name = name, type = e, pkg = pkg) + execute("%s.java" % name, "Enum.tpl", name = name, type = e) diff --git a/java/common/genutil.py b/java/common/genutil.py index f8f234548c..57a461ed40 100644 --- a/java/common/genutil.py +++ b/java/common/genutil.py @@ -1,3 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# def camel(offset, *args): parts = [] @@ -180,6 +198,7 @@ class Field: self.read = "dec.read%s()" % self.coder self.write = "enc.write%s(check(struct).%s)" % (self.coder, self.name) self.type = jtype(self.type_node) + self.ref_type = jref(self.type) self.default = DEFAULTS.get(self.type, "null") self.has = camel(1, "has", self.name) self.get = camel(1, "get", self.name) diff --git a/java/common/jython-2.2-rc2.jar b/java/common/jython-2.2-rc2.jar Binary files differdeleted file mode 100644 index e25e2cb945..0000000000 --- a/java/common/jython-2.2-rc2.jar +++ /dev/null diff --git a/java/common/jython-lib.jar b/java/common/jython-lib.jar Binary files differdeleted file mode 100644 index bb3ba82db5..0000000000 --- a/java/common/jython-lib.jar +++ /dev/null diff --git a/java/common/pom.xml b/java/common/pom.xml deleted file mode 100644 index 894ca26710..0000000000 --- a/java/common/pom.xml +++ /dev/null @@ -1,187 +0,0 @@ -<!-- - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. ---> -<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-M3-SNAPSHOT</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-M3-SNAPSHOT</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>${basedir}/../../specs</specs.dir> - <mllib.dir>${basedir}/../../python</mllib.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> - <echo>"${generated.path}"</echo> - <ant antfile="protocol-version.xml" /> -<!-- <exec executable="python"> - <arg line="generate"/> - <arg line="${generated.path}"/> - <arg line="org.apache.qpid"/> - <arg line="${specs.dir}/amqp-transitional.0-10.xml"/> - </exec> --> - </tasks> - <sourceRoot>${generated.path}</sourceRoot> - </configuration> - <goals> - <goal>run</goal> - </goals> - </execution> - </executions> - </plugin> - - <plugin> - <groupId>org.apache.qpid</groupId> - <artifactId>jython-plugin</artifactId> - <executions> - <execution> - <id>jython</id> - <phase>generate-sources</phase> - <configuration> - <params> - <param>-Dpython.cachedir.skip=true</param> - <param>-Dpython.path=${basedir}/jython-lib.jar/Lib${path.separator}${mllib.dir}</param> - <param>${basedir}/codegen</param> - <param>${generated.path}</param> - <param>${specs.dir}/amqp.0-10-qpid-errata.xml</param> - <param>${basedir}</param> - </params> - <sources> - <source>${specs.dir}/amqp.0-10-qpid-errata.xml</source> - <source>${basedir}/codegen</source> - </sources> - <timestamp>${generated.path}/generated.timestamp</timestamp> - </configuration> - <goals> - <goal>jython</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> - <!--- This is used by filter --> - <dependency> - <groupId>org.apache.geronimo.specs</groupId> - <artifactId>geronimo-jms_1.1_spec</artifactId> - </dependency> - </dependencies> -</project> diff --git a/java/common/protocol-version.xml b/java/common/protocol-version.xml index ee9db6049b..5435a0a582 100644 --- a/java/common/protocol-version.xml +++ b/java/common/protocol-version.xml @@ -27,8 +27,8 @@ <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 amqp.0-9.xml" /> - <property name="xml.spec.list" value="${xml.spec.dir}/amqp.0-8.xml ${xml.spec.dir}/amqp.0-9.xml" /> + <property name="xml.spec.deps" value="amqp.0-8.xml amqp.0-9.xml amqp0-9-1.stripped.xml" /> + <property name="xml.spec.list" value="${xml.spec.dir}/amqp.0-8.xml ${xml.spec.dir}/amqp.0-9.xml ${xml.spec.dir}/amqp0-9-1.stripped.xml" /> <property name="template.dir" value="${topDirectoryLocation}/common/templates" /> diff --git a/java/common/src/main/java/common.bnd b/java/common/src/main/java/common.bnd new file mode 100755 index 0000000000..120a2401d3 --- /dev/null +++ b/java/common/src/main/java/common.bnd @@ -0,0 +1,6 @@ +ver: 0.6.0
+
+Bundle-SymbolicName: qpid-common
+Bundle-Version: ${ver}
+Export-Package: *;version=${ver}
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
diff --git a/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java b/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java index afd415b1eb..8ef6facef1 100644 --- a/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java +++ b/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java @@ -60,7 +60,7 @@ public class AMQConnectionException extends AMQException public AMQFrame getCloseFrame(int channel) { MethodRegistry reg = MethodRegistry.getMethodRegistry(new ProtocolVersion(major,minor)); - return new AMQFrame(channel, + return new AMQFrame(0, reg.createConnectionCloseBody(getErrorCode().getCode(), new AMQShortString(getMessage()), _classId, diff --git a/java/common/src/main/java/org/apache/qpid/AMQException.java b/java/common/src/main/java/org/apache/qpid/AMQException.java index eda532b64e..be335c5dba 100644 --- a/java/common/src/main/java/org/apache/qpid/AMQException.java +++ b/java/common/src/main/java/org/apache/qpid/AMQException.java @@ -90,4 +90,33 @@ public class AMQException extends Exception { return true; } + + /** + * Rethrown this exception as a new exception. + * + * Attempt to create a new exception of the same class if they hav the default constructor of: + * {AMQConstant.class, String.class, Throwable.class} + * + * Individual subclasses may override as requried to create a new instance. + * + */ + public AMQException cloneForCurrentThread() + { + Class amqeClass = this.getClass(); + Class<?>[] paramClasses = {AMQConstant.class, String.class, Throwable.class}; + Object[] params = {getErrorCode(), getMessage(), this}; + + AMQException newAMQE; + + try + { + newAMQE = (AMQException) amqeClass.getConstructor(paramClasses).newInstance(params); + } + catch (Exception creationException) + { + newAMQE = new AMQException(getErrorCode(), getMessage(), this); + } + + return newAMQE; + } } diff --git a/java/common/src/main/java/org/apache/qpid/BrokerDetails.java b/java/common/src/main/java/org/apache/qpid/BrokerDetails.java deleted file mode 100644 index 63f67a7857..0000000000 --- a/java/common/src/main/java/org/apache/qpid/BrokerDetails.java +++ /dev/null @@ -1,138 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.qpid; - -import java.util.Map; - -/** - * This interface represents a broker and provides the basic information - * required for opening a connection with a broker. - */ -public interface BrokerDetails -{ - /** - * Those are the supported protocols - */ - public static final String PROTOCOL_TCP = "tcp"; - public static final String PROTOCOL_TLS = "tls"; - - public static final String VIRTUAL_HOST = "virtualhost"; - public static final String CLIENT_ID = "client_id"; - public static final String USERNAME = "username"; - public static final String PASSWORD = "password"; - - /** - * Get the broker host name. - * - * @return The broker host name. - */ - public String getHost(); - - /** - * Get the broker port number. - * - * @return The broker port number. - */ - public int getPort(); - - /** - * Get the virtual host to connect to. - * - * @return The virtual host of this broker. - */ - public String getVirtualHost(); - - /** - * Get the user name. - * - * @return The user name - */ - public String getUserName(); - - /** - * Get the user password - * - * @return The user password - */ - public String getPassword(); - - /** - * Get the protocol used to connect to hise broker. - * - * @return the protocol used to connect to the broker. - */ - public String getProtocol(); - - /** - * Set the broker host name. - * - * @param host The broker host name. - */ - public void setHost(String host); - - /** - * Set the broker port number. - * - * @param port The broker port number. - */ - public void setPort(int port); - - /** - * Set the virtual host to connect to. - * - * @param virtualHost The virtual host of this broker. - */ - public void setVirtualHost(String virtualHost); - - /** - * Set the user name. - * - * @param userName The user name - */ - public void setUserName(String userName); - - /** - * Set the user password - * - * @param password The user password - */ - public void setPassword(String password); - - /** - * Set the protocol used to connect to hise broker. - * - * @param protocol the protocol used to connect to the broker. - */ - public void setProtocol(String protocol); - - /** - * Ex: keystore path - * - * @return the Properties associated with this connection. - */ - public Map<String,String> getProperties(); - - /** - * Sets the properties associated with this connection - * - * @param props the new p[roperties. - */ - public void setProperties(Map<String,String> props); - - public void setProperty(String key,String value); -} diff --git a/java/common/src/main/java/org/apache/qpid/BrokerDetailsImpl.java b/java/common/src/main/java/org/apache/qpid/BrokerDetailsImpl.java deleted file mode 100644 index 201d43e21f..0000000000 --- a/java/common/src/main/java/org/apache/qpid/BrokerDetailsImpl.java +++ /dev/null @@ -1,264 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.qpid; - -import java.util.HashMap; -import java.util.Map; - -/** - * Implements the interface BrokerDetails - */ -public class BrokerDetailsImpl implements BrokerDetails -{ - //--- Those are the default values - private final String DEFAULT_USERNAME = "guest"; - private final String DEFAULT_PASSWORD = "guest"; - private final String DEFAULT_VIRTUALHOST = ""; - - //---- The brker details - private String _host; - private int _port = 0; - private String _virtualHost; - private String _userName = DEFAULT_USERNAME; - private String _password = DEFAULT_PASSWORD; - private String _protocol; - private Map<String, String> _props = new HashMap<String, String>(); - ; - - //--- Constructors - - public BrokerDetailsImpl() - { - } - - /** - * Create a new broker details given all the reuqired information - * - * @param protocol The protocol used for this broker connection - * @param host The host name. - * @param port The port number. - * @param virtualHost The virtual host. - * @param userName The user name. - * @param password The user password. - */ - public BrokerDetailsImpl(String protocol, String host, int port, String virtualHost, String userName, - String password, Map<String, String> props) - { - _protocol = protocol; - _host = host; - _port = port; - _virtualHost = virtualHost; - _userName = userName; - _password = password; - _props = props; - } - - /** - * Create a new broker details given the host name and the procol type, - * default values are used for the other details. - * - * @param protocol The protocol used for this broker connection - * @param host The host name. - */ - public BrokerDetailsImpl(String protocol, String host) - { - _protocol = protocol; - _host = host; - _virtualHost = DEFAULT_VIRTUALHOST; - _userName = DEFAULT_USERNAME; - _password = DEFAULT_PASSWORD; - } - - //--- API BrokerDetails - /** - * Get the user password - * - * @return The user password - */ - public String getPassword() - { - return _password; - } - - /** - * Get the broker host name. - * - * @return The broker host name. - */ - public String getHost() - { - return _host; - } - - /** - * Get the broker port number. - * - * @return The broker port number. - */ - public int getPort() - { - if (_port == 0) - { - if (getProtocol().equals(BrokerDetails.PROTOCOL_TCP)) - { - _port = 5672; - } - else if (getProtocol().equals(BrokerDetails.PROTOCOL_TLS)) - { - _port = 5555; - } - } - return _port; - } - - /** - * Get the virtual host to connect to. - * - * @return The virtual host of this broker. - */ - public String getVirtualHost() - { - return _virtualHost; - } - - /** - * Get the user name. - * - * @return The user name - */ - public String getUserName() - { - return _userName; - } - - /** - * Get the protocol used to connect to hise broker. - * - * @return the protocol used to connect to the broker. - */ - public String getProtocol() - { - return _protocol; - } - - /** - * Set the broker host name. - * - * @param host The broker host name. - */ - public void setHost(String host) - { - _host = host; - } - - /** - * Set the broker port number. - * - * @param port The broker port number. - */ - public void setPort(int port) - { - _port = port; - } - - /** - * Set the virtual host to connect to. - * - * @param virtualHost The virtual host of this broker. - */ - public void setVirtualHost(String virtualHost) - { - _virtualHost = virtualHost; - } - - /** - * Set the user name. - * - * @param userName The user name - */ - public void setUserName(String userName) - { - _userName = userName; - } - - /** - * Set the user password - * - * @param password The user password - */ - public void setPassword(String password) - { - _password = password; - } - - /** - * Set the protocol used to connect to hise broker. - * - * @param protocol the protocol used to connect to the broker. - */ - public void setProtocol(String protocol) - { - _protocol = protocol; - } - - /** - * Ex: keystore path - * - * @return the Properties associated with this connection. - */ - public Map<String, String> getProperties() - { - return _props; - } - - /** - * Sets the properties associated with this connection - * - * @param props - */ - public void setProperties(Map<String, String> props) - { - _props = props; - } - - public void setProperty(String key, String value) - { - _props.put(key, value); - } - - public String toString() - { - StringBuilder b = new StringBuilder(); - b.append("[username=" + _userName); - b.append(",password=" + _password); - b.append(",transport=" + _protocol); - b.append(",host=" + _host); - b.append(",port=" + getPort() + "]"); - b.append(" - Properties["); - if (_props != null) - { - for (String k : _props.keySet()) - { - b.append(k + "=" + _props.get(k) + ","); - } - } - b.append("]"); - - return b.toString(); - } -} diff --git a/java/common/src/main/java/org/apache/qpid/ConsoleOutput.java b/java/common/src/main/java/org/apache/qpid/ConsoleOutput.java index f17782ebf4..7d8a5b7b36 100644 --- a/java/common/src/main/java/org/apache/qpid/ConsoleOutput.java +++ b/java/common/src/main/java/org/apache/qpid/ConsoleOutput.java @@ -20,12 +20,12 @@ */ package org.apache.qpid; +import static org.apache.qpid.transport.util.Functions.str; + import java.nio.ByteBuffer; import org.apache.qpid.transport.Sender; -import static org.apache.qpid.transport.util.Functions.*; - /** * ConsoleOutput @@ -51,4 +51,12 @@ public class ConsoleOutput implements Sender<ByteBuffer> System.out.println("CLOSED"); } + public void setIdleTimeout(long l) + { + // TODO Auto-generated method stub + + } + + + } diff --git a/java/common/src/main/java/org/apache/qpid/ErrorCode.java b/java/common/src/main/java/org/apache/qpid/ErrorCode.java index be1ad16dd5..0549869e71 100644 --- a/java/common/src/main/java/org/apache/qpid/ErrorCode.java +++ b/java/common/src/main/java/org/apache/qpid/ErrorCode.java @@ -1,4 +1,25 @@ package org.apache.qpid; +/* + * + * 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. + * + */ + public enum ErrorCode { @@ -99,4 +120,4 @@ public enum ErrorCode An invalid or illegal argument was passed to a method, and the operation could not proceed. </doc> </constant> -*/
\ No newline at end of file +*/ diff --git a/java/common/src/main/java/org/apache/qpid/QpidConfig.java b/java/common/src/main/java/org/apache/qpid/QpidConfig.java index e8d42fdf83..9c8019f109 100644 --- a/java/common/src/main/java/org/apache/qpid/QpidConfig.java +++ b/java/common/src/main/java/org/apache/qpid/QpidConfig.java @@ -1,4 +1,25 @@ package org.apache.qpid; +/* + * + * 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. + * + */ + /** * API to configure the Security parameters of the client. diff --git a/java/common/src/main/java/org/apache/qpid/SecurityHelper.java b/java/common/src/main/java/org/apache/qpid/SecurityHelper.java deleted file mode 100644 index dda5a6506d..0000000000 --- a/java/common/src/main/java/org/apache/qpid/SecurityHelper.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid; - -import java.io.UnsupportedEncodingException; -import java.util.HashSet; -import java.util.List; -import java.util.StringTokenizer; - -import org.apache.qpid.security.AMQPCallbackHandler; -import org.apache.qpid.security.CallbackHandlerRegistry; - -public class SecurityHelper -{ - public static String chooseMechanism(List<Object> mechanisms) throws UnsupportedEncodingException - { - HashSet mechanismSet = new HashSet(); - for (Object m : mechanisms) - { - mechanismSet.add(m); - } - - String preferredMechanisms = CallbackHandlerRegistry.getInstance().getMechanisms(); - StringTokenizer prefTokenizer = new StringTokenizer(preferredMechanisms, " "); - while (prefTokenizer.hasMoreTokens()) - { - String mech = prefTokenizer.nextToken(); - if (mechanismSet.contains(mech)) - { - return mech; - } - } - return null; - } - - public static AMQPCallbackHandler createCallbackHandler(String mechanism, String username,String password) - throws QpidException - { - Class mechanismClass = CallbackHandlerRegistry.getInstance().getCallbackHandlerClass(mechanism); - try - { - Object instance = mechanismClass.newInstance(); - AMQPCallbackHandler cbh = (AMQPCallbackHandler) instance; - cbh.initialise(username,password); - return cbh; - } - catch (Exception e) - { - throw new QpidException("Unable to create callback handler: " + e,ErrorCode.UNDEFINED, e.getCause()); - } - } - -} diff --git a/java/common/src/main/java/org/apache/qpid/SerialException.java b/java/common/src/main/java/org/apache/qpid/SerialException.java index 4fc8458e45..c59a6af779 100644 --- a/java/common/src/main/java/org/apache/qpid/SerialException.java +++ b/java/common/src/main/java/org/apache/qpid/SerialException.java @@ -1,4 +1,25 @@ package org.apache.qpid; +/* + * + * 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. + * + */ + /** * This exception is used by the serial class (imp RFC 1982) diff --git a/java/common/src/main/java/org/apache/qpid/ToyBroker.java b/java/common/src/main/java/org/apache/qpid/ToyBroker.java index 83d434b20a..db84b83adb 100644 --- a/java/common/src/main/java/org/apache/qpid/ToyBroker.java +++ b/java/common/src/main/java/org/apache/qpid/ToyBroker.java @@ -174,23 +174,14 @@ class ToyBroker extends SessionDelegate public static final void main(String[] args) throws IOException { final ToyExchange exchange = new ToyExchange(); - ConnectionDelegate delegate = new ConnectionDelegate() + ConnectionDelegate delegate = new ServerDelegate() { public SessionDelegate getSessionDelegate() { return new ToyBroker(exchange); } - public void exception(Throwable t) - { - t.printStackTrace(); - } - public void closed() {} }; - //hack - delegate.setUsername("guest"); - delegate.setPassword("guest"); - MinaHandler.accept("0.0.0.0", 5672, delegate); } diff --git a/java/common/src/main/java/org/apache/qpid/ToyClient.java b/java/common/src/main/java/org/apache/qpid/ToyClient.java index cb10859c9f..5b2db10613 100644 --- a/java/common/src/main/java/org/apache/qpid/ToyClient.java +++ b/java/common/src/main/java/org/apache/qpid/ToyClient.java @@ -33,48 +33,30 @@ import org.apache.qpid.transport.network.mina.MinaHandler; * @author Rafael H. Schloming */ -class ToyClient extends SessionDelegate +class ToyClient implements SessionListener { + public void opened(Session ssn) {} - @Override public void messageReject(Session ssn, MessageReject reject) + public void resumed(Session ssn) {} + + public void exception(Session ssn, SessionException exc) { - for (Range range : reject.getTransfers()) - { - for (long l = range.getLower(); l <= range.getUpper(); l++) - { - System.out.println("message rejected: " + - ssn.getCommand((int) l)); - } - } + exc.printStackTrace(); } - @Override public void messageTransfer(Session ssn, MessageTransfer xfr) + public void message(Session ssn, MessageTransfer xfr) { System.out.println("msg: " + xfr); } + public void closed(Session ssn) {} + public static final void main(String[] args) { - Connection conn = MinaHandler.connect("0.0.0.0", 5672, - new ClientDelegate() - { - public SessionDelegate getSessionDelegate() - { - return new ToyClient(); - } - public void exception(Throwable t) - { - t.printStackTrace(); - } - public void closed() {} - }); - conn.send(new ProtocolHeader - (1, 0, 10)); - - Channel ch = conn.getChannel(0); - Session ssn = new Session("my-session".getBytes()); - ssn.attach(ch); - ssn.sessionAttach(ssn.getName()); + Connection conn = new Connection(); + conn.connect("0.0.0.0", 5672, null, "guest", "guest", false); + Session ssn = conn.createSession(); + ssn.setSessionListener(new ToyClient()); ssn.queueDeclare("asdf", null, null); ssn.sync(); diff --git a/java/common/src/main/java/org/apache/qpid/ToyExchange.java b/java/common/src/main/java/org/apache/qpid/ToyExchange.java index c638679596..da6aed9629 100644 --- a/java/common/src/main/java/org/apache/qpid/ToyExchange.java +++ b/java/common/src/main/java/org/apache/qpid/ToyExchange.java @@ -1,4 +1,25 @@ package org.apache.qpid; +/* + * + * 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. + * + */ + import java.util.ArrayList; import java.util.HashMap; diff --git a/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java b/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java index fa890d0ebb..591dbd085b 100644 --- a/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java +++ b/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java @@ -23,6 +23,7 @@ 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; +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; /** * AMQCodecFactory is a Mina codec factory. It supplies the encoders and decoders need to read and write the bytes to @@ -50,9 +51,9 @@ public class AMQCodecFactory implements ProtocolCodecFactory * @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) + public AMQCodecFactory(boolean expectProtocolInitiation, AMQVersionAwareProtocolSession session) { - _frameDecoder = new AMQDecoder(expectProtocolInitiation); + _frameDecoder = new AMQDecoder(expectProtocolInitiation, session); } /** @@ -70,7 +71,7 @@ public class AMQCodecFactory implements ProtocolCodecFactory * * @return The AMQP decoder. */ - public ProtocolDecoder getDecoder() + public AMQDecoder getDecoder() { return _frameDecoder; } diff --git a/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java b/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java index 7eef73f337..281c0761d9 100644 --- a/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java +++ b/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java @@ -20,14 +20,21 @@ */ package org.apache.qpid.codec; +import java.util.ArrayList; + import org.apache.mina.common.ByteBuffer; import org.apache.mina.common.IoSession; import org.apache.mina.common.SimpleByteBufferAllocator; import org.apache.mina.filter.codec.CumulativeProtocolDecoder; import org.apache.mina.filter.codec.ProtocolDecoderOutput; +import org.apache.qpid.framing.AMQDataBlock; import org.apache.qpid.framing.AMQDataBlockDecoder; +import org.apache.qpid.framing.AMQFrameDecodingException; +import org.apache.qpid.framing.AMQMethodBodyFactory; +import org.apache.qpid.framing.AMQProtocolVersionException; import org.apache.qpid.framing.ProtocolInitiation; +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; /** * AMQDecoder delegates the decoding of AMQP either to a data block decoder, or in the case of new connections, to a @@ -62,14 +69,19 @@ public class AMQDecoder extends CumulativeProtocolDecoder private boolean _expectProtocolInitiation; private boolean firstDecode = true; + private AMQMethodBodyFactory _bodyFactory; + + private ByteBuffer _remainingBuf; + /** * Creates a new AMQP decoder. * * @param expectProtocolInitiation <tt>true</tt> if this decoder needs to handle protocol initiation. */ - public AMQDecoder(boolean expectProtocolInitiation) + public AMQDecoder(boolean expectProtocolInitiation, AMQVersionAwareProtocolSession session) { _expectProtocolInitiation = expectProtocolInitiation; + _bodyFactory = new AMQMethodBodyFactory(session); } /** @@ -120,7 +132,7 @@ public class AMQDecoder extends CumulativeProtocolDecoder protected boolean doDecodeDataBlock(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception { int pos = in.position(); - boolean enoughData = _dataBlockDecoder.decodable(session, in); + boolean enoughData = _dataBlockDecoder.decodable(in.buf()); in.position(pos); if (!enoughData) { @@ -149,7 +161,7 @@ public class AMQDecoder extends CumulativeProtocolDecoder */ private boolean doDecodePI(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception { - boolean enoughData = _piDecoder.decodable(session, in); + boolean enoughData = _piDecoder.decodable(in.buf()); if (!enoughData) { // returning false means it will leave the contents in the buffer and @@ -158,7 +170,8 @@ public class AMQDecoder extends CumulativeProtocolDecoder } else { - _piDecoder.decode(session, in, out); + ProtocolInitiation pi = new ProtocolInitiation(in.buf()); + out.write(pi); return true; } @@ -177,7 +190,7 @@ public class AMQDecoder extends CumulativeProtocolDecoder } - /** + /** * Cumulates content of <tt>in</tt> into internal buffer and forwards * decoding request to {@link #doDecode(IoSession, ByteBuffer, ProtocolDecoderOutput)}. * <tt>doDecode()</tt> is invoked repeatedly until it returns <tt>false</tt> @@ -268,4 +281,60 @@ public class AMQDecoder extends CumulativeProtocolDecoder session.setAttribute( BUFFER, remainingBuf ); } + public ArrayList<AMQDataBlock> decodeBuffer(java.nio.ByteBuffer buf) throws AMQFrameDecodingException, AMQProtocolVersionException + { + + // get prior remaining data from accumulator + ArrayList<AMQDataBlock> dataBlocks = new ArrayList<AMQDataBlock>(); + ByteBuffer msg; + // if we have a session buffer, append data to that otherwise + // use the buffer read from the network directly + if( _remainingBuf != null ) + { + _remainingBuf.put(buf); + _remainingBuf.flip(); + msg = _remainingBuf; + } + else + { + msg = ByteBuffer.wrap(buf); + } + + if (_expectProtocolInitiation + || (firstDecode + && (msg.remaining() > 0) + && (msg.get(msg.position()) == (byte)'A'))) + { + if (_piDecoder.decodable(msg.buf())) + { + dataBlocks.add(new ProtocolInitiation(msg.buf())); + } + } + else + { + boolean enoughData = true; + while (enoughData) + { + int pos = msg.position(); + + enoughData = _dataBlockDecoder.decodable(msg); + msg.position(pos); + if (enoughData) + { + dataBlocks.add(_dataBlockDecoder.createAndPopulateFrame(_bodyFactory, msg)); + } + else + { + _remainingBuf = SIMPLE_BYTE_BUFFER_ALLOCATOR.allocate(msg.remaining(), false); + _remainingBuf.setAutoExpand(true); + _remainingBuf.put(msg); + } + } + } + if(firstDecode && dataBlocks.size() > 0) + { + firstDecode = false; + } + return dataBlocks; + } } diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java b/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java index 82ffc60802..228867b2b0 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java +++ b/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java @@ -47,7 +47,7 @@ public class AMQDataBlockDecoder public AMQDataBlockDecoder() { } - public boolean decodable(IoSession session, ByteBuffer in) throws AMQFrameDecodingException + public boolean decodable(java.nio.ByteBuffer in) throws AMQFrameDecodingException { final int remainingAfterAttributes = in.remaining() - (1 + 2 + 4 + 1); // type, channel, body length and end byte @@ -56,14 +56,15 @@ public class AMQDataBlockDecoder return false; } - in.skip(1 + 2); - final long bodySize = in.getUnsignedInt(); + in.position(in.position() + 1 + 2); + // Get an unsigned int, lifted from MINA ByteBuffer getUnsignedInt() + final long bodySize = in.getInt() & 0xffffffffL; return (remainingAfterAttributes >= bodySize); } - protected Object createAndPopulateFrame(IoSession session, ByteBuffer in) + public AMQFrame createAndPopulateFrame(AMQMethodBodyFactory methodBodyFactory, ByteBuffer in) throws AMQFrameDecodingException, AMQProtocolVersionException { final byte type = in.get(); @@ -71,15 +72,7 @@ public class AMQDataBlockDecoder 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); - - } - + bodyFactory = methodBodyFactory; } else { @@ -115,6 +108,24 @@ public class AMQDataBlockDecoder public void decode(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception { - out.write(createAndPopulateFrame(session, in)); + AMQMethodBodyFactory bodyFactory = (AMQMethodBodyFactory) 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); + } + + out.write(createAndPopulateFrame(bodyFactory, in)); + } + + public boolean decodable(ByteBuffer msg) throws AMQFrameDecodingException + { + return decodable(msg.buf()); + } + + public AMQDataBlock createAndPopulateFrame(AMQMethodBodyFactory factory, java.nio.ByteBuffer msg) throws AMQProtocolVersionException, AMQFrameDecodingException + { + return createAndPopulateFrame(factory, ByteBuffer.wrap(msg)); } } diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java b/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java index 05fd2bb480..374644b4f2 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java +++ b/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java @@ -50,7 +50,7 @@ public final class AMQDataBlockEncoder implements MessageEncoder { _logger.debug("Encoded frame byte-buffer is '" + EncodingUtils.convertToHexString(buffer) + "'"); } - + out.write(buffer); } diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java b/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java index ad7f36f790..cd3d721065 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java +++ b/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java @@ -93,4 +93,166 @@ public abstract class AMQMethodBodyImpl implements AMQMethodBody session.methodFrameReceived(channelId, this); } + public int getSize() + { + return 2 + 2 + getBodySize(); + } + + public void writePayload(ByteBuffer buffer) + { + EncodingUtils.writeUnsignedShort(buffer, getClazz()); + EncodingUtils.writeUnsignedShort(buffer, getMethod()); + writeMethodPayload(buffer); + } + + + protected byte readByte(ByteBuffer buffer) + { + return buffer.get(); + } + + protected AMQShortString readAMQShortString(ByteBuffer buffer) + { + return EncodingUtils.readAMQShortString(buffer); + } + + protected int getSizeOf(AMQShortString string) + { + return EncodingUtils.encodedShortStringLength(string); + } + + protected void writeByte(ByteBuffer buffer, byte b) + { + buffer.put(b); + } + + protected void writeAMQShortString(ByteBuffer buffer, AMQShortString string) + { + EncodingUtils.writeShortStringBytes(buffer, string); + } + + protected int readInt(ByteBuffer buffer) + { + return buffer.getInt(); + } + + protected void writeInt(ByteBuffer buffer, int i) + { + buffer.putInt(i); + } + + protected FieldTable readFieldTable(ByteBuffer buffer) throws AMQFrameDecodingException + { + return EncodingUtils.readFieldTable(buffer); + } + + protected int getSizeOf(FieldTable table) + { + return EncodingUtils.encodedFieldTableLength(table); //To change body of created methods use File | Settings | File Templates. + } + + protected void writeFieldTable(ByteBuffer buffer, FieldTable table) + { + EncodingUtils.writeFieldTableBytes(buffer, table); + } + + protected long readLong(ByteBuffer buffer) + { + return buffer.getLong(); + } + + protected void writeLong(ByteBuffer buffer, long l) + { + buffer.putLong(l); + } + + protected int getSizeOf(byte[] response) + { + return (response == null) ? 4 : response.length + 4; + } + + protected void writeBytes(ByteBuffer buffer, byte[] data) + { + EncodingUtils.writeBytes(buffer,data); + } + + protected byte[] readBytes(ByteBuffer buffer) + { + return EncodingUtils.readBytes(buffer); + } + + protected short readShort(ByteBuffer buffer) + { + return EncodingUtils.readShort(buffer); + } + + protected void writeShort(ByteBuffer buffer, short s) + { + EncodingUtils.writeShort(buffer, s); + } + + protected Content readContent(ByteBuffer buffer) + { + return null; //To change body of created methods use File | Settings | File Templates. + } + + protected int getSizeOf(Content body) + { + return 0; //To change body of created methods use File | Settings | File Templates. + } + + protected void writeContent(ByteBuffer buffer, Content body) + { + //To change body of created methods use File | Settings | File Templates. + } + + protected byte readBitfield(ByteBuffer buffer) + { + return readByte(buffer); //To change body of created methods use File | Settings | File Templates. + } + + protected int readUnsignedShort(ByteBuffer buffer) + { + return buffer.getUnsignedShort(); //To change body of created methods use File | Settings | File Templates. + } + + protected void writeBitfield(ByteBuffer buffer, byte bitfield0) + { + buffer.put(bitfield0); + } + + protected void writeUnsignedShort(ByteBuffer buffer, int s) + { + EncodingUtils.writeUnsignedShort(buffer, s); + } + + protected long readUnsignedInteger(ByteBuffer buffer) + { + return buffer.getUnsignedInt(); + } + protected void writeUnsignedInteger(ByteBuffer buffer, long i) + { + EncodingUtils.writeUnsignedInteger(buffer, i); + } + + + protected short readUnsignedByte(ByteBuffer buffer) + { + return buffer.getUnsigned(); + } + + protected void writeUnsignedByte(ByteBuffer buffer, short unsignedByte) + { + EncodingUtils.writeUnsignedByte(buffer, unsignedByte); + } + + protected long readTimestamp(ByteBuffer buffer) + { + return EncodingUtils.readTimestamp(buffer); + } + + protected void writeTimestamp(ByteBuffer buffer, long t) + { + EncodingUtils.writeTimestamp(buffer, t); + } } diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java b/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java index 1ff39ca790..647d531476 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java +++ b/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java @@ -22,6 +22,10 @@ package org.apache.qpid.framing; import org.apache.mina.common.ByteBuffer; +import java.util.Date; +import java.util.Map; +import java.math.BigDecimal; + /** * AMQTypedValue combines together a native Java Object value, and an {@link AMQType}, as a fully typed AMQP parameter * value. It provides the ability to read and write fully typed parameters to and from byte buffers. It also provides @@ -113,4 +117,63 @@ public class AMQTypedValue return _type.hashCode() ^ (_value == null ? 0 : _value.hashCode()); } + + public static AMQTypedValue toTypedValue(Object val) + { + if(val == null) + { + return AMQType.VOID.asTypedValue(null); + } + + Class klass = val.getClass(); + if(klass == String.class) + { + return AMQType.ASCII_STRING.asTypedValue(val); + } + else if(klass == Character.class) + { + return AMQType.ASCII_CHARACTER.asTypedValue(val); + } + else if(klass == Integer.class) + { + return AMQType.INT.asTypedValue(val); + } + else if(klass == Long.class) + { + return AMQType.LONG.asTypedValue(val); + } + else if(klass == Float.class) + { + return AMQType.FLOAT.asTypedValue(val); + } + else if(klass == Double.class) + { + return AMQType.DOUBLE.asTypedValue(val); + } + else if(klass == Date.class) + { + return AMQType.TIMESTAMP.asTypedValue(val); + } + else if(klass == Byte.class) + { + return AMQType.BYTE.asTypedValue(val); + } + else if(klass == Boolean.class) + { + return AMQType.BOOLEAN.asTypedValue(val); + } + else if(klass == byte[].class) + { + return AMQType.BINARY.asTypedValue(val); + } + else if(klass == BigDecimal.class) + { + return AMQType.DECIMAL.asTypedValue(val); + } + else if(val instanceof Map) + { + return AMQType.FIELD_TABLE.asTypedValue(FieldTable.convertToFieldTable((Map)val)); + } + return null; + } } diff --git a/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java b/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java index 47b5c02beb..c7d89a9927 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java +++ b/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java @@ -27,6 +27,10 @@ import org.slf4j.LoggerFactory; public class BasicContentHeaderProperties implements CommonContentHeaderProperties { + //persistent & non-persistent constants, values as per JMS DeliveryMode + public static final int NON_PERSISTENT = 1; + public static final int PERSISTENT = 2; + private static final Logger _logger = LoggerFactory.getLogger(BasicContentHeaderProperties.class); private static final AMQShortString ZERO_STRING = null; diff --git a/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java b/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java index ed01c91804..341238c667 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java +++ b/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java @@ -131,9 +131,10 @@ public class FieldTable } else if ((_encodedForm != null) && (val != null)) { - EncodingUtils.writeShortStringBytes(_encodedForm, key); - val.writeToBuffer(_encodedForm); - + // We have updated data to store in the buffer + // So clear the _encodedForm to allow it to be rebuilt later + // this is safer than simply appending to any existing buffer. + _encodedForm = null; } else if (val == null) { @@ -828,6 +829,7 @@ public class FieldTable recalculateEncodedSize(); } + public static interface FieldTableElementProcessor { public boolean processElement(String propertyName, AMQTypedValue value); @@ -904,10 +906,13 @@ public class FieldTable } } + public Object get(String key) + { + return get(new AMQShortString(key)); + } public Object get(AMQShortString key) { - return getObject(key); } @@ -1184,4 +1189,24 @@ public class FieldTable return _properties.equals(f._properties); } + + public static FieldTable convertToFieldTable(Map<String, Object> map) + { + if (map != null) + { + FieldTable table = new FieldTable(); + for(Map.Entry<String,Object> entry : map.entrySet()) + { + table.put(new AMQShortString(entry.getKey()), entry.getValue()); + } + + return table; + } + else + { + return null; + } + } + + } diff --git a/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java b/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java index 3ac17e9204..ac21fe4243 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java +++ b/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java @@ -20,12 +20,10 @@ */ 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; +import java.nio.ByteBuffer; public class ProtocolInitiation extends AMQDataBlock implements EncodableAMQDataBlock { @@ -53,13 +51,16 @@ public class ProtocolInitiation extends AMQDataBlock implements EncodableAMQData _protocolMajor = protocolMajor; _protocolMinor = protocolMinor; } - + public ProtocolInitiation(ProtocolVersion pv) { - this(AMQP_HEADER, CURRENT_PROTOCOL_CLASS, TCP_PROTOCOL_INSTANCE, pv.getMajorVersion(), pv.getMinorVersion()); + this(AMQP_HEADER, + pv.equals(ProtocolVersion.v0_91) ? 0 : CURRENT_PROTOCOL_CLASS, + pv.equals(ProtocolVersion.v0_91) ? 0 : TCP_PROTOCOL_INSTANCE, + pv.equals(ProtocolVersion.v0_91) ? 9 : pv.getMajorVersion(), + pv.equals(ProtocolVersion.v0_91) ? 1 : pv.getMinorVersion()); } - public ProtocolInitiation(ByteBuffer in) { _protocolHeader = new byte[4]; @@ -71,6 +72,11 @@ public class ProtocolInitiation extends AMQDataBlock implements EncodableAMQData _protocolMinor = in.get(); } + public void writePayload(org.apache.mina.common.ByteBuffer buffer) + { + writePayload(buffer.buf()); + } + public long getSize() { return 4 + 1 + 1 + 1 + 1; @@ -122,21 +128,15 @@ public class ProtocolInitiation extends AMQDataBlock implements EncodableAMQData { /** * - * @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) + public boolean decodable(ByteBuffer in) { return (in.remaining() >= 8); } - public void decode(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) - { - ProtocolInitiation pi = new ProtocolInitiation(in); - out.write(pi); - } } public ProtocolVersion checkVersion() throws AMQException @@ -160,18 +160,33 @@ public class ProtocolInitiation extends AMQDataBlock implements EncodableAMQData } } } - if (_protocolClass != CURRENT_PROTOCOL_CLASS) + + ProtocolVersion pv; + + // Hack for 0-9-1 which changed how the header was defined + if(_protocolInstance == 0 && _protocolMajor == 9 && _protocolMinor == 1) + { + pv = ProtocolVersion.v0_91; + if (_protocolClass != 0) + { + throw new AMQProtocolClassException("Protocol class " + 0 + " was expected; received " + + _protocolClass, null); + } + } + else if (_protocolClass != CURRENT_PROTOCOL_CLASS) { throw new AMQProtocolClassException("Protocol class " + CURRENT_PROTOCOL_CLASS + " was expected; received " + _protocolClass, null); } - if (_protocolInstance != TCP_PROTOCOL_INSTANCE) + else if (_protocolInstance != TCP_PROTOCOL_INSTANCE) { throw new AMQProtocolInstanceException("Protocol instance " + TCP_PROTOCOL_INSTANCE + " was expected; received " + _protocolInstance, null); } - - ProtocolVersion pv = new ProtocolVersion(_protocolMajor, _protocolMinor); + else + { + pv = new ProtocolVersion(_protocolMajor, _protocolMinor); + } if (!pv.isSupported()) @@ -192,4 +207,5 @@ public class ProtocolInitiation extends AMQDataBlock implements EncodableAMQData buffer.append(Integer.toHexString(_protocolMinor)); return buffer.toString(); } + } diff --git a/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoImpl.java b/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoImpl.java new file mode 100644 index 0000000000..e3d5da73da --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoImpl.java @@ -0,0 +1,85 @@ +/* + * + * 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.abstraction.MessagePublishInfo; +import org.apache.qpid.framing.AMQShortString; + +public class MessagePublishInfoImpl implements MessagePublishInfo +{ + private AMQShortString _exchange; + private boolean _immediate; + private boolean _mandatory; + private AMQShortString _routingKey; + + public MessagePublishInfoImpl() + { + } + + public MessagePublishInfoImpl(AMQShortString exchange, boolean immediate, boolean mandatory, + AMQShortString routingKey) + { + _exchange = exchange; + _immediate = immediate; + _mandatory = mandatory; + _routingKey = routingKey; + } + + public AMQShortString getExchange() + { + return _exchange; + } + + public void setExchange(AMQShortString exchange) + { + _exchange = exchange; + } + + public boolean isImmediate() + { + return _immediate; + } + + public void setImmediate(boolean immedate) + { + _immediate = immedate; + } + + public boolean isMandatory() + { + return _mandatory; + } + + public void setMandatory(boolean mandatory) + { + _mandatory = mandatory; + } + + public AMQShortString getRoutingKey() + { + return _routingKey; + } + + public void setRoutingKey(AMQShortString routingKey) + { + _routingKey = routingKey; + } +} diff --git a/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java b/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java index 0a1cedc4e6..7544d9b7e7 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java +++ b/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java @@ -23,10 +23,14 @@ package org.apache.qpid.framing.abstraction; import org.apache.qpid.framing.AMQBody; +import java.nio.ByteBuffer; + public interface ProtocolVersionMethodConverter extends MessagePublishInfoConverter { AMQBody convertToBody(ContentChunk contentBody); ContentChunk convertToContentChunk(AMQBody body); void configure(); + + AMQBody convertToBody(ByteBuffer buf); } diff --git a/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/AMQMethodBody_0_9.java b/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/AMQMethodBody_0_9.java index 948f5baaf6..8d51343507 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/AMQMethodBody_0_9.java +++ b/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/AMQMethodBody_0_9.java @@ -21,14 +21,6 @@ package org.apache.qpid.framing.amqp_0_9; -import org.apache.qpid.framing.EncodingUtils; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.framing.AMQFrameDecodingException; -import org.apache.qpid.framing.Content; - -import org.apache.mina.common.ByteBuffer; - public abstract class AMQMethodBody_0_9 extends org.apache.qpid.framing.AMQMethodBodyImpl { @@ -40,170 +32,6 @@ public abstract class AMQMethodBody_0_9 extends org.apache.qpid.framing.AMQMetho public byte getMinor() { return 9; - } - - public int getSize() - { - return 2 + 2 + getBodySize(); - } - - public void writePayload(ByteBuffer buffer) - { - EncodingUtils.writeUnsignedShort(buffer, getClazz()); - EncodingUtils.writeUnsignedShort(buffer, getMethod()); - writeMethodPayload(buffer); - } - - - protected byte readByte(ByteBuffer buffer) - { - return buffer.get(); - } - - protected AMQShortString readAMQShortString(ByteBuffer buffer) - { - return EncodingUtils.readAMQShortString(buffer); - } - - protected int getSizeOf(AMQShortString string) - { - return EncodingUtils.encodedShortStringLength(string); - } - - protected void writeByte(ByteBuffer buffer, byte b) - { - buffer.put(b); - } - - protected void writeAMQShortString(ByteBuffer buffer, AMQShortString string) - { - EncodingUtils.writeShortStringBytes(buffer, string); - } - - protected int readInt(ByteBuffer buffer) - { - return buffer.getInt(); - } - - protected void writeInt(ByteBuffer buffer, int i) - { - buffer.putInt(i); - } - - protected FieldTable readFieldTable(ByteBuffer buffer) throws AMQFrameDecodingException - { - return EncodingUtils.readFieldTable(buffer); - } - - protected int getSizeOf(FieldTable table) - { - return EncodingUtils.encodedFieldTableLength(table); //To change body of created methods use File | Settings | File Templates. - } - - protected void writeFieldTable(ByteBuffer buffer, FieldTable table) - { - EncodingUtils.writeFieldTableBytes(buffer, table); - } - - protected long readLong(ByteBuffer buffer) - { - return buffer.getLong(); - } - - protected void writeLong(ByteBuffer buffer, long l) - { - buffer.putLong(l); - } - - protected int getSizeOf(byte[] response) - { - return (response == null) ? 4 :response.length + 4; - } - - protected void writeBytes(ByteBuffer buffer, byte[] data) - { - EncodingUtils.writeBytes(buffer,data); - } - - protected byte[] readBytes(ByteBuffer buffer) - { - return EncodingUtils.readBytes(buffer); - } - - protected short readShort(ByteBuffer buffer) - { - return EncodingUtils.readShort(buffer); - } - - protected void writeShort(ByteBuffer buffer, short s) - { - EncodingUtils.writeShort(buffer, s); - } - - protected Content readContent(ByteBuffer buffer) - { - return null; //To change body of created methods use File | Settings | File Templates. - } - - protected int getSizeOf(Content body) - { - return 0; //To change body of created methods use File | Settings | File Templates. - } - - protected void writeContent(ByteBuffer buffer, Content body) - { - //To change body of created methods use File | Settings | File Templates. - } - - protected byte readBitfield(ByteBuffer buffer) - { - return readByte(buffer); //To change body of created methods use File | Settings | File Templates. - } - - protected int readUnsignedShort(ByteBuffer buffer) - { - return buffer.getUnsignedShort(); //To change body of created methods use File | Settings | File Templates. - } - - protected void writeBitfield(ByteBuffer buffer, byte bitfield0) - { - buffer.put(bitfield0); - } - - protected void writeUnsignedShort(ByteBuffer buffer, int s) - { - EncodingUtils.writeUnsignedShort(buffer, s); - } - - protected long readUnsignedInteger(ByteBuffer buffer) - { - return buffer.getUnsignedInt(); - } - protected void writeUnsignedInteger(ByteBuffer buffer, long i) - { - EncodingUtils.writeUnsignedInteger(buffer, i); - } - - - protected short readUnsignedByte(ByteBuffer buffer) - { - return buffer.getUnsigned(); - } - - protected void writeUnsignedByte(ByteBuffer buffer, short unsignedByte) - { - EncodingUtils.writeUnsignedByte(buffer, unsignedByte); - } - - protected long readTimestamp(ByteBuffer buffer) - { - return EncodingUtils.readTimestamp(buffer); - } - - protected void writeTimestamp(ByteBuffer buffer, long t) - { - EncodingUtils.writeTimestamp(buffer, t); - } - + } } diff --git a/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java b/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java index fd5195eb6b..1c4a29b106 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java +++ b/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java @@ -27,6 +27,7 @@ import org.apache.qpid.framing.abstraction.AbstractMethodConverter; 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.MessagePublishInfoImpl; import org.apache.qpid.framing.*; import org.apache.qpid.framing.amqp_0_9.*; import org.apache.qpid.framing.amqp_0_9.BasicPublishBodyImpl; @@ -71,6 +72,11 @@ public class MethodConverter_0_9 extends AbstractMethodConverter implements Prot } + public AMQBody convertToBody(java.nio.ByteBuffer buf) + { + return new ContentBody(ByteBuffer.wrap(buf)); + } + public MessagePublishInfo convertToInfo(AMQMethodBody methodBody) { final BasicPublishBody publishBody = ((BasicPublishBody) methodBody); @@ -78,7 +84,7 @@ public class MethodConverter_0_9 extends AbstractMethodConverter implements Prot final AMQShortString exchange = publishBody.getExchange(); final AMQShortString routingKey = publishBody.getRoutingKey(); - return new MethodConverter_0_9.MessagePublishInfoImpl(exchange, + return new MessagePublishInfoImpl(exchange, publishBody.getImmediate(), publishBody.getMandatory(), routingKey); @@ -96,50 +102,6 @@ public class MethodConverter_0_9 extends AbstractMethodConverter implements Prot } - private static class MessagePublishInfoImpl implements MessagePublishInfo - { - private AMQShortString _exchange; - private final boolean _immediate; - private final boolean _mandatory; - private final AMQShortString _routingKey; - - public MessagePublishInfoImpl(final AMQShortString exchange, - final boolean immediate, - final boolean mandatory, - final AMQShortString routingKey) - { - _exchange = exchange; - _immediate = immediate; - _mandatory = mandatory; - _routingKey = routingKey; - } - - public AMQShortString getExchange() - { - return _exchange; - } - - public void setExchange(AMQShortString exchange) - { - _exchange = exchange; - } - - public boolean isImmediate() - { - return _immediate; - } - - public boolean isMandatory() - { - return _mandatory; - } - - public AMQShortString getRoutingKey() - { - return _routingKey; - } - } - private static class ContentChunk_0_9 implements ContentChunk { private final ContentBody _contentBodyChunk; diff --git a/java/common/src/main/java/org/apache/qpid/framing/amqp_0_91/AMQMethodBody_0_91.java b/java/common/src/main/java/org/apache/qpid/framing/amqp_0_91/AMQMethodBody_0_91.java new file mode 100644 index 0000000000..60b8a7e1a6 --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/framing/amqp_0_91/AMQMethodBody_0_91.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.framing.amqp_0_91; + +public abstract class AMQMethodBody_0_91 extends org.apache.qpid.framing.AMQMethodBodyImpl +{ + + public byte getMajor() + { + return 0; + } + + public byte getMinor() + { + return 91; + } + +}
\ No newline at end of file diff --git a/java/common/src/main/java/org/apache/qpid/framing/amqp_0_91/MethodConverter_0_91.java b/java/common/src/main/java/org/apache/qpid/framing/amqp_0_91/MethodConverter_0_91.java new file mode 100644 index 0000000000..6e330574bc --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/framing/amqp_0_91/MethodConverter_0_91.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.amqp_0_91; + +import org.apache.mina.common.ByteBuffer; + +import org.apache.qpid.framing.abstraction.AbstractMethodConverter; +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.MessagePublishInfoImpl; +import org.apache.qpid.framing.*; + +public class MethodConverter_0_91 extends AbstractMethodConverter implements ProtocolVersionMethodConverter +{ + private int _basicPublishClassId; + private int _basicPublishMethodId; + + public MethodConverter_0_91() + { + super((byte)0,(byte)9); + + + } + + public AMQBody convertToBody(ContentChunk contentChunk) + { + if(contentChunk instanceof ContentChunk_0_9) + { + return ((ContentChunk_0_9)contentChunk).toBody(); + } + else + { + return new ContentBody(contentChunk.getData()); + } + } + + public ContentChunk convertToContentChunk(AMQBody body) + { + final ContentBody contentBodyChunk = (ContentBody) body; + + return new ContentChunk_0_9(contentBodyChunk); + + } + + public void configure() + { + + _basicPublishClassId = BasicPublishBodyImpl.CLASS_ID; + _basicPublishMethodId = BasicPublishBodyImpl.METHOD_ID; + + } + + public AMQBody convertToBody(java.nio.ByteBuffer buf) + { + return new ContentBody(ByteBuffer.wrap(buf)); + } + + public MessagePublishInfo convertToInfo(AMQMethodBody methodBody) + { + final BasicPublishBody publishBody = ((BasicPublishBody) methodBody); + + final AMQShortString exchange = publishBody.getExchange(); + final AMQShortString routingKey = publishBody.getRoutingKey(); + + return new MessagePublishInfoImpl(exchange, + publishBody.getImmediate(), + publishBody.getMandatory(), + routingKey); + + } + + public AMQMethodBody convertToBody(MessagePublishInfo info) + { + + return new BasicPublishBodyImpl(0, + info.getExchange(), + info.getRoutingKey(), + info.isMandatory(), + info.isImmediate()) ; + + } + + private static class ContentChunk_0_9 implements ContentChunk + { + private final ContentBody _contentBodyChunk; + + public ContentChunk_0_9(final ContentBody contentBodyChunk) + { + _contentBodyChunk = contentBodyChunk; + } + + public int getSize() + { + return _contentBodyChunk.getSize(); + } + + public ByteBuffer getData() + { + return _contentBodyChunk.payload; + } + + public void reduceToFit() + { + _contentBodyChunk.reduceBufferToFit(); + } + + public AMQBody toBody() + { + return _contentBodyChunk; + } + } +}
\ No newline at end of file diff --git a/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/AMQMethodBody_8_0.java b/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/AMQMethodBody_8_0.java index e9b4447140..35645854c0 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/AMQMethodBody_8_0.java +++ b/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/AMQMethodBody_8_0.java @@ -21,14 +21,6 @@ package org.apache.qpid.framing.amqp_8_0; -import org.apache.qpid.framing.EncodingUtils; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.framing.AMQFrameDecodingException; -import org.apache.qpid.framing.Content; - -import org.apache.mina.common.ByteBuffer; - public abstract class AMQMethodBody_8_0 extends org.apache.qpid.framing.AMQMethodBodyImpl { @@ -42,168 +34,7 @@ public abstract class AMQMethodBody_8_0 extends org.apache.qpid.framing.AMQMetho return 0; } - public int getSize() - { - return 2 + 2 + getBodySize(); - } - - public void writePayload(ByteBuffer buffer) - { - EncodingUtils.writeUnsignedShort(buffer, getClazz()); - EncodingUtils.writeUnsignedShort(buffer, getMethod()); - writeMethodPayload(buffer); - } - - protected byte readByte(ByteBuffer buffer) - { - return buffer.get(); - } - - protected AMQShortString readAMQShortString(ByteBuffer buffer) - { - return EncodingUtils.readAMQShortString(buffer); - } - - protected int getSizeOf(AMQShortString string) - { - return EncodingUtils.encodedShortStringLength(string); - } - - protected void writeByte(ByteBuffer buffer, byte b) - { - buffer.put(b); - } - - protected void writeAMQShortString(ByteBuffer buffer, AMQShortString string) - { - EncodingUtils.writeShortStringBytes(buffer, string); - } - - protected int readInt(ByteBuffer buffer) - { - return buffer.getInt(); - } - - protected void writeInt(ByteBuffer buffer, int i) - { - buffer.putInt(i); - } - - protected FieldTable readFieldTable(ByteBuffer buffer) throws AMQFrameDecodingException - { - return EncodingUtils.readFieldTable(buffer); - } - - protected int getSizeOf(FieldTable table) - { - return EncodingUtils.encodedFieldTableLength(table); //To change body of created methods use File | Settings | File Templates. - } - - protected void writeFieldTable(ByteBuffer buffer, FieldTable table) - { - EncodingUtils.writeFieldTableBytes(buffer, table); - } - - protected long readLong(ByteBuffer buffer) - { - return buffer.getLong(); - } - - protected void writeLong(ByteBuffer buffer, long l) - { - buffer.putLong(l); - } - - protected int getSizeOf(byte[] response) - { - return (response == null) ? 4 : response.length + 4; - } - - protected void writeBytes(ByteBuffer buffer, byte[] data) - { - EncodingUtils.writeBytes(buffer,data); - } - - protected byte[] readBytes(ByteBuffer buffer) - { - return EncodingUtils.readBytes(buffer); - } - - protected short readShort(ByteBuffer buffer) - { - return EncodingUtils.readShort(buffer); - } - - protected void writeShort(ByteBuffer buffer, short s) - { - EncodingUtils.writeShort(buffer, s); - } - - protected Content readContent(ByteBuffer buffer) - { - return null; //To change body of created methods use File | Settings | File Templates. - } - - protected int getSizeOf(Content body) - { - return 0; //To change body of created methods use File | Settings | File Templates. - } - - protected void writeContent(ByteBuffer buffer, Content body) - { - //To change body of created methods use File | Settings | File Templates. - } - - protected byte readBitfield(ByteBuffer buffer) - { - return readByte(buffer); //To change body of created methods use File | Settings | File Templates. - } - - protected int readUnsignedShort(ByteBuffer buffer) - { - return buffer.getUnsignedShort(); //To change body of created methods use File | Settings | File Templates. - } - - protected void writeBitfield(ByteBuffer buffer, byte bitfield0) - { - buffer.put(bitfield0); - } - - protected void writeUnsignedShort(ByteBuffer buffer, int s) - { - EncodingUtils.writeUnsignedShort(buffer, s); - } - - protected long readUnsignedInteger(ByteBuffer buffer) - { - return buffer.getUnsignedInt(); - } - protected void writeUnsignedInteger(ByteBuffer buffer, long i) - { - EncodingUtils.writeUnsignedInteger(buffer, i); - } - - - protected short readUnsignedByte(ByteBuffer buffer) - { - return buffer.getUnsigned(); - } - - protected void writeUnsignedByte(ByteBuffer buffer, short unsignedByte) - { - EncodingUtils.writeUnsignedByte(buffer, unsignedByte); - } - - protected long readTimestamp(ByteBuffer buffer) - { - return EncodingUtils.readTimestamp(buffer); - } - - protected void writeTimestamp(ByteBuffer buffer, long t) - { - EncodingUtils.writeTimestamp(buffer, t); - } } diff --git a/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java b/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java index 299c655698..c87820b9b2 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java +++ b/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java @@ -25,6 +25,7 @@ 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.qpid.framing.abstraction.MessagePublishInfoImpl; import org.apache.qpid.framing.amqp_8_0.BasicPublishBodyImpl; import org.apache.qpid.framing.*; @@ -79,6 +80,11 @@ public class MethodConverter_8_0 extends AbstractMethodConverter implements Prot _basicPublishMethodId = BasicPublishBodyImpl.METHOD_ID; } + + public AMQBody convertToBody(java.nio.ByteBuffer buf) + { + return new ContentBody(ByteBuffer.wrap(buf)); + } public MessagePublishInfo convertToInfo(AMQMethodBody methodBody) { @@ -104,48 +110,4 @@ public class MethodConverter_8_0 extends AbstractMethodConverter implements Prot info.isImmediate()) ; } - - private static class MessagePublishInfoImpl implements MessagePublishInfo - { - private AMQShortString _exchange; - private final boolean _immediate; - private final boolean _mandatory; - private final AMQShortString _routingKey; - - public MessagePublishInfoImpl(final AMQShortString exchange, - final boolean immediate, - final boolean mandatory, - final AMQShortString routingKey) - { - _exchange = exchange; - _immediate = immediate; - _mandatory = mandatory; - _routingKey = routingKey; - } - - public AMQShortString getExchange() - { - return _exchange; - } - - public void setExchange(AMQShortString exchange) - { - _exchange = exchange; - } - - public boolean isImmediate() - { - return _immediate; - } - - public boolean isMandatory() - { - return _mandatory; - } - - public AMQShortString getRoutingKey() - { - return _routingKey; - } - } } diff --git a/java/common/src/main/java/org/apache/qpid/pool/Event.java b/java/common/src/main/java/org/apache/qpid/pool/Event.java deleted file mode 100644 index 5996cbf89c..0000000000 --- a/java/common/src/main/java/org/apache/qpid/pool/Event.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.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/java/common/src/main/java/org/apache/qpid/pool/Job.java b/java/common/src/main/java/org/apache/qpid/pool/Job.java index 00da005515..82b600de88 100644 --- a/java/common/src/main/java/org/apache/qpid/pool/Job.java +++ b/java/common/src/main/java/org/apache/qpid/pool/Job.java @@ -21,9 +21,12 @@ package org.apache.qpid.pool; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicBoolean; -import org.apache.mina.common.IoSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A Job is a continuation that batches together other continuations, specifically {@link Event}s, into one continuation. @@ -52,35 +55,28 @@ import org.apache.mina.common.IoSession; */ public class Job implements ReadWriteRunnable { + + /** Defines the maximum number of events that will be batched into a single job. */ + public static final int MAX_JOB_EVENTS = Integer.getInteger("amqj.server.read_write_pool.max_events", 10); + /** 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>(); + private final java.util.Queue<Runnable> _eventQueue = new ConcurrentLinkedQueue<Runnable>(); /** 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; - private final boolean _readJob; - /** - * 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. - * @param readJob - */ - Job(IoSession session, JobCompletionHandler completionHandler, int maxEvents, final boolean readJob) + private ReferenceCountingExecutorService _poolReference; + + private final static Logger _logger = LoggerFactory.getLogger(Job.class); + + public Job(ReferenceCountingExecutorService poolReference, int maxEvents, boolean readJob) { - _session = session; - _completionHandler = completionHandler; + _poolReference = poolReference; _maxEvents = maxEvents; _readJob = readJob; } @@ -90,7 +86,7 @@ public class Job implements ReadWriteRunnable * * @param evt The continuation to enqueue. */ - void add(Event evt) + public void add(Runnable evt) { _eventQueue.add(evt); } @@ -104,14 +100,14 @@ public class Job implements ReadWriteRunnable int i = _maxEvents; while( --i != 0 ) { - Event e = _eventQueue.poll(); + Runnable e = _eventQueue.poll(); if (e == null) { return true; } else { - e.process(_session); + e.run(); } } return false; @@ -153,40 +149,105 @@ public class Job implements ReadWriteRunnable if(processAll()) { deactivate(); - _completionHandler.completed(_session, this); + completed(); } else { - _completionHandler.notCompleted(_session, this); + notCompleted(); } } - public boolean isReadJob() - { - return _readJob; - } - public boolean isRead() { return _readJob; } - - public boolean isWrite() + + /** + * Adds an {@link Event} to a {@link Job}, triggering the execution of the job if it is not already running. + * + * @param job The job. + * @param event The event to hand off asynchronously. + */ + public static void fireAsynchEvent(ExecutorService pool, Job job, Runnable event) { - return !_readJob; - } + job.add(event); + + + if(pool == null) + { + return; + } + + // rather than perform additional checks on pool to check that it hasn't shutdown. + // catch the RejectedExecutionException that will result from executing on a shutdown pool + if (job.activate()) + { + try + { + pool.execute(job); + } + catch(RejectedExecutionException e) + { + _logger.warn("Thread pool shutdown while tasks still outstanding"); + } + } + } + /** - * Another interface for a continuation. + * 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. * - * @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. + * @param session The Mina session to work in. + * @param job The job that completed. */ - static interface JobCompletionHandler + public void completed() + { + if (!isComplete()) + { + final ExecutorService pool = _poolReference.getPool(); + + if(pool == null) + { + return; + } + + + // ritchiem : 2006-12-13 Do we need to perform the additional checks here? + // Can the pool be shutdown at this point? + if (activate()) + { + try + { + pool.execute(this); + } + catch(RejectedExecutionException e) + { + _logger.warn("Thread pool shutdown while tasks still outstanding"); + } + + } + } + } + + public void notCompleted() { - public void completed(IoSession session, Job job); + final ExecutorService pool = _poolReference.getPool(); + + if(pool == null) + { + return; + } - public void notCompleted(final IoSession session, final Job job); + try + { + pool.execute(this); + } + catch(RejectedExecutionException e) + { + _logger.warn("Thread pool shutdown while tasks still outstanding"); + } } + } diff --git a/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java b/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java deleted file mode 100644 index a080cc7e04..0000000000 --- a/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java +++ /dev/null @@ -1,491 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.pool; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -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.RejectedExecutionException; -import java.util.concurrent.ExecutorService; - -/** - * 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 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 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. */ - static final int MAX_JOB_EVENTS = Integer.getInteger("amqj.server.read_write_pool.max_events", 10); - - private final int _maxEvents; - - private final boolean _readFilter; - - /** - * 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, int maxEvents, boolean readFilter) - { - _poolReference = refCountingPool; - _name = name; - _maxEvents = maxEvents; - _readFilter = readFilter; - } - - /** - * 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 job The job. - * @param event The event to hand off asynchronously. - */ - void fireAsynchEvent(Job job, Event event) - { - - job.add(event); - - final ExecutorService pool = _poolReference.getPool(); - - if(pool == null) - { - return; - } - - // rather than perform additional checks on pool to check that it hasn't shutdown. - // catch the RejectedExecutionException that will result from executing on a shutdown pool - if (job.activate()) - { - try - { - pool.execute(job); - } - catch(RejectedExecutionException e) - { - _logger.warn("Thread pool shutdown while tasks still outstanding"); - } - } - - } - - /** - * 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, MAX_JOB_EVENTS,_readFilter); - 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. - */ - public Job getJobForSession(IoSession session) - { - return (Job) session.getAttribute(_name); - } - - /** - * 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()) - { - final ExecutorService pool = _poolReference.getPool(); - - if(pool == null) - { - return; - } - - - // ritchiem : 2006-12-13 Do we need to perform the additional checks here? - // Can the pool be shutdown at this point? - if (job.activate()) - { - try - { - pool.execute(job); - } - catch(RejectedExecutionException e) - { - _logger.warn("Thread pool shutdown while tasks still outstanding"); - } - - } - } - } - - public void notCompleted(IoSession session, Job job) - { - final ExecutorService pool = _poolReference.getPool(); - - if(pool == null) - { - return; - } - - try - { - pool.execute(job); - } - catch(RejectedExecutionException e) - { - _logger.warn("Thread pool shutdown while tasks still outstanding"); - } - - } - - - - /** - * 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, Integer.getInteger("amqj.server.read_write_pool.max_read_events", MAX_JOB_EVENTS),true); - } - - /** - * 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) - { - Job job = getJobForSession(session); - fireAsynchEvent(job, 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) - { - Job job = getJobForSession(session); - fireAsynchEvent(job, 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, Integer.getInteger("amqj.server.read_write_pool.max_write_events", MAX_JOB_EVENTS),false); - } - - /** - * 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) - { - Job job = getJobForSession(session); - fireAsynchEvent(job, 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) - { - Job job = getJobForSession(session); - fireAsynchEvent(job, new CloseEvent(nextFilter)); - } - } -} diff --git a/java/common/src/main/java/org/apache/qpid/pool/ReadWriteRunnable.java b/java/common/src/main/java/org/apache/qpid/pool/ReadWriteRunnable.java index ad04a923e1..140c93ca8d 100644 --- a/java/common/src/main/java/org/apache/qpid/pool/ReadWriteRunnable.java +++ b/java/common/src/main/java/org/apache/qpid/pool/ReadWriteRunnable.java @@ -23,5 +23,4 @@ package org.apache.qpid.pool; public interface ReadWriteRunnable extends Runnable { boolean isRead(); - boolean isWrite(); } diff --git a/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.java b/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.java deleted file mode 100644 index 8cea70e597..0000000000 --- a/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.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/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java b/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java index ce9c6ae4cb..20a30b3ed3 100644 --- a/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java +++ b/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java @@ -160,4 +160,13 @@ public class ReferenceCountingExecutorService { return _pool; } + + /** + * Return the ReferenceCount to this ExecutorService + * @return reference count + */ + public int getReferenceCount() + { + return _refCount; + } } diff --git a/java/common/src/main/java/org/apache/qpid/protocol/ProtocolEngine.java b/java/common/src/main/java/org/apache/qpid/protocol/ProtocolEngine.java new file mode 100644 index 0000000000..31953ea6ab --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/protocol/ProtocolEngine.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.protocol; + +import java.net.SocketAddress; + +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.transport.NetworkDriver; +import org.apache.qpid.transport.Receiver; + +/** + * A ProtocolEngine is a Receiver for java.nio.ByteBuffers. It takes the data passed to it in the received + * decodes it and then process the result. + */ +public interface ProtocolEngine extends Receiver<java.nio.ByteBuffer> +{ + // Sets the network driver providing data for this ProtocolEngine + void setNetworkDriver (NetworkDriver driver); + + // Returns the remote address of the NetworkDriver + SocketAddress getRemoteAddress(); + + // Returns the local address of the NetworkDriver + SocketAddress getLocalAddress(); + + // Returns number of bytes written + long getWrittenBytes(); + + // Returns number of bytes read + long getReadBytes(); + + // Called by the NetworkDriver when the socket has been closed for reading + void closed(); + + // Called when the NetworkEngine has not written data for the specified period of time (will trigger a + // heartbeat) + void writerIdle(); + + // Called when the NetworkEngine has not read data for the specified period of time (will close the connection) + void readerIdle(); + + +}
\ No newline at end of file diff --git a/java/common/src/main/java/org/apache/qpid/protocol/ProtocolEngineFactory.java b/java/common/src/main/java/org/apache/qpid/protocol/ProtocolEngineFactory.java new file mode 100644 index 0000000000..9df84eef90 --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/protocol/ProtocolEngineFactory.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.protocol; + +import org.apache.qpid.transport.NetworkDriver; + +public interface ProtocolEngineFactory +{ + + // Returns a new instance of a ProtocolEngine + ProtocolEngine newProtocolEngine(NetworkDriver networkDriver); + +}
\ No newline at end of file diff --git a/java/common/src/main/java/org/apache/qpid/security/CallbackHandlerRegistry.java b/java/common/src/main/java/org/apache/qpid/security/CallbackHandlerRegistry.java deleted file mode 100644 index 8c80a1b5b7..0000000000 --- a/java/common/src/main/java/org/apache/qpid/security/CallbackHandlerRegistry.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.security; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.qpid.QpidConfig; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class CallbackHandlerRegistry -{ - private static final Logger _logger = LoggerFactory.getLogger(CallbackHandlerRegistry.class); - private static CallbackHandlerRegistry _instance = new CallbackHandlerRegistry(); - - private Map<String,Class> _mechanismToHandlerClassMap = new HashMap<String,Class>(); - - private StringBuilder _mechanisms; - - public static CallbackHandlerRegistry getInstance() - { - return _instance; - } - - public Class getCallbackHandlerClass(String mechanism) - { - return _mechanismToHandlerClassMap.get(mechanism); - } - - public String getMechanisms() - { - return _mechanisms.toString(); - } - - private CallbackHandlerRegistry() - { - // first we register any Sasl client factories - DynamicSaslRegistrar.registerSaslProviders(); - registerMechanisms(); - } - - private void registerMechanisms() - { - for (QpidConfig.SecurityMechanism securityMechanism: QpidConfig.get().getSecurityMechanisms() ) - { - Class clazz = null; - try - { - clazz = Class.forName(securityMechanism.getHandler()); - if (!AMQPCallbackHandler.class.isAssignableFrom(clazz)) - { - _logger.debug("SASL provider " + clazz + " does not implement " + AMQPCallbackHandler.class + - ". Skipping"); - continue; - } - _mechanismToHandlerClassMap.put(securityMechanism.getType(), clazz); - if (_mechanisms == null) - { - - _mechanisms = new StringBuilder(); - _mechanisms.append(securityMechanism.getType()); - } - else - { - _mechanisms.append(" " + securityMechanism.getType()); - } - } - catch (ClassNotFoundException ex) - { - _logger.debug("Unable to load class " + securityMechanism.getHandler() + ". Skipping that SASL provider"); - continue; - } - } - } -} diff --git a/java/common/src/main/java/org/apache/qpid/security/DynamicSaslRegistrar.java b/java/common/src/main/java/org/apache/qpid/security/DynamicSaslRegistrar.java deleted file mode 100644 index 9f48ac96a3..0000000000 --- a/java/common/src/main/java/org/apache/qpid/security/DynamicSaslRegistrar.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.security; - -import java.security.Security; -import java.util.Map; -import java.util.TreeMap; - -import javax.security.sasl.SaslClientFactory; - -import org.apache.qpid.QpidConfig; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class DynamicSaslRegistrar -{ - private static final Logger _logger = LoggerFactory.getLogger(DynamicSaslRegistrar.class); - - public static void registerSaslProviders() - { - Map<String, Class> factories = registerSaslClientFactories(); - if (factories.size() > 0) - { - Security.addProvider(new JCAProvider(factories)); - _logger.debug("Dynamic SASL provider added as a security provider"); - } - } - - private static Map<String, Class> registerSaslClientFactories() - { - TreeMap<String, Class> factoriesToRegister = - new TreeMap<String, Class>(); - - for (QpidConfig.SaslClientFactory factory: QpidConfig.get().getSaslClientFactories()) - { - String className = factory.getFactoryClass(); - try - { - Class clazz = Class.forName(className); - if (!(SaslClientFactory.class.isAssignableFrom(clazz))) - { - _logger.debug("Class " + clazz + " does not implement " + SaslClientFactory.class + " - skipping"); - continue; - } - factoriesToRegister.put(factory.getType(), clazz); - } - catch (Exception ex) - { - _logger.debug("Error instantiating SaslClientFactory calss " + className + " - skipping"); - } - } - return factoriesToRegister; - } - - -} diff --git a/java/common/src/main/java/org/apache/qpid/security/amqplain/AmqPlainSaslClient.java b/java/common/src/main/java/org/apache/qpid/security/amqplain/AmqPlainSaslClient.java deleted file mode 100644 index 81acc66de4..0000000000 --- a/java/common/src/main/java/org/apache/qpid/security/amqplain/AmqPlainSaslClient.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.security.amqplain; - -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.framing.FieldTableFactory; - -import javax.security.sasl.SaslClient; -import javax.security.sasl.SaslException; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.NameCallback; -import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.callback.Callback; - -/** - * Implements the "AMQPlain" authentication protocol that uses FieldTables to send username and pwd. - * - */ -public class AmqPlainSaslClient implements SaslClient -{ - /** - * The name of this mechanism - */ - public static final String MECHANISM = "AMQPLAIN"; - - private CallbackHandler _cbh; - - public AmqPlainSaslClient(CallbackHandler cbh) - { - _cbh = cbh; - } - - public String getMechanismName() - { - return "AMQPLAIN"; - } - - public boolean hasInitialResponse() - { - return true; - } - - public byte[] evaluateChallenge(byte[] challenge) throws SaslException - { - // we do not care about the prompt or the default name - NameCallback nameCallback = new NameCallback("prompt", "defaultName"); - PasswordCallback pwdCallback = new PasswordCallback("prompt", false); - Callback[] callbacks = new Callback[]{nameCallback, pwdCallback}; - try - { - _cbh.handle(callbacks); - } - catch (Exception e) - { - throw new SaslException("Error handling SASL callbacks: " + e, e); - } - FieldTable table = FieldTableFactory.newFieldTable(); - table.setString("LOGIN", nameCallback.getName()); - table.setString("PASSWORD", new String(pwdCallback.getPassword())); - return table.getDataAsBytes(); - } - - public boolean isComplete() - { - return true; - } - - public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException - { - throw new SaslException("Not supported"); - } - - public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException - { - throw new SaslException("Not supported"); - } - - public Object getNegotiatedProperty(String propName) - { - return null; - } - - public void dispose() throws SaslException - { - _cbh = null; - } -} diff --git a/java/common/src/main/java/org/apache/qpid/security/amqplain/AmqPlainSaslClientFactory.java b/java/common/src/main/java/org/apache/qpid/security/amqplain/AmqPlainSaslClientFactory.java deleted file mode 100644 index 6c66c87f4c..0000000000 --- a/java/common/src/main/java/org/apache/qpid/security/amqplain/AmqPlainSaslClientFactory.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.security.amqplain; - -import javax.security.sasl.SaslClientFactory; -import javax.security.sasl.SaslClient; -import javax.security.sasl.SaslException; -import javax.security.sasl.Sasl; -import javax.security.auth.callback.CallbackHandler; -import java.util.Map; - -public class AmqPlainSaslClientFactory implements SaslClientFactory -{ - public SaslClient createSaslClient(String[] mechanisms, String authorizationId, String protocol, String serverName, Map props, CallbackHandler cbh) throws SaslException - { - for (int i = 0; i < mechanisms.length; i++) - { - if (mechanisms[i].equals(AmqPlainSaslClient.MECHANISM)) - { - if (cbh == null) - { - throw new SaslException("CallbackHandler must not be null"); - } - return new AmqPlainSaslClient(cbh); - } - } - return null; - } - - public String[] getMechanismNames(Map props) - { - if (props.containsKey(Sasl.POLICY_NOPLAINTEXT) || - props.containsKey(Sasl.POLICY_NODICTIONARY) || - props.containsKey(Sasl.POLICY_NOACTIVE)) - { - // returned array must be non null according to interface documentation - return new String[0]; - } - else - { - return new String[]{AmqPlainSaslClient.MECHANISM}; - } - } -} diff --git a/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java b/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java index 950279fff1..e142d21e06 100644 --- a/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java +++ b/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java @@ -41,38 +41,74 @@ public class SSLContextFactory { /** * Path to the Java keystore file */ - private String _keystorePath; + private String _keyStorePath; /** * Password for the keystore */ - private String _keystorePassword; + private String _keyStorePassword; /** - * Cert type to use + * Cert type to use in keystore */ - private String _certType; + private String _keyStoreCertType; /** + * Path to the Java truststore file + */ + private String _trustStorePath; + + /** + * Password for the truststore + */ + private String _trustStorePassword; + + /** + * Cert type to use in truststore + */ + private String _trustStoreCertType; + + + + public SSLContextFactory(String trustStorePath, String trustStorePassword, + String trustStoreCertType) + { + this(trustStorePath,trustStorePassword,trustStoreCertType, + trustStorePath,trustStorePassword,trustStoreCertType); + } + + /** * 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) + public SSLContextFactory(String trustStorePath, String trustStorePassword, String trustStoreCertType, + String keyStorePath, String keyStorePassword, String keyStoreCertType) { - _keystorePath = keystorePath; - _keystorePassword = keystorePassword; - if (_keystorePassword.equals("none")) + + _trustStorePath = trustStorePath; + _trustStorePassword = trustStorePassword; + + if (_trustStorePassword.equals("none")) + { + _trustStorePassword = null; + } + _trustStoreCertType = trustStoreCertType; + + _keyStorePath = keyStorePath; + _keyStorePassword = keyStorePassword; + + if (_keyStorePassword.equals("none")) { - _keystorePassword = null; + _keyStorePassword = null; } - _certType = certType; - if (keystorePath == null) { - throw new IllegalArgumentException("Keystore path must be specified"); + _keyStoreCertType = keyStoreCertType; + + if (_trustStorePath == null) { + throw new IllegalArgumentException("A TrustStore path or KeyStore path must be specified"); } - if (certType == null) { + if (_trustStoreCertType == null) { throw new IllegalArgumentException("Cert type must be specified"); } } @@ -86,16 +122,18 @@ public class SSLContextFactory { public SSLContext buildServerContext() throws GeneralSecurityException, IOException { // Create keystore - KeyStore ks = getInitializedKeyStore(); + KeyStore ks = getInitializedKeyStore(_keyStorePath,_keyStorePassword); // Set up key manager factory to use our key store - KeyManagerFactory kmf = KeyManagerFactory.getInstance(_certType); - kmf.init(ks, _keystorePassword.toCharArray()); + KeyManagerFactory kmf = KeyManagerFactory.getInstance(_keyStoreCertType); + kmf.init(ks, _keyStorePassword.toCharArray()); + KeyStore ts = getInitializedKeyStore(_trustStorePath,_trustStorePassword); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(_trustStoreCertType); + tmf.init(ts); + // Initialize the SSLContext to work with our key managers. - SSLContext sslContext = SSLContext.getInstance("TLS"); - TrustManagerFactory tmf = TrustManagerFactory.getInstance(_certType); - tmf.init(ks); + SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); return sslContext; @@ -109,34 +147,34 @@ public class SSLContextFactory { */ public SSLContext buildClientContext() throws GeneralSecurityException, IOException { - KeyStore ks = getInitializedKeyStore(); - TrustManagerFactory tmf = TrustManagerFactory.getInstance(_certType); + KeyStore ks = getInitializedKeyStore(_trustStorePath,_trustStorePassword); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(_trustStoreCertType); tmf.init(ks); SSLContext context = SSLContext.getInstance("TLS"); context.init(null, tmf.getTrustManagers(), null); return context; } - private KeyStore getInitializedKeyStore() throws GeneralSecurityException, IOException + private KeyStore getInitializedKeyStore(String storePath, String storePassword) throws GeneralSecurityException, IOException { KeyStore ks = KeyStore.getInstance("JKS"); InputStream in = null; try { - File f = new File(_keystorePath); + File f = new File(storePath); if (f.exists()) { in = new FileInputStream(f); } else { - in = Thread.currentThread().getContextClassLoader().getResourceAsStream(_keystorePath); + in = Thread.currentThread().getContextClassLoader().getResourceAsStream(storePath); } if (in == null) { - throw new IOException("Unable to load keystore resource: " + _keystorePath); + throw new IOException("Unable to load keystore resource: " + storePath); } - ks.load(in, _keystorePassword.toCharArray()); + ks.load(in, storePassword.toCharArray()); } finally { diff --git a/java/common/src/main/java/org/apache/qpid/thread/DefaultThreadFactory.java b/java/common/src/main/java/org/apache/qpid/thread/DefaultThreadFactory.java new file mode 100644 index 0000000000..94869ab205 --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/thread/DefaultThreadFactory.java @@ -0,0 +1,18 @@ +package org.apache.qpid.thread; + +public class DefaultThreadFactory implements ThreadFactory +{ + + public Thread createThread(Runnable r) + { + return new Thread(r); + } + + public Thread createThread(Runnable r, int priority) + { + Thread t = new Thread(r); + t.setPriority(priority); + return t; + } + +} diff --git a/java/common/src/main/java/org/apache/qpid/security/JCAProvider.java b/java/common/src/main/java/org/apache/qpid/thread/QpidThreadExecutor.java index 033deb550c..38f60c04fe 100644 --- a/java/common/src/main/java/org/apache/qpid/security/JCAProvider.java +++ b/java/common/src/main/java/org/apache/qpid/thread/QpidThreadExecutor.java @@ -7,9 +7,9 @@ * 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 @@ -18,27 +18,25 @@ * under the License. * */ -package org.apache.qpid.security; -import java.security.Provider; -import java.security.Security; -import java.util.Map; +package org.apache.qpid.thread; -public class JCAProvider extends Provider -{ - public JCAProvider(Map<String, Class> providerMap) - { - super("AMQSASLProvider", 1.0, "A JCA provider that registers all " + - "AMQ SASL providers that want to be registered"); - register(providerMap); - Security.addProvider(this); - } +import org.apache.qpid.thread.Threading; - private void register(Map<String, Class> providerMap) +import edu.emory.mathcs.backport.java.util.concurrent.Executor; + +public class QpidThreadExecutor implements Executor +{ + public void execute(Runnable command) { - for (Map.Entry<String, Class> me :providerMap.entrySet()) + try { - put("SaslClientFactory." + me.getKey(), me.getValue().getName()); + Threading.getThreadFactory().createThread(command).start(); + } + catch(Exception e) + { + throw new RuntimeException("Error creating a thread using Qpid thread factory",e); } } -}
\ No newline at end of file + +} diff --git a/java/common/src/main/java/org/apache/qpid/thread/RealtimeThreadFactory.java b/java/common/src/main/java/org/apache/qpid/thread/RealtimeThreadFactory.java new file mode 100644 index 0000000000..b711f749f8 --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/thread/RealtimeThreadFactory.java @@ -0,0 +1,47 @@ +package org.apache.qpid.thread; + +import java.lang.reflect.Constructor; + +public class RealtimeThreadFactory implements ThreadFactory +{ + private Class threadClass; + private Constructor threadConstructor; + private Constructor priorityParameterConstructor; + private int defaultRTThreadPriority = 20; + + public RealtimeThreadFactory() throws Exception + { + defaultRTThreadPriority = Integer.getInteger("qpid.rt_thread_priority",20); + threadClass = Class.forName("javax.realtime.RealtimeThread"); + + Class schedulingParametersClass = Class.forName("javax.realtime.SchedulingParameters"); + Class releaseParametersClass = Class.forName("javax.realtime.ReleaseParameters"); + Class memoryParametersClass = Class.forName("javax.realtime.MemoryParameters"); + Class memoryAreaClass = Class.forName("javax.realtime.MemoryArea"); + Class processingGroupParametersClass = Class.forName("javax.realtime.ProcessingGroupParameters"); + + Class[] paramTypes = new Class[]{schedulingParametersClass, + releaseParametersClass, + memoryParametersClass, + memoryAreaClass, + processingGroupParametersClass, + java.lang.Runnable.class}; + + threadConstructor = threadClass.getConstructor(paramTypes); + + Class priorityParameterClass = Class.forName("javax.realtime.PriorityParameters"); + priorityParameterConstructor = priorityParameterClass.getConstructor(new Class[]{int.class}); + } + + public Thread createThread(Runnable r) throws Exception + { + return createThread(r,defaultRTThreadPriority); + } + + public Thread createThread(Runnable r, int priority) throws Exception + { + Object priorityParams = priorityParameterConstructor.newInstance(priority); + return (Thread)threadConstructor.newInstance(priorityParams,null,null,null,null,r); + } + +} diff --git a/java/common/src/main/java/org/apache/qpid/thread/ThreadFactory.java b/java/common/src/main/java/org/apache/qpid/thread/ThreadFactory.java new file mode 100644 index 0000000000..f9bcabfa3d --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/thread/ThreadFactory.java @@ -0,0 +1,7 @@ +package org.apache.qpid.thread; + +public interface ThreadFactory +{ + public Thread createThread(Runnable r) throws Exception; + public Thread createThread(Runnable r, int priority) throws Exception; +} diff --git a/java/common/src/main/java/org/apache/qpid/thread/Threading.java b/java/common/src/main/java/org/apache/qpid/thread/Threading.java new file mode 100644 index 0000000000..0fb113d22c --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/thread/Threading.java @@ -0,0 +1,26 @@ +package org.apache.qpid.thread; + +public final class Threading +{ + private static ThreadFactory threadFactory; + + static { + try + { + Class threadFactoryClass = + Class.forName(System.getProperty("qpid.thread_factory", + "org.apache.qpid.thread.DefaultThreadFactory")); + + threadFactory = (ThreadFactory)threadFactoryClass.newInstance(); + } + catch(Exception e) + { + throw new Error("Error occured while loading thread factory",e); + } + } + + public static ThreadFactory getThreadFactory() + { + return threadFactory; + } +} diff --git a/java/common/src/main/java/org/apache/qpid/transport/Binary.java b/java/common/src/main/java/org/apache/qpid/transport/Binary.java index e6dedc536f..4e97855a6f 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/Binary.java +++ b/java/common/src/main/java/org/apache/qpid/transport/Binary.java @@ -20,6 +20,10 @@ */ package org.apache.qpid.transport; +import java.nio.ByteBuffer; + +import static org.apache.qpid.transport.util.Functions.*; + /** * Binary @@ -51,6 +55,13 @@ public final class Binary this(bytes, 0, bytes.length); } + public final byte[] getBytes() + { + byte[] result = new byte[size]; + System.arraycopy(bytes, offset, result, 0, size); + return result; + } + public final byte[] array() { return bytes; @@ -126,4 +137,9 @@ public final class Binary return true; } + public String toString() + { + return str(ByteBuffer.wrap(bytes, offset, size)); + } + } diff --git a/java/common/src/main/java/org/apache/qpid/transport/Channel.java b/java/common/src/main/java/org/apache/qpid/transport/Channel.java deleted file mode 100644 index d6b015930b..0000000000 --- a/java/common/src/main/java/org/apache/qpid/transport/Channel.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.transport; - -import org.apache.qpid.transport.network.Frame; -import org.apache.qpid.transport.util.Logger; - -import java.nio.ByteBuffer; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -import static org.apache.qpid.transport.network.Frame.*; -import static org.apache.qpid.transport.util.Functions.*; - - -/** - * Channel - * - * @author Rafael H. Schloming - */ - -public class Channel extends Invoker - implements Receiver<ProtocolEvent>, ProtocolDelegate<Void> -{ - - private static final Logger log = Logger.get(Channel.class); - - final private Connection connection; - final private int channel; - final private MethodDelegate<Channel> delegate; - final private SessionDelegate sessionDelegate; - // session may be null - private Session session; - - public Channel(Connection connection, int channel, SessionDelegate delegate) - { - this.connection = connection; - this.channel = channel; - this.delegate = new ChannelDelegate(); - this.sessionDelegate = delegate; - } - - public Connection getConnection() - { - return connection; - } - - public void received(ProtocolEvent event) - { - event.delegate(null, this); - } - - public void init(Void v, ProtocolHeader hdr) - { - connection.getConnectionDelegate().init(this, hdr); - } - - public void control(Void v, Method method) - { - switch (method.getEncodedTrack()) - { - case L1: - method.dispatch(this, connection.getConnectionDelegate()); - break; - case L2: - method.dispatch(this, delegate); - break; - case L3: - method.delegate(session, sessionDelegate); - break; - default: - throw new IllegalStateException - ("unknown track: " + method.getEncodedTrack()); - } - } - - public void command(Void v, Method method) - { - method.delegate(session, sessionDelegate); - } - - public void error(Void v, ProtocolError error) - { - throw new RuntimeException(error.getMessage()); - } - - public void exception(Throwable t) - { - session.exception(t); - } - - public void closed() - { - log.debug("channel closed: ", this); - if (session != null) - { - session.closed(); - } - connection.removeChannel(channel); - } - - public int getEncodedChannel() { - return channel; - } - - public Session getSession() - { - return session; - } - - void setSession(Session session) - { - this.session = session; - } - - void closeCode(ConnectionClose close) - { - if (session != null) - { - session.closeCode(close); - } - } - - private void emit(ProtocolEvent event) - { - event.setChannel(channel); - connection.send(event); - } - - public void method(Method m) - { - emit(m); - - if (!m.isBatch()) - { - connection.flush(); - } - } - - protected void invoke(Method m) - { - method(m); - } - - protected <T> Future<T> invoke(Method m, Class<T> cls) - { - throw new UnsupportedOperationException(); - } - - public String toString() - { - return String.format("%s:%s", connection, channel); - } - -} diff --git a/java/common/src/main/java/org/apache/qpid/transport/ClientDelegate.java b/java/common/src/main/java/org/apache/qpid/transport/ClientDelegate.java index bbdadfadb9..bd03c3e242 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/ClientDelegate.java +++ b/java/common/src/main/java/org/apache/qpid/transport/ClientDelegate.java @@ -20,21 +20,152 @@ */ package org.apache.qpid.transport; +import static org.apache.qpid.transport.Connection.State.OPEN; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; + +import org.apache.qpid.security.UsernamePasswordCallbackHandler; +import org.apache.qpid.transport.util.Logger; + /** * ClientDelegate * */ -public abstract class ClientDelegate extends ConnectionDelegate +public class ClientDelegate extends ConnectionDelegate { + private static final Logger log = Logger.get(ClientDelegate.class); + + private String vhost; + private String username; + private String password; + private String[] saslMechs; + private String protocol; + private String serverName; + + public ClientDelegate(String vhost, String username, String password,String saslMechs) + { + this.vhost = vhost; + this.username = username; + this.password = password; + this.saslMechs = saslMechs.split(" "); + + // Looks kinda of silly but the Sun SASL Kerberos client uses the + // protocol + servername as the service key. + this.protocol = System.getProperty("qpid.sasl_protocol","AMQP"); + this.serverName = System.getProperty("qpid.sasl_server_name","localhost"); + } - public void init(Channel ch, ProtocolHeader hdr) + public void init(Connection conn, ProtocolHeader hdr) { - if (hdr.getMajor() != 0 && hdr.getMinor() != 10) + if (!(hdr.getMajor() == 0 && hdr.getMinor() == 10)) { - throw new ProtocolVersionException(hdr.getMajor(), hdr.getMinor()); + conn.exception(new ProtocolVersionException(hdr.getMajor(), hdr.getMinor())); } } + @Override public void connectionStart(Connection conn, ConnectionStart start) + { + Map clientProperties = new HashMap(); + clientProperties.put("qpid.session_flow", 1); + + List<Object> mechanisms = start.getMechanisms(); + if (mechanisms == null || mechanisms.isEmpty()) + { + conn.connectionStartOk + (clientProperties, null, null, conn.getLocale()); + return; + } + + String[] mechs = new String[mechanisms.size()]; + mechanisms.toArray(mechs); + + try + { + UsernamePasswordCallbackHandler handler = + new UsernamePasswordCallbackHandler(); + handler.initialise(username, password); + SaslClient sc = Sasl.createSaslClient + (saslMechs, null, protocol, serverName, null, handler); + conn.setSaslClient(sc); + + byte[] response = sc.hasInitialResponse() ? + sc.evaluateChallenge(new byte[0]) : null; + conn.connectionStartOk + (clientProperties, sc.getMechanismName(), response, + conn.getLocale()); + } + catch (SaslException e) + { + conn.exception(e); + } + } + + @Override public void connectionSecure(Connection conn, ConnectionSecure secure) + { + SaslClient sc = conn.getSaslClient(); + try + { + byte[] response = sc.evaluateChallenge(secure.getChallenge()); + conn.connectionSecureOk(response); + } + catch (SaslException e) + { + conn.exception(e); + } + } + + @Override public void connectionTune(Connection conn, ConnectionTune tune) + { + conn.setChannelMax(tune.getChannelMax()); + int hb_interval = calculateHeartbeatInterval(conn, + tune.getHeartbeatMin(), + tune.getHeartbeatMax() + ); + conn.connectionTuneOk(tune.getChannelMax(), + tune.getMaxFrameSize(), + hb_interval); + conn.setIdleTimeout(hb_interval*1000); + conn.connectionOpen(vhost, null, Option.INSIST); + } + + @Override public void connectionOpenOk(Connection conn, ConnectionOpenOk ok) + { + conn.setState(OPEN); + } + + @Override public void connectionRedirect(Connection conn, ConnectionRedirect redir) + { + throw new UnsupportedOperationException(); + } + + @Override public void connectionHeartbeat(Connection conn, ConnectionHeartbeat hearbeat) + { + conn.connectionHeartbeat(); + } + + /** + * Currently the spec specified the min and max for heartbeat using secs + */ + private int calculateHeartbeatInterval(Connection conn,int min, int max) + { + long l = conn.getIdleTimeout()/1000; + if (l !=0 && l >= min && l <= max) + { + return (int)l; + } + else + { + log.warn("Ignoring the idle timeout %s set by the connection," + + " using the brokers max value %s", l,max); + return max; + } + } } diff --git a/java/common/src/main/java/org/apache/qpid/transport/Connection.java b/java/common/src/main/java/org/apache/qpid/transport/Connection.java index 68b9b209bb..773746af79 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/Connection.java +++ b/java/common/src/main/java/org/apache/qpid/transport/Connection.java @@ -20,14 +20,23 @@ */ package org.apache.qpid.transport; +import org.apache.qpid.transport.network.ConnectionBinding; +import org.apache.qpid.transport.network.io.IoTransport; import org.apache.qpid.transport.util.Logger; +import org.apache.qpid.transport.util.Waiter; +import org.apache.qpid.util.Strings; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.nio.ByteBuffer; +import java.util.UUID; + +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslServer; + +import static org.apache.qpid.transport.Connection.State.*; /** @@ -40,27 +49,222 @@ import java.nio.ByteBuffer; * short instead of Short */ -public class Connection +public class Connection extends ConnectionInvoker implements Receiver<ProtocolEvent>, Sender<ProtocolEvent> { private static final Logger log = Logger.get(Connection.class); - final private Sender<ProtocolEvent> sender; - final private ConnectionDelegate delegate; + public enum State { NEW, CLOSED, OPENING, OPEN, CLOSING, CLOSE_RCVD } + + class DefaultConnectionListener implements ConnectionListener + { + public void opened(Connection conn) {} + public void exception(Connection conn, ConnectionException exception) + { + log.error(exception, "connection exception"); + } + public void closed(Connection conn) {} + } + + private ConnectionDelegate delegate; + private Sender<ProtocolEvent> sender; + + final private Map<Binary,Session> sessions = new HashMap<Binary,Session>(); + final private Map<Integer,Session> channels = new HashMap<Integer,Session>(); + + private State state = NEW; + final private Object lock = new Object(); + private long timeout = 60000; + private ConnectionListener listener = new DefaultConnectionListener(); + private ConnectionException error = null; + private int channelMax = 1; + private String locale; + private SaslServer saslServer; + private SaslClient saslClient; + private long idleTimeout = 0; + private String _authorizationID; + // want to make this final private int _connectionId; - final private Map<Integer,Channel> channels = new HashMap<Integer,Channel>(); + public Connection() {} - public Connection(Sender<ProtocolEvent> sender, - ConnectionDelegate delegate) + public void setConnectionDelegate(ConnectionDelegate delegate) { - this.sender = sender; this.delegate = delegate; } + public void setConnectionListener(ConnectionListener listener) + { + if (listener == null) + { + this.listener = new DefaultConnectionListener(); + } + else + { + this.listener = listener; + } + } + + public Sender<ProtocolEvent> getSender() + { + return sender; + } + + public void setSender(Sender<ProtocolEvent> sender) + { + this.sender = sender; + sender.setIdleTimeout(idleTimeout); + } + + protected void setState(State state) + { + synchronized (lock) + { + this.state = state; + lock.notifyAll(); + } + } + + void setLocale(String locale) + { + this.locale = locale; + } + + String getLocale() + { + return locale; + } + + void setSaslServer(SaslServer saslServer) + { + this.saslServer = saslServer; + } + + SaslServer getSaslServer() + { + return saslServer; + } + + void setSaslClient(SaslClient saslClient) + { + this.saslClient = saslClient; + } + + SaslClient getSaslClient() + { + return saslClient; + } + + public void connect(String host, int port, String vhost, String username, String password) + { + connect(host, port, vhost, username, password, false); + } + + public void connect(String host, int port, String vhost, String username, String password, boolean ssl) + { + connect(host, port, vhost, username, password, ssl,"PLAIN"); + } + + public void connect(String host, int port, String vhost, String username, String password, boolean ssl,String saslMechs) + { + synchronized (lock) + { + state = OPENING; + + delegate = new ClientDelegate(vhost, username, password,saslMechs); + + IoTransport.connect(host, port, ConnectionBinding.get(this), ssl); + send(new ProtocolHeader(1, 0, 10)); + + Waiter w = new Waiter(lock, timeout); + while (w.hasTime() && state == OPENING && error == null) + { + w.await(); + } + + if (error != null) + { + ConnectionException t = error; + error = null; + try + { + close(); + } + catch (ConnectionException ce) + { + if (!(t instanceof ProtocolVersionException)) + { + throw ce; + } + } + t.rethrow(); + } + + switch (state) + { + case OPENING: + close(); + throw new ConnectionException("connect() timed out"); + case OPEN: + break; + case CLOSED: + throw new ConnectionException("connect() aborted"); + default: + throw new IllegalStateException(String.valueOf(state)); + } + } + + listener.opened(this); + } + + public Session createSession() + { + return createSession(0); + } + + public Session createSession(long expiry) + { + return createSession(UUID.randomUUID().toString(), expiry); + } + + public Session createSession(String name) + { + return createSession(name, 0); + } + + public Session createSession(String name, long expiry) + { + return createSession(Strings.toUTF8(name), expiry); + } + + public Session createSession(byte[] name, long expiry) + { + return createSession(new Binary(name), expiry); + } + + public Session createSession(Binary name, long expiry) + { + synchronized (lock) + { + Session ssn = new Session(this, name, expiry); + sessions.put(name, ssn); + map(ssn); + ssn.attach(); + return ssn; + } + } + + void removeSession(Session ssn) + { + synchronized (lock) + { + sessions.remove(ssn.getName()); + } + } + public void setConnectionId(int id) { _connectionId = id; @@ -79,14 +283,18 @@ public class Connection public void received(ProtocolEvent event) { log.debug("RECV: [%s] %s", this, event); - Channel channel = getChannel(event.getChannel()); - channel.received(event); + event.delegate(this, delegate); } public void send(ProtocolEvent event) { log.debug("SEND: [%s] %s", this, event); - sender.send(event); + Sender s = sender; + if (s == null) + { + throw new ConnectionException("connection closed"); + } + s.send(event); } public void flush() @@ -95,6 +303,30 @@ public class Connection sender.flush(); } + protected void invoke(Method method) + { + method.setChannel(0); + send(method); + if (!method.isBatch()) + { + flush(); + } + } + + public void dispatch(Method method) + { + Session ssn = getSession(method.getChannel()); + if(ssn != null) + { + ssn.received(method); + } + else + { + throw new ProtocolViolationException( + "Received frames for an already dettached session", null); + } + } + public int getChannelMax() { return channelMax; @@ -105,15 +337,16 @@ public class Connection channelMax = max; } - public Channel getChannel() + private int map(Session ssn) { - synchronized (channels) + synchronized (lock) { for (int i = 0; i < getChannelMax(); i++) { if (!channels.containsKey(i)) { - return getChannel(i); + map(ssn, i); + return i; } } @@ -121,61 +354,195 @@ public class Connection } } - public Channel getChannel(int number) + void map(Session ssn, int channel) + { + synchronized (lock) + { + channels.put(channel, ssn); + ssn.setChannel(channel); + } + } + + void unmap(Session ssn) + { + synchronized (lock) + { + channels.remove(ssn.getChannel()); + } + } + + Session getSession(int channel) + { + synchronized (lock) + { + return channels.get(channel); + } + } + + public void resume() { - synchronized (channels) + synchronized (lock) { - Channel channel = channels.get(number); - if (channel == null) + for (Session ssn : sessions.values()) { - channel = new Channel(this, number, delegate.getSessionDelegate()); - channels.put(number, channel); + map(ssn); + ssn.attach(); + ssn.resume(); } - return channel; } } - void removeChannel(int number) + public void exception(ConnectionException e) { - synchronized (channels) + synchronized (lock) { - channels.remove(number); + switch (state) + { + case OPENING: + case CLOSING: + error = e; + lock.notifyAll(); + return; + } } + + listener.exception(this, e); } public void exception(Throwable t) { - delegate.exception(t); + exception(new ConnectionException(t)); } void closeCode(ConnectionClose close) { - synchronized (channels) + synchronized (lock) { - for (Channel ch : channels.values()) + for (Session ssn : channels.values()) { - ch.closeCode(close); + ssn.closeCode(close); + } + ConnectionCloseCode code = close.getReplyCode(); + if (code != ConnectionCloseCode.NORMAL) + { + exception(new ConnectionException(close)); } } } public void closed() { + if (state == OPEN) + { + exception(new ConnectionException("connection aborted")); + } + log.debug("connection closed: %s", this); - synchronized (channels) + + synchronized (lock) { - List<Channel> values = new ArrayList<Channel>(channels.values()); - for (Channel ch : values) + List<Session> values = new ArrayList<Session>(channels.values()); + for (Session ssn : values) + { + ssn.closed(); + } + + try + { + sender.close(); + } + catch(Exception e) { - ch.closed(); + // ignore. } + sender = null; + setState(CLOSED); } - delegate.closed(); + + listener.closed(this); } public void close() { - sender.close(); + synchronized (lock) + { + switch (state) + { + case OPEN: + state = CLOSING; + connectionClose(ConnectionCloseCode.NORMAL, null); + Waiter w = new Waiter(lock, timeout); + while (w.hasTime() && state == CLOSING && error == null) + { + w.await(); + } + + if (error != null) + { + close(); + throw new ConnectionException(error); + } + + switch (state) + { + case CLOSING: + close(); + throw new ConnectionException("close() timed out"); + case CLOSED: + break; + default: + throw new IllegalStateException(String.valueOf(state)); + } + break; + case CLOSED: + break; + default: + if (sender != null) + { + sender.close(); + w = new Waiter(lock, timeout); + while (w.hasTime() && sender != null && error == null) + { + w.await(); + } + + if (error != null) + { + throw new ConnectionException(error); + } + + if (sender != null) + { + throw new ConnectionException("close() timed out"); + } + } + break; + } + } + } + + public void setIdleTimeout(long l) + { + idleTimeout = l; + if (sender != null) + { + sender.setIdleTimeout(l); + } + } + + public long getIdleTimeout() + { + return idleTimeout; + } + + public void setAuthorizationID(String authorizationID) + { + _authorizationID = authorizationID; + } + + public String getAuthorizationID() + { + return _authorizationID; } public String toString() diff --git a/java/common/src/main/java/org/apache/qpid/transport/ConnectionDelegate.java b/java/common/src/main/java/org/apache/qpid/transport/ConnectionDelegate.java index 2aa1db7b28..9ef7a47e1b 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/ConnectionDelegate.java +++ b/java/common/src/main/java/org/apache/qpid/transport/ConnectionDelegate.java @@ -22,22 +22,7 @@ package org.apache.qpid.transport; import org.apache.qpid.transport.util.Logger; -import org.apache.qpid.SecurityHelper; -import org.apache.qpid.QpidException; - -import java.io.UnsupportedEncodingException; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; - -import javax.security.sasl.Sasl; -import javax.security.sasl.SaslClient; -import javax.security.sasl.SaslException; -import javax.security.sasl.SaslServer; +import static org.apache.qpid.transport.Connection.State.*; /** @@ -52,236 +37,67 @@ import javax.security.sasl.SaslServer; * * the connectionClose is kind of different for both sides */ -public abstract class ConnectionDelegate extends MethodDelegate<Channel> +public abstract class ConnectionDelegate + extends MethodDelegate<Connection> + implements ProtocolDelegate<Connection> { private static final Logger log = Logger.get(ConnectionDelegate.class); - private String _username = "guest"; - private String _password = "guest";; - private String _mechanism; - private String _virtualHost; - private SaslClient saslClient; - private SaslServer saslServer; - private String _locale = "utf8"; - private int maxFrame = 64*1024; - private Condition _negotiationComplete; - private Lock _negotiationCompleteLock; - - public abstract SessionDelegate getSessionDelegate(); - - public abstract void exception(Throwable t); - - public abstract void closed(); - - public void setCondition(Lock negotiationCompleteLock,Condition negotiationComplete) + public void control(Connection conn, Method method) { - _negotiationComplete = negotiationComplete; - _negotiationCompleteLock = negotiationCompleteLock; + method.dispatch(conn, this); } - public void init(Channel ch, ProtocolHeader hdr) + public void command(Connection conn, Method method) { - ch.getConnection().send(new ProtocolHeader (1, hdr.getMajor(), hdr.getMinor())); - List<Object> plain = new ArrayList<Object>(); - plain.add("PLAIN"); - List<Object> utf8 = new ArrayList<Object>(); - utf8.add("utf8"); - ch.connectionStart(null, plain, utf8); + method.dispatch(conn, this); } - // ---------------------------------------------- - // Client side - //----------------------------------------------- - @Override public void connectionStart(Channel context, ConnectionStart struct) + public void error(Connection conn, ProtocolError error) { - String mechanism = null; - byte[] response = null; - try - { - mechanism = SecurityHelper.chooseMechanism(struct.getMechanisms()); - saslClient = Sasl.createSaslClient(new String[]{ mechanism },null, "AMQP", "localhost", null, - SecurityHelper.createCallbackHandler(mechanism,_username,_password )); - response = saslClient.evaluateChallenge(new byte[0]); - } - catch (UnsupportedEncodingException e) - { - // need error handling - } - catch (SaslException e) - { - // need error handling - } - catch (QpidException e) - { - // need error handling - } - - Map<String,Object> props = new HashMap<String,Object>(); - context.connectionStartOk(props, mechanism, response, _locale); + conn.exception(new ConnectionException(error.getMessage())); } - @Override public void connectionSecure(Channel context, ConnectionSecure struct) + public void handle(Connection conn, Method method) { - try - { - byte[] response = saslClient.evaluateChallenge(struct.getChallenge()); - context.connectionSecureOk(response); - } - catch (SaslException e) - { - // need error handling - } + conn.dispatch(method); } - @Override public void connectionTune(Channel context, ConnectionTune struct) + @Override public void connectionHeartbeat(Connection conn, ConnectionHeartbeat hearbeat) { - context.getConnection().setChannelMax(struct.getChannelMax()); - context.connectionTuneOk(struct.getChannelMax(), struct.getMaxFrameSize(), struct.getHeartbeatMax()); - context.connectionOpen(_virtualHost, null, Option.INSIST); + // do nothing } - - @Override public void connectionOpenOk(Channel context, ConnectionOpenOk struct) + @Override public void connectionClose(Connection conn, ConnectionClose close) { - List<Object> knownHosts = struct.getKnownHosts(); - if(_negotiationCompleteLock != null) - { - _negotiationCompleteLock.lock(); - try - { - _negotiationComplete.signalAll(); - } - finally - { - _negotiationCompleteLock.unlock(); - } - } + conn.connectionCloseOk(); + conn.getSender().close(); + conn.closeCode(close); + conn.setState(CLOSE_RCVD); } - public void connectionRedirect(Channel context, ConnectionRedirect struct) + @Override public void connectionCloseOk(Connection conn, ConnectionCloseOk ok) { - // not going to bother at the moment + conn.getSender().close(); } - // ---------------------------------------------- - // Server side - //----------------------------------------------- - @Override public void connectionStartOk(Channel context, ConnectionStartOk struct) + @Override public void sessionDetach(Connection conn, SessionDetach dtc) { - //set the client side locale on the server side - _locale = struct.getLocale(); - _mechanism = struct.getMechanism(); - - //try - //{ - //saslServer = Sasl.createSaslServer(_mechanism, "AMQP", "ABC",null,SecurityHelper.createCallbackHandler(_mechanism,_username,_password)); - //byte[] challenge = saslServer.evaluateResponse(struct.getResponse().getBytes()); - byte[] challenge = null; - if ( challenge == null) - { - context.connectionTune(Integer.MAX_VALUE, maxFrame, 0, Integer.MAX_VALUE); - } - else - { - try - { - context.connectionSecure(challenge); - } - catch(Exception e) - { - - } - } - - - /*} - catch (SaslException e) - { - // need error handling - } - catch (QpidException e) - { - // need error handling - }*/ + Session ssn = conn.getSession(dtc.getChannel()); + conn.unmap(ssn); + ssn.sessionDetached(dtc.getName(), SessionDetachCode.NORMAL); + ssn.closed(); } - @Override public void connectionSecureOk(Channel context, ConnectionSecureOk struct) + @Override public void sessionDetached(Connection conn, SessionDetached dtc) { - try - { - saslServer = Sasl.createSaslServer(_mechanism, "AMQP", "ABC",new HashMap(),SecurityHelper.createCallbackHandler(_mechanism,_username,_password)); - byte[] challenge = saslServer.evaluateResponse(struct.getResponse()); - if ( challenge == null) - { - context.connectionTune(Integer.MAX_VALUE, maxFrame, 0, Integer.MAX_VALUE); - } - else - { - try - { - context.connectionSecure(challenge); - } - catch(Exception e) - { - - } - } - - - } - catch (SaslException e) + Session ssn = conn.getSession(dtc.getChannel()); + if (ssn != null) { - // need error handling + conn.unmap(ssn); + ssn.closed(); } - catch (QpidException e) - { - // need error handling - } - } - - - @Override public void connectionOpen(Channel context, ConnectionOpen struct) - { - List<Object> hosts = new ArrayList<Object>(); - hosts.add("amqp:1223243232325"); - context.connectionOpenOk(hosts); - } - - @Override public void connectionClose(Channel ch, ConnectionClose close) - { - ch.getConnection().closeCode(close); - ch.connectionCloseOk(); - } - - public String getPassword() - { - return _password; - } - - public void setPassword(String password) - { - _password = password; - } - - public String getUsername() - { - return _username; - } - - public void setUsername(String username) - { - _username = username; - } - - public String getVirtualHost() - { - return _virtualHost; - } - - public void setVirtualHost(String host) - { - _virtualHost = host; } } diff --git a/java/common/src/main/java/org/apache/qpid/transport/ConnectionException.java b/java/common/src/main/java/org/apache/qpid/transport/ConnectionException.java index c3239ef684..6d3972eb43 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/ConnectionException.java +++ b/java/common/src/main/java/org/apache/qpid/transport/ConnectionException.java @@ -26,20 +26,45 @@ package org.apache.qpid.transport; * */ -public class ConnectionException extends RuntimeException +public class ConnectionException extends TransportException { private ConnectionClose close; - public ConnectionException(ConnectionClose close) + public ConnectionException(String message, ConnectionClose close, Throwable cause) { - super(close.getReplyText()); + super(message, cause); this.close = close; } + public ConnectionException(String message) + { + this(message, null, null); + } + + public ConnectionException(String message, Throwable cause) + { + this(message, null, cause); + } + + public ConnectionException(Throwable cause) + { + this(cause.getMessage(), null, cause); + } + + public ConnectionException(ConnectionClose close) + { + this(close.getReplyText(), close, null); + } + public ConnectionClose getClose() { return close; } + @Override public void rethrow() + { + throw new ConnectionException(getMessage(), close, this); + } + } diff --git a/java/common/src/main/java/org/apache/qpid/transport/ConnectionListener.java b/java/common/src/main/java/org/apache/qpid/transport/ConnectionListener.java new file mode 100644 index 0000000000..616e76825a --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/transport/ConnectionListener.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.transport; + + +/** + * ConnectionListener + * + */ + +public interface ConnectionListener +{ + + void opened(Connection connection); + + void exception(Connection connection, ConnectionException exception); + + void closed(Connection connection); + +} diff --git a/java/common/src/main/java/org/apache/qpid/transport/Echo.java b/java/common/src/main/java/org/apache/qpid/transport/Echo.java index b2be32331a..0e969464ab 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/Echo.java +++ b/java/common/src/main/java/org/apache/qpid/transport/Echo.java @@ -32,36 +32,41 @@ import org.apache.qpid.transport.network.io.IoAcceptor; * */ -public class Echo extends SessionDelegate +public class Echo implements SessionListener { - public void messageTransfer(Session ssn, MessageTransfer xfr) + public void opened(Session ssn) {} + + public void resumed(Session ssn) {} + + public void message(Session ssn, MessageTransfer xfr) { + int id = xfr.getId(); ssn.invoke(xfr); - ssn.processed(xfr); + ssn.processed(id); + } + + public void exception(Session ssn, SessionException exc) + { + exc.printStackTrace(); } + public void closed(Session ssn) {} + public static final void main(String[] args) throws IOException { - ConnectionDelegate delegate = new ConnectionDelegate() + ConnectionDelegate delegate = new ServerDelegate() { - public SessionDelegate getSessionDelegate() - { - return new Echo(); - } - public void exception(Throwable t) + @Override public Session getSession(Connection conn, SessionAttach atc) { - t.printStackTrace(); + Session ssn = super.getSession(conn, atc); + ssn.setSessionListener(new Echo()); + return ssn; } - public void closed() {} }; - //hack - delegate.setUsername("guest"); - delegate.setPassword("guest"); - IoAcceptor ioa = new IoAcceptor - ("0.0.0.0", 5672, new ConnectionBinding(delegate)); + ("0.0.0.0", 5672, ConnectionBinding.get(delegate)); ioa.start(); } diff --git a/java/common/src/main/java/org/apache/qpid/transport/Method.java b/java/common/src/main/java/org/apache/qpid/transport/Method.java index 37c347db98..3c80180d0b 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/Method.java +++ b/java/common/src/main/java/org/apache/qpid/transport/Method.java @@ -35,6 +35,7 @@ import static org.apache.qpid.transport.util.Functions.*; public abstract class Method extends Struct implements ProtocolEvent { + public static final Method create(int type) { // XXX: should generate separate factories for separate @@ -43,11 +44,18 @@ public abstract class Method extends Struct implements ProtocolEvent } // XXX: command subclass? + public static interface CompletionListener + { + public void onComplete(Method method); + } + private int id; private int channel; private boolean idSet = false; private boolean sync = false; private boolean batch = false; + private boolean unreliable = false; + private CompletionListener completionListener; public final int getId() { @@ -60,6 +68,11 @@ public abstract class Method extends Struct implements ProtocolEvent this.idSet = true; } + boolean idSet() + { + return idSet; + } + public final int getChannel() { return channel; @@ -75,7 +88,7 @@ public abstract class Method extends Struct implements ProtocolEvent return sync; } - protected final void setSync(boolean value) + public final void setSync(boolean value) { this.sync = value; } @@ -85,12 +98,22 @@ public abstract class Method extends Struct implements ProtocolEvent return batch; } - protected final void setBatch(boolean value) + final void setBatch(boolean value) { this.batch = value; } - public abstract boolean hasPayloadSegment(); + public final boolean isUnreliable() + { + return unreliable; + } + + final void setUnreliable(boolean value) + { + this.unreliable = value; + } + + public abstract boolean hasPayload(); public Header getHeader() { @@ -112,18 +135,22 @@ public abstract class Method extends Struct implements ProtocolEvent throw new UnsupportedOperationException(); } - public abstract byte getEncodedTrack(); - - public <C> void dispatch(C context, MethodDelegate<C> delegate) + public int getBodySize() { - throw new UnsupportedOperationException(); + ByteBuffer body = getBody(); + if (body == null) + { + return 0; + } + else + { + return body.remaining(); + } } - public <C> void dispatch - (C context, org.apache.qpid.transport.v1_0.MethodDelegate<C> delegate) - { - throw new UnsupportedOperationException(); - } + public abstract byte getEncodedTrack(); + + public abstract <C> void dispatch(C context, MethodDelegate<C> delegate); public <C> void delegate(C context, ProtocolDelegate<C> delegate) { @@ -137,6 +164,26 @@ public abstract class Method extends Struct implements ProtocolEvent } } + + public void setCompletionListener(CompletionListener completionListener) + { + this.completionListener = completionListener; + } + + public void complete() + { + if(completionListener!= null) + { + completionListener.onComplete(this); + completionListener = null; + } + } + + public boolean hasCompletionListener() + { + return completionListener != null; + } + public String toString() { StringBuilder str = new StringBuilder(); diff --git a/java/common/src/main/java/org/apache/qpid/transport/NetworkDriver.java b/java/common/src/main/java/org/apache/qpid/transport/NetworkDriver.java new file mode 100644 index 0000000000..86af97bf7e --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/transport/NetworkDriver.java @@ -0,0 +1,63 @@ +/* + * + * 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.transport; + +import java.net.BindException; +import java.net.InetAddress; +import java.net.SocketAddress; + +import org.apache.qpid.protocol.ProtocolEngine; +import org.apache.qpid.protocol.ProtocolEngineFactory; +import org.apache.qpid.ssl.SSLContextFactory; + +public interface NetworkDriver extends Sender<java.nio.ByteBuffer> +{ + // Creates a NetworkDriver which attempts to connect to destination on port and attaches the ProtocolEngine to + // it using the SSLContextFactory if provided + void open(int port, InetAddress destination, ProtocolEngine engine, + NetworkDriverConfiguration config, SSLContextFactory sslFactory) + throws OpenException; + + // listens for incoming connections on the specified ports and address and creates a new NetworkDriver which + // processes incoming connections with ProtocolEngines and SSLEngines created from the factories + // (in the case of an SSLContextFactory, if provided) + void bind (int port, InetAddress[] addresses, ProtocolEngineFactory protocolFactory, + NetworkDriverConfiguration config, SSLContextFactory sslFactory) throws BindException; + + // Returns the remote address of the underlying socket + SocketAddress getRemoteAddress(); + + // Returns the local address of the underlying socket + SocketAddress getLocalAddress(); + + /** + * The length of time after which the ProtocolEngines readIdle() method should be called if no data has been + * read in seconds + */ + void setMaxReadIdle(int idleTime); + + /** + * The length of time after which the ProtocolEngines writeIdle() method should be called if no data has been + * written in seconds + */ + void setMaxWriteIdle(int idleTime); + +}
\ No newline at end of file diff --git a/java/common/src/main/java/org/apache/qpid/transport/NetworkDriverConfiguration.java b/java/common/src/main/java/org/apache/qpid/transport/NetworkDriverConfiguration.java new file mode 100644 index 0000000000..c38afe5dd5 --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/transport/NetworkDriverConfiguration.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.transport; + +/** + * This interface provides a means for NetworkDrivers to configure TCP options such as incoming and outgoing + * buffer sizes and set particular options on the socket. NetworkDrivers should honour the values returned + * from here if the underlying implementation supports them. + */ +public interface NetworkDriverConfiguration +{ + // Taken from Socket + Boolean getKeepAlive(); + Boolean getOOBInline(); + Boolean getReuseAddress(); + Integer getSoLinger(); // null means off + Integer getSoTimeout(); + Boolean getTcpNoDelay(); + Integer getTrafficClass(); + + // The amount of memory in bytes to allocate to the incoming buffer + Integer getReceiveBufferSize(); + + // The amount of memory in bytes to allocate to the outgoing buffer + Integer getSendBufferSize(); +} diff --git a/java/common/src/main/java/org/apache/qpid/transport/OpenException.java b/java/common/src/main/java/org/apache/qpid/transport/OpenException.java new file mode 100644 index 0000000000..68fbb5e8ec --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/transport/OpenException.java @@ -0,0 +1,34 @@ +/* + * + * 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.transport; + +import java.io.IOException; + +public class OpenException extends IOException +{ + + public OpenException(String string, Throwable lastException) + { + super(string, lastException); + } + +} diff --git a/java/common/src/main/java/org/apache/qpid/transport/ProtocolHeader.java b/java/common/src/main/java/org/apache/qpid/transport/ProtocolHeader.java index fa0c1e9c63..00ea55ff96 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/ProtocolHeader.java +++ b/java/common/src/main/java/org/apache/qpid/transport/ProtocolHeader.java @@ -39,13 +39,15 @@ public final class ProtocolHeader implements NetworkEvent, ProtocolEvent private static final byte[] AMQP = {'A', 'M', 'Q', 'P' }; private static final byte CLASS = 1; + final private byte protoClass; final private byte instance; final private byte major; final private byte minor; private int channel; - public ProtocolHeader(byte instance, byte major, byte minor) + public ProtocolHeader(byte protoClass, byte instance, byte major, byte minor) { + this.protoClass = protoClass; this.instance = instance; this.major = major; this.minor = minor; @@ -53,7 +55,7 @@ public final class ProtocolHeader implements NetworkEvent, ProtocolEvent public ProtocolHeader(int instance, int major, int minor) { - this((byte) instance, (byte) major, (byte) minor); + this(CLASS, (byte) instance, (byte) major, (byte) minor); } public byte getInstance() @@ -90,7 +92,7 @@ public final class ProtocolHeader implements NetworkEvent, ProtocolEvent { ByteBuffer buf = ByteBuffer.allocate(8); buf.put(AMQP); - buf.put(CLASS); + buf.put(protoClass); buf.put(instance); buf.put(major); buf.put(minor); diff --git a/java/common/src/main/java/org/apache/qpid/transport/ProtocolVersionException.java b/java/common/src/main/java/org/apache/qpid/transport/ProtocolVersionException.java index 2de0c169a5..db8064268c 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/ProtocolVersionException.java +++ b/java/common/src/main/java/org/apache/qpid/transport/ProtocolVersionException.java @@ -26,19 +26,24 @@ package org.apache.qpid.transport; * */ -public final class ProtocolVersionException extends TransportException +public final class ProtocolVersionException extends ConnectionException { private final byte major; private final byte minor; - public ProtocolVersionException(byte major, byte minor) + public ProtocolVersionException(byte major, byte minor, Throwable cause) { - super(String.format("version missmatch: %s-%s", major, minor)); + super(String.format("version mismatch: %s-%s", major, minor), cause); this.major = major; this.minor = minor; } + public ProtocolVersionException(byte major, byte minor) + { + this(major, minor, null); + } + public byte getMajor() { return this.major; @@ -49,4 +54,9 @@ public final class ProtocolVersionException extends TransportException return this.minor; } + @Override public void rethrow() + { + throw new ProtocolVersionException(major, minor, this); + } + } diff --git a/java/common/src/main/java/org/apache/qpid/transport/ProtocolViolationException.java b/java/common/src/main/java/org/apache/qpid/transport/ProtocolViolationException.java new file mode 100644 index 0000000000..6787157e8e --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/transport/ProtocolViolationException.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.transport; + + +/** + * ProtocolViolationException + * + */ + +public final class ProtocolViolationException extends ConnectionException +{ + public ProtocolViolationException(String msg,Throwable cause) + { + super(msg, cause); + } +} diff --git a/java/common/src/main/java/org/apache/qpid/transport/RangeSet.java b/java/common/src/main/java/org/apache/qpid/transport/RangeSet.java index 9b2744ee8b..3850dc162b 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/RangeSet.java +++ b/java/common/src/main/java/org/apache/qpid/transport/RangeSet.java @@ -52,6 +52,11 @@ public final class RangeSet implements Iterable<Range> return ranges.getFirst(); } + public Range getLast() + { + return ranges.getLast(); + } + public boolean includes(Range range) { for (Range r : this) diff --git a/java/common/src/main/java/org/apache/qpid/transport/Sender.java b/java/common/src/main/java/org/apache/qpid/transport/Sender.java index 9a6f675d7f..475289c83f 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/Sender.java +++ b/java/common/src/main/java/org/apache/qpid/transport/Sender.java @@ -28,6 +28,7 @@ package org.apache.qpid.transport; public interface Sender<T> { + void setIdleTimeout(long l); void send(T msg); diff --git a/java/common/src/main/java/org/apache/qpid/transport/ChannelDelegate.java b/java/common/src/main/java/org/apache/qpid/transport/SenderException.java index 8475fbf174..a96079dc27 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/ChannelDelegate.java +++ b/java/common/src/main/java/org/apache/qpid/transport/SenderException.java @@ -20,35 +20,33 @@ */ package org.apache.qpid.transport; -import java.util.UUID; - /** - * ChannelDelegate + * SenderException * - * @author Rafael H. Schloming */ -class ChannelDelegate extends MethodDelegate<Channel> +public class SenderException extends TransportException { - public @Override void sessionAttach(Channel channel, SessionAttach atch) + public SenderException(String message, Throwable cause) + { + super(message, cause); + } + + public SenderException(String message) { - Session ssn = new Session(atch.getName()); - ssn.attach(channel); - ssn.sessionAttached(ssn.getName()); + super(message); } - public @Override void sessionDetached(Channel channel, SessionDetached closed) + public SenderException(Throwable cause) { - channel.closed(); + super(cause); } - public @Override void sessionDetach(Channel channel, SessionDetach dtc) + public void rethrow() { - channel.getSession().closed(); - channel.sessionDetached(dtc.getName(), SessionDetachCode.NORMAL); - channel.closed(); + throw new SenderException(getMessage(), this); } } diff --git a/java/common/src/main/java/org/apache/qpid/transport/ServerDelegate.java b/java/common/src/main/java/org/apache/qpid/transport/ServerDelegate.java new file mode 100644 index 0000000000..453921ea2b --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/transport/ServerDelegate.java @@ -0,0 +1,183 @@ +/* + * + * 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.transport; + +import java.util.Collections; + + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; + +import java.io.UnsupportedEncodingException; + +import org.apache.qpid.QpidException; + +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + + +import static org.apache.qpid.transport.Connection.State.*; + + +/** + * ServerDelegate + * + */ + +public class ServerDelegate extends ConnectionDelegate +{ + + private SaslServer saslServer; + private List<Object> _locales; + private List<Object> _mechanisms; + private Map<String, Object> _clientProperties; + + + public ServerDelegate() + { + this(null, Collections.EMPTY_LIST, Collections.singletonList((Object)"utf8")); + } + + protected ServerDelegate(Map<String, Object> clientProperties, List<Object> mechanisms, List<Object> locales) + { + _clientProperties = clientProperties; + _mechanisms = mechanisms; + _locales = locales; + } + + public void init(Connection conn, ProtocolHeader hdr) + { + conn.send(new ProtocolHeader(1, 0, 10)); + + conn.connectionStart(_clientProperties, _mechanisms, _locales); + } + + @Override public void connectionStartOk(Connection conn, ConnectionStartOk ok) + { + conn.setLocale(ok.getLocale()); + String mechanism = ok.getMechanism(); + + if (mechanism == null || mechanism.length() == 0) + { + conn.connectionTune + (Integer.MAX_VALUE, + org.apache.qpid.transport.network.ConnectionBinding.MAX_FRAME_SIZE, + 0, Integer.MAX_VALUE); + return; + } + + try + { + + SaslServer ss = createSaslServer(mechanism); + if (ss == null) + { + conn.connectionClose + (ConnectionCloseCode.CONNECTION_FORCED, + "null SASL mechanism: " + mechanism); + return; + } + conn.setSaslServer(ss); + secure(conn, ok.getResponse()); + } + catch (SaslException e) + { + conn.exception(e); + } + } + + protected SaslServer createSaslServer(String mechanism) + throws SaslException + { + SaslServer ss = Sasl.createSaslServer + (mechanism, "AMQP", "localhost", null, null); + return ss; + } + + private void secure(Connection conn, byte[] response) + { + SaslServer ss = conn.getSaslServer(); + try + { + byte[] challenge = ss.evaluateResponse(response); + if (ss.isComplete()) + { + ss.dispose(); + conn.connectionTune + (Integer.MAX_VALUE, + org.apache.qpid.transport.network.ConnectionBinding.MAX_FRAME_SIZE, + 0, Integer.MAX_VALUE); + conn.setAuthorizationID(ss.getAuthorizationID()); + } + else + { + conn.connectionSecure(challenge); + } + } + catch (SaslException e) + { + conn.exception(e); + } + } + + @Override public void connectionSecureOk(Connection conn, ConnectionSecureOk ok) + { + secure(conn, ok.getResponse()); + } + + @Override public void connectionTuneOk(Connection conn, ConnectionTuneOk ok) + { + + } + + @Override public void connectionOpen(Connection conn, ConnectionOpen open) + { + conn.connectionOpenOk(Collections.EMPTY_LIST); + + conn.setState(OPEN); + } + + protected Session getSession(Connection conn, SessionDelegate delegate, SessionAttach atc) + { + return new Session(conn, delegate, new Binary(atc.getName()), 0); + } + + + public Session getSession(Connection conn, SessionAttach atc) + { + return new Session(conn, new Binary(atc.getName()), 0); + } + + @Override public void sessionAttach(Connection conn, SessionAttach atc) + { + Session ssn = getSession(conn, atc); + conn.map(ssn, atc.getChannel()); + ssn.sessionAttached(atc.getName()); + ssn.setState(Session.State.OPEN); + } + +} diff --git a/java/common/src/main/java/org/apache/qpid/transport/Session.java b/java/common/src/main/java/org/apache/qpid/transport/Session.java index 10ca6cfb0a..818bb19c08 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/Session.java +++ b/java/common/src/main/java/org/apache/qpid/transport/Session.java @@ -24,6 +24,7 @@ package org.apache.qpid.transport; import org.apache.qpid.transport.network.Frame; import org.apache.qpid.transport.util.Logger; +import org.apache.qpid.transport.util.Waiter; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -32,8 +33,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; import static org.apache.qpid.transport.Option.*; +import static org.apache.qpid.transport.Session.State.*; import static org.apache.qpid.transport.util.Functions.*; import static org.apache.qpid.util.Serial.*; import static org.apache.qpid.util.Strings.*; @@ -44,59 +48,125 @@ import static org.apache.qpid.util.Strings.*; * @author Rafael H. Schloming */ -public class Session extends Invoker +public class Session extends SessionInvoker { private static final Logger log = Logger.get(Session.class); - private static boolean ENABLE_REPLAY = false; + enum State { NEW, DETACHED, RESUMING, OPEN, CLOSING, CLOSED } - static + class DefaultSessionListener implements SessionListener { - String enableReplay = "enable_command_replay"; - try + + public void opened(Session ssn) {} + + public void resumed(Session ssn) {} + + public void message(Session ssn, MessageTransfer xfr) { - ENABLE_REPLAY = new Boolean(System.getProperties().getProperty(enableReplay, "false")); + log.info("message: %s", xfr); } - catch (Exception e) + + public void exception(Session ssn, SessionException exc) { - ENABLE_REPLAY = false; + log.error(exc, "session exception"); } + + public void closed(Session ssn) {} } - private byte[] name; + public static final int UNLIMITED_CREDIT = 0xFFFFFFFF; + + private Connection connection; + private Binary name; + private long expiry; + private int channel; + private SessionDelegate delegate; + private SessionListener listener = new DefaultSessionListener(); private long timeout = 60000; private boolean autoSync = false; - // channel may be null - Channel channel; - + private boolean incomingInit; // incoming command count - int commandsIn = 0; + private int commandsIn; // completed incoming commands private final Object processedLock = new Object(); - private RangeSet processed = new RangeSet(); - private int maxProcessed = commandsIn - 1; - private int syncPoint = maxProcessed; + private RangeSet processed; + private int maxProcessed; + private int syncPoint; // outgoing command count private int commandsOut = 0; - private Map<Integer,Method> commands = new HashMap<Integer,Method>(); + private Method[] commands = new Method[Integer.getInteger("qpid.session.command_limit", 64*1024)]; + private int commandBytes = 0; + private int byteLimit = Integer.getInteger("qpid.session.byte_limit", 1024*1024); private int maxComplete = commandsOut - 1; private boolean needSync = false; - private AtomicBoolean closed = new AtomicBoolean(false); + private State state = NEW; + + // transfer flow control + private volatile boolean flowControl = false; + private Semaphore credit = new Semaphore(0); + + private Thread resumer = null; - public Session(byte[] name) + protected Session(Connection connection, Binary name, long expiry) { + this(connection, new SessionDelegate(), name, expiry); + } + + protected Session(Connection connection, SessionDelegate delegate, Binary name, long expiry) + { + this.connection = connection; + this.delegate = delegate; this.name = name; + this.expiry = expiry; + initReceiver(); + } + + public Connection getConnection() + { + return connection; } - public byte[] getName() + public Binary getName() { return name; } + void setExpiry(long expiry) + { + this.expiry = expiry; + } + + int getChannel() + { + return channel; + } + + void setChannel(int channel) + { + this.channel = channel; + } + + public void setSessionListener(SessionListener listener) + { + if (listener == null) + { + this.listener = new DefaultSessionListener(); + } + else + { + this.listener = listener; + } + } + + public SessionListener getSessionListener() + { + return listener; + } + public void setAutoSync(boolean value) { synchronized (commands) @@ -105,9 +175,119 @@ public class Session extends Invoker } } - public Map<Integer,Method> getOutstandingCommands() + void setState(State state) + { + synchronized (commands) + { + this.state = state; + commands.notifyAll(); + } + } + + void setFlowControl(boolean value) + { + flowControl = value; + } + + void addCredit(int value) + { + credit.release(value); + } + + void drainCredit() { - return commands; + credit.drainPermits(); + } + + void acquireCredit() + { + if (flowControl) + { + try + { + if (!credit.tryAcquire(timeout, TimeUnit.MILLISECONDS)) + { + throw new SessionException + ("timed out waiting for message credit"); + } + } + catch (InterruptedException e) + { + throw new SessionException + ("interrupted while waiting for credit", null, e); + } + } + } + + private void initReceiver() + { + synchronized (processedLock) + { + incomingInit = false; + processed = new RangeSet(); + } + } + + void attach() + { + initReceiver(); + sessionAttach(name.getBytes()); + // XXX: when the broker and client support full session + // recovery we should use expiry as the requested timeout + sessionRequestTimeout(0); + } + + void resume() + { + synchronized (commands) + { + for (int i = maxComplete + 1; lt(i, commandsOut); i++) + { + Method m = commands[mod(i, commands.length)]; + if (m == null) + { + m = new ExecutionSync(); + m.setId(i); + } + sessionCommandPoint(m.getId(), 0); + send(m); + } + + sessionCommandPoint(commandsOut, 0); + sessionFlush(COMPLETED); + resumer = Thread.currentThread(); + state = RESUMING; + listener.resumed(this); + resumer = null; + } + } + + void dump() + { + synchronized (commands) + { + for (Method m : commands) + { + if (m != null) + { + System.out.println(m); + } + } + } + } + + final void commandPoint(int id) + { + synchronized (processedLock) + { + this.commandsIn = id; + if (!incomingInit) + { + incomingInit = true; + maxProcessed = commandsIn - 1; + syncPoint = maxProcessed; + } + } } public int getCommandsOut() @@ -127,6 +307,11 @@ public class Session extends Invoker final void identify(Method cmd) { + if (!incomingInit) + { + throw new IllegalStateException(); + } + int id = nextCommandId(); cmd.setId(id); @@ -160,11 +345,19 @@ public class Session extends Invoker public void processed(Range range) { - log.debug("%s processed(%s)", this, range); + log.debug("%s processed(%s) %s %s", this, range, syncPoint, maxProcessed); boolean flush; synchronized (processedLock) { + log.debug("%s", processed); + + if (ge(range.getUpper(), commandsIn)) + { + throw new IllegalArgumentException + ("range exceeds max received command-id: " + range); + } + processed.add(range); Range first = processed.getFirst(); int lower = first.getLower(); @@ -174,8 +367,12 @@ public class Session extends Invoker { maxProcessed = max(maxProcessed, upper); } - flush = lt(old, syncPoint) && ge(maxProcessed, syncPoint); - syncPoint = maxProcessed; + boolean synced = ge(maxProcessed, syncPoint); + flush = lt(old, syncPoint) && synced; + if (synced) + { + syncPoint = maxProcessed; + } } if (flush) { @@ -183,6 +380,19 @@ public class Session extends Invoker } } + void flushExpected() + { + RangeSet rs = new RangeSet(); + synchronized (processedLock) + { + if (incomingInit) + { + rs.add(commandsIn); + } + } + sessionExpected(rs, null); + } + public void flushProcessed(Option ... options) { RangeSet copy; @@ -190,7 +400,15 @@ public class Session extends Invoker { copy = processed.copy(); } - sessionCompleted(copy, options); + + synchronized (commands) + { + if (state == DETACHED || state == CLOSING) + { + return; + } + sessionCompleted(copy, options); + } } void knownComplete(RangeSet kc) @@ -228,21 +446,7 @@ public class Session extends Invoker } } - public void attach(Channel channel) - { - this.channel = channel; - channel.setSession(this); - } - - public Method getCommand(int id) - { - synchronized (commands) - { - return commands.get(id); - } - } - - boolean complete(int lower, int upper) + protected boolean complete(int lower, int upper) { //avoid autoboxing if(log.isDebugEnabled()) @@ -254,57 +458,210 @@ public class Session extends Invoker int old = maxComplete; for (int id = max(maxComplete, lower); le(id, upper); id++) { - commands.remove(id); + int idx = mod(id, commands.length); + Method m = commands[idx]; + if (m != null) + { + commandBytes -= m.getBodySize(); + m.complete(); + commands[idx] = null; + } } if (le(lower, maxComplete + 1)) { maxComplete = max(maxComplete, upper); } - log.debug("%s commands remaining: %s", this, commands); + log.debug("%s commands remaining: %s", this, commandsOut - maxComplete); commands.notifyAll(); return gt(maxComplete, old); } } - public void invoke(Method m) + void received(Method m) { - if (closed.get()) + m.delegate(this, delegate); + } + + private void send(Method m) + { + m.setChannel(channel); + connection.send(m); + + if (!m.isBatch()) { - List<ExecutionException> exc = getExceptions(); - if (!exc.isEmpty()) - { - throw new SessionException(exc); - } - else if (close != null) - { - throw new ConnectionException(close); - } - else - { - throw new SessionClosedException(); - } + connection.flush(); } + } + + protected boolean isFull(int id) + { + return isCommandsFull(id) || isBytesFull(); + } + + protected boolean isBytesFull() + { + return commandBytes >= byteLimit; + } + + protected boolean isCommandsFull(int id) + { + return id - maxComplete >= commands.length; + } + + public void invoke(Method m) + { + invoke(m,(Runnable)null); + } + public void invoke(Method m, Runnable postIdSettingAction) + { if (m.getEncodedTrack() == Frame.L4) { + if (m.hasPayload()) + { + acquireCredit(); + } + synchronized (commands) { - int next = commandsOut++; + if (state == DETACHED && m.isUnreliable()) + { + Thread current = Thread.currentThread(); + if (!current.equals(resumer)) + { + return; + } + } + + if (state != OPEN && state != CLOSED) + { + Thread current = Thread.currentThread(); + if (!current.equals(resumer)) + { + Waiter w = new Waiter(commands, timeout); + while (w.hasTime() && (state != OPEN && state != CLOSED)) + { + w.await(); + } + } + } + + switch (state) + { + case OPEN: + break; + case RESUMING: + Thread current = Thread.currentThread(); + if (!current.equals(resumer)) + { + throw new SessionException + ("timed out waiting for resume to finish"); + } + break; + case CLOSED: + ExecutionException exc = getException(); + if (exc != null) + { + throw new SessionException(exc); + } + else + { + throw new SessionClosedException(); + } + default: + throw new SessionException + (String.format + ("timed out waiting for session to become open " + + "(state=%s)", state)); + } + + int next; + next = commandsOut++; m.setId(next); + if(postIdSettingAction != null) + { + postIdSettingAction.run(); + } + + if (isFull(next)) + { + Waiter w = new Waiter(commands, timeout); + while (w.hasTime() && isFull(next) && state != CLOSED) + { + if (state == OPEN || state == RESUMING) + { + try + { + sessionFlush(COMPLETED); + } + catch (SenderException e) + { + if (expiry > 0) + { + // if expiry is > 0 then this will + // happen again on resume + log.error(e, "error sending flush (full replay buffer)"); + } + else + { + e.rethrow(); + } + } + } + w.await(); + } + } + + if (state == CLOSED) + { + ExecutionException exc = getException(); + if (exc != null) + { + throw new SessionException(exc); + } + else + { + throw new SessionClosedException(); + } + } + + if (isFull(next)) + { + throw new SessionException("timed out waiting for completion"); + } + if (next == 0) { sessionCommandPoint(0, 0); } - if (ENABLE_REPLAY) + if ((expiry > 0 && !m.isUnreliable()) || m.hasCompletionListener()) { - commands.put(next, m); + commands[mod(next, commands.length)] = m; + commandBytes += m.getBodySize(); } if (autoSync) { m.setSync(true); } needSync = !m.isSync(); - channel.method(m); + + try + { + send(m); + } + catch (SenderException e) + { + if (expiry > 0) + { + // if expiry is > 0 then this will happen + // again on resume + log.error(e, "error sending command"); + } + else + { + e.rethrow(); + } + } if (autoSync) { sync(); @@ -312,18 +669,39 @@ public class Session extends Invoker // flush every 64K commands to avoid ambiguity on // wraparound - if ((next % 65536) == 0) + if (shouldIssueFlush(next)) { - sessionFlush(COMPLETED); + try + { + sessionFlush(COMPLETED); + } + catch (SenderException e) + { + if (expiry > 0) + { + // if expiry is > 0 then this will happen + // again on resume + log.error(e, "error sending flush (periodic)"); + } + else + { + e.rethrow(); + } + } } } } else { - channel.method(m); + send(m); } } + protected boolean shouldIssueFlush(int next) + { + return (next % 65536) == 0; + } + public void sync() { sync(timeout); @@ -341,31 +719,23 @@ public class Session extends Invoker executionSync(SYNC); } - long start = System.currentTimeMillis(); - long elapsed = 0; - while (!closed.get() && elapsed < timeout && lt(maxComplete, point)) + Waiter w = new Waiter(commands, timeout); + while (w.hasTime() && state != CLOSED && lt(maxComplete, point)) { - try { - log.debug("%s waiting for[%d]: %d, %s", this, point, - maxComplete, commands); - commands.wait(timeout - elapsed); - elapsed = System.currentTimeMillis() - start; - } - catch (InterruptedException e) - { - throw new RuntimeException(e); - } + log.debug("%s waiting for[%d]: %d, %s", this, point, + maxComplete, commands); + w.await(); } if (lt(maxComplete, point)) { - if (closed.get()) + if (state == CLOSED) { - throw new SessionException(getExceptions()); + throw new SessionException(getException()); } else { - throw new RuntimeException + throw new SessionException (String.format ("timed out waiting for sync: complete = %s, point = %s", maxComplete, point)); } @@ -375,8 +745,7 @@ public class Session extends Invoker private Map<Integer,ResultFuture<?>> results = new HashMap<Integer,ResultFuture<?>>(); - private List<ExecutionException> exceptions = - new ArrayList<ExecutionException>(); + private ExecutionException exception = null; void result(int command, Struct result) { @@ -388,11 +757,17 @@ public class Session extends Invoker future.set(result); } - void addException(ExecutionException exc) + void setException(ExecutionException exc) { - synchronized (exceptions) + synchronized (results) { - exceptions.add(exc); + if (exception != null) + { + throw new IllegalStateException + (String.format + ("too many exceptions: %s, %s", exception, exc)); + } + exception = exc; } } @@ -403,11 +778,11 @@ public class Session extends Invoker this.close = close; } - List<ExecutionException> getExceptions() + ExecutionException getException() { - synchronized (exceptions) + synchronized (results) { - return new ArrayList<ExecutionException>(exceptions); + return exception; } } @@ -450,20 +825,11 @@ public class Session extends Invoker { synchronized (this) { - long start = System.currentTimeMillis(); - long elapsed = 0; - while (!closed.get() && timeout - elapsed > 0 && !isDone()) + Waiter w = new Waiter(this, timeout); + while (w.hasTime() && state != CLOSED && !isDone()) { - try - { - log.debug("%s waiting for result: %s", Session.this, this); - wait(timeout - elapsed); - elapsed = System.currentTimeMillis() - start; - } - catch (InterruptedException e) - { - throw new RuntimeException(e); - } + log.debug("%s waiting for result: %s", Session.this, this); + w.await(); } } @@ -471,13 +837,15 @@ public class Session extends Invoker { return result; } - else if (closed.get()) + else if (state == CLOSED) { - throw new SessionException(getExceptions()); + throw new SessionException(getException()); } else { - return null; + throw new SessionException + (String.format("%s timed out waiting for result: %s", + Session.this, this)); } } @@ -520,25 +888,29 @@ public class Session extends Invoker public void close() { - sessionRequestTimeout(0); - sessionDetach(name); synchronized (commands) { - long start = System.currentTimeMillis(); - long elapsed = 0; - try + state = CLOSING; + // XXX: we manually set the expiry to zero here to + // simulate full session recovery in brokers that don't + // support it, we should remove this line when there is + // broker support for full session resume: + expiry = 0; + sessionRequestTimeout(0); + sessionDetach(name.getBytes()); + Waiter w = new Waiter(commands, timeout); + while (w.hasTime() && state != CLOSED) { - while (!closed.get() && elapsed < timeout) - { - commands.wait(timeout - elapsed); - elapsed = System.currentTimeMillis() - start; - } + w.await(); } - catch (InterruptedException e) + + if (state != CLOSED) { - throw new RuntimeException(e); + throw new SessionException("close() timed out"); } } + + connection.removeSession(this); } public void exception(Throwable t) @@ -548,28 +920,43 @@ public class Session extends Invoker public void closed() { - closed.set(true); synchronized (commands) { + if (expiry == 0 || getException() != null) + { + state = CLOSED; + } + else + { + state = DETACHED; + } + commands.notifyAll(); - } - synchronized (results) - { - for (ResultFuture<?> result : results.values()) + + synchronized (results) { - synchronized(result) + for (ResultFuture<?> result : results.values()) { - result.notifyAll(); + synchronized(result) + { + result.notifyAll(); + } } } + if(state == CLOSED) + { + delegate.closed(this); + } + else + { + delegate.detached(this); + } } - channel.setSession(null); - channel = null; } public String toString() { - return String.format("ssn:%s", str(name)); + return String.format("ssn:%s", name); } } diff --git a/java/common/src/main/java/org/apache/qpid/transport/SessionClosedException.java b/java/common/src/main/java/org/apache/qpid/transport/SessionClosedException.java index d2c54cf339..64f9039484 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/SessionClosedException.java +++ b/java/common/src/main/java/org/apache/qpid/transport/SessionClosedException.java @@ -33,7 +33,17 @@ public class SessionClosedException extends SessionException public SessionClosedException() { - super(Collections.EMPTY_LIST); + this(null); + } + + public SessionClosedException(Throwable cause) + { + super("session closed", null, cause); + } + + @Override public void rethrow() + { + throw new SessionClosedException(this); } } diff --git a/java/common/src/main/java/org/apache/qpid/transport/SessionDelegate.java b/java/common/src/main/java/org/apache/qpid/transport/SessionDelegate.java index 5e4336f988..6146f029b2 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/SessionDelegate.java +++ b/java/common/src/main/java/org/apache/qpid/transport/SessionDelegate.java @@ -20,7 +20,7 @@ */ package org.apache.qpid.transport; -import org.apache.qpid.transport.network.Frame; +import org.apache.qpid.transport.util.Logger; /** @@ -29,10 +29,12 @@ import org.apache.qpid.transport.network.Frame; * @author Rafael H. Schloming */ -public abstract class SessionDelegate +public class SessionDelegate extends MethodDelegate<Session> implements ProtocolDelegate<Session> { + private static final Logger log = Logger.get(SessionDelegate.class); + public void init(Session ssn, ProtocolHeader hdr) { } public void control(Session ssn, Method method) { @@ -42,7 +44,7 @@ public abstract class SessionDelegate public void command(Session ssn, Method method) { ssn.identify(method); method.dispatch(ssn, this); - if (!method.hasPayloadSegment()) + if (!method.hasPayload()) { ssn.processed(method); } @@ -50,14 +52,21 @@ public abstract class SessionDelegate public void error(Session ssn, ProtocolError error) { } - @Override public void executionResult(Session ssn, ExecutionResult result) + public void handle(Session ssn, Method method) { - ssn.result(result.getCommandId(), result.getValue()); + log.warn("UNHANDLED: [%s] %s", ssn, method); } - @Override public void executionException(Session ssn, ExecutionException exc) + @Override public void sessionAttached(Session ssn, SessionAttached atc) { - ssn.addException(exc); + ssn.setState(Session.State.OPEN); + } + + @Override public void sessionTimeout(Session ssn, SessionTimeout t) + { + // XXX: we ignore this right now, we should uncomment this + // when full session resume is supported: + // ssn.setExpiry(t.getTimeout()); } @Override public void sessionCompleted(Session ssn, SessionCompleted cmp) @@ -108,13 +117,13 @@ public abstract class SessionDelegate } if (flush.getExpected()) { - throw new Error("not implemented"); + ssn.flushExpected(); } } @Override public void sessionCommandPoint(Session ssn, SessionCommandPoint scp) { - ssn.commandsIn = scp.getCommandId(); + ssn.commandPoint(scp.getCommandId()); } @Override public void executionSync(Session ssn, ExecutionSync sync) @@ -122,4 +131,64 @@ public abstract class SessionDelegate ssn.syncPoint(); } + @Override public void executionResult(Session ssn, ExecutionResult result) + { + ssn.result(result.getCommandId(), result.getValue()); + } + + @Override public void executionException(Session ssn, ExecutionException exc) + { + ssn.setException(exc); + } + + @Override public void messageTransfer(Session ssn, MessageTransfer xfr) + { + ssn.getSessionListener().message(ssn, xfr); + } + + @Override public void messageSetFlowMode(Session ssn, MessageSetFlowMode sfm) + { + if ("".equals(sfm.getDestination()) && + MessageFlowMode.CREDIT.equals(sfm.getFlowMode())) + { + ssn.setFlowControl(true); + } + else + { + super.messageSetFlowMode(ssn, sfm); + } + } + + @Override public void messageFlow(Session ssn, MessageFlow flow) + { + if ("".equals(flow.getDestination()) && + MessageCreditUnit.MESSAGE.equals(flow.getUnit())) + { + ssn.addCredit((int) flow.getValue()); + } + else + { + super.messageFlow(ssn, flow); + } + } + + @Override public void messageStop(Session ssn, MessageStop stop) + { + if ("".equals(stop.getDestination())) + { + ssn.drainCredit(); + } + else + { + super.messageStop(ssn, stop); + } + } + + public void closed(Session session) + { + } + + public void detached(Session session) + { + } } diff --git a/java/common/src/main/java/org/apache/qpid/transport/SessionException.java b/java/common/src/main/java/org/apache/qpid/transport/SessionException.java index dc294b2206..c4fc9558a1 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/SessionException.java +++ b/java/common/src/main/java/org/apache/qpid/transport/SessionException.java @@ -27,20 +27,35 @@ import java.util.List; * */ -public class SessionException extends RuntimeException +public class SessionException extends TransportException { - private List<ExecutionException> exceptions; + private ExecutionException exception; - public SessionException(List<ExecutionException> exceptions) + public SessionException(String message, ExecutionException exception, Throwable cause) { - super(exceptions.isEmpty() ? "" : exceptions.toString()); - this.exceptions = exceptions; + super(message, cause); + this.exception = exception; } - public List<ExecutionException> getExceptions() + public SessionException(ExecutionException exception) { - return exceptions; + this(String.valueOf(exception), exception, null); + } + + public SessionException(String message) + { + this(message, null, null); + } + + public ExecutionException getException() + { + return exception; + } + + @Override public void rethrow() + { + throw new SessionException(getMessage(), exception, this); } } diff --git a/java/common/src/main/java/org/apache/qpid/transport/SessionListener.java b/java/common/src/main/java/org/apache/qpid/transport/SessionListener.java new file mode 100644 index 0000000000..eb650eb9ed --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/transport/SessionListener.java @@ -0,0 +1,42 @@ +/* + * + * 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.transport; + + +/** + * SessionListener + * + */ + +public interface SessionListener +{ + + void opened(Session session); + + void resumed(Session session); + + void message(Session ssn, MessageTransfer xfr); + + void exception(Session session, SessionException exception); + + void closed(Session session); + +} diff --git a/java/common/src/main/java/org/apache/qpid/transport/Sink.java b/java/common/src/main/java/org/apache/qpid/transport/Sink.java index 8653acedbe..88870284f6 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/Sink.java +++ b/java/common/src/main/java/org/apache/qpid/transport/Sink.java @@ -31,7 +31,7 @@ import org.apache.qpid.transport.network.io.IoAcceptor; * */ -public class Sink extends SessionDelegate +public class Sink implements SessionListener { private static final String FORMAT_HDR = "%-12s %-18s %-18s %-18s"; @@ -85,7 +85,11 @@ public class Sink extends SessionDelegate return String.format("%d/%.2f", count, ((double) bytes)/(1024*1024)); } - public void messageTransfer(Session ssn, MessageTransfer xfr) + public void opened(Session ssn) {} + + public void resumed(Session ssn) {} + + public void message(Session ssn, MessageTransfer xfr) { count++; bytes += xfr.getBody().remaining(); @@ -101,30 +105,27 @@ public class Sink extends SessionDelegate ssn.processed(xfr); } - public static final void main(String[] args) throws IOException + public void exception(Session ssn, SessionException exc) { - ConnectionDelegate delegate = new ConnectionDelegate() - { + exc.printStackTrace(); + } - public SessionDelegate getSessionDelegate() - { - return new Sink(); - } + public void closed(Session ssn) {} - public void exception(Throwable t) + public static final void main(String[] args) throws IOException + { + ConnectionDelegate delegate = new ServerDelegate() + { + @Override public Session getSession(Connection conn, SessionAttach atc) { - t.printStackTrace(); + Session ssn = super.getSession(conn, atc); + ssn.setSessionListener(new Sink()); + return ssn; } - - public void closed() {} }; - //hack - delegate.setUsername("guest"); - delegate.setPassword("guest"); - IoAcceptor ioa = new IoAcceptor - ("0.0.0.0", 5672, new ConnectionBinding(delegate)); + ("0.0.0.0", 5672, ConnectionBinding.get(delegate)); System.out.println (String.format (FORMAT_HDR, "Session", "Count/MBytes", "Cumulative Rate", "Interval Rate")); diff --git a/java/common/src/main/java/org/apache/qpid/transport/Struct.java b/java/common/src/main/java/org/apache/qpid/transport/Struct.java index 097a6de1b5..22bd9f34ad 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/Struct.java +++ b/java/common/src/main/java/org/apache/qpid/transport/Struct.java @@ -42,7 +42,7 @@ public abstract class Struct implements Encodable return StructFactory.create(type); } - protected boolean dirty = true; + boolean dirty = true; public boolean isDirty() { diff --git a/java/common/src/main/java/org/apache/qpid/transport/TransportException.java b/java/common/src/main/java/org/apache/qpid/transport/TransportException.java index 5ef15154fc..0de190dfad 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/TransportException.java +++ b/java/common/src/main/java/org/apache/qpid/transport/TransportException.java @@ -43,4 +43,9 @@ public class TransportException extends RuntimeException super(cause); } + public void rethrow() + { + throw new TransportException(getMessage(), this); + } + } diff --git a/java/common/src/main/java/org/apache/qpid/transport/codec/BBDecoder.java b/java/common/src/main/java/org/apache/qpid/transport/codec/BBDecoder.java index dd634eb94a..6f7a2fa3b2 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/codec/BBDecoder.java +++ b/java/common/src/main/java/org/apache/qpid/transport/codec/BBDecoder.java @@ -25,16 +25,14 @@ import java.nio.ByteOrder; import org.apache.qpid.transport.Binary; - /** - * BBDecoder + * Byte Buffer Decoder. + * Decoder concrete implementor using a backing byte buffer for decoding data. * * @author Rafael H. Schloming */ - public final class BBDecoder extends AbstractDecoder { - private ByteBuffer in; public void init(ByteBuffer in) @@ -93,4 +91,54 @@ public final class BBDecoder extends AbstractDecoder return in.getLong(); } -} + public byte[] readBin128() + { + byte[] result = new byte[16]; + get(result); + return result; + } + + public byte[] readBytes(int howManyBytes) + { + byte[] result = new byte[howManyBytes]; + get(result); + return result; + } + + public double readDouble() + { + return in.getDouble(); + } + + public float readFloat() + { + return in.getFloat(); + } + + public short readInt16() + { + return in.getShort(); + } + + public int readInt32() + { + return in.getInt(); + } + + public byte readInt8() + { + return in.get(); + } + + public byte[] readReaminingBytes() + { + byte[] result = new byte[in.limit() - in.position()]; + get(result); + return result; + } + + public long readInt64() + { + return in.getLong(); + } +}
\ No newline at end of file diff --git a/java/common/src/main/java/org/apache/qpid/transport/codec/BBEncoder.java b/java/common/src/main/java/org/apache/qpid/transport/codec/BBEncoder.java index 390de881ab..d18a0f64db 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/codec/BBEncoder.java +++ b/java/common/src/main/java/org/apache/qpid/transport/codec/BBEncoder.java @@ -26,14 +26,13 @@ import java.nio.ByteOrder; /** - * BBEncoder - * + * Byte Buffer Encoder. + * Encoder concrete implementor using a backing byte buffer for encoding data. + * * @author Rafael H. Schloming */ - public final class BBEncoder extends AbstractEncoder { - private ByteBuffer out; private int segment; @@ -60,12 +59,23 @@ public final class BBEncoder extends AbstractEncoder return slice; } + public ByteBuffer buffer() + { + int pos = out.position(); + out.position(segment); + ByteBuffer slice = out.slice(); + slice.limit(pos - segment); + out.position(pos); + return slice; + } + private void grow(int size) { ByteBuffer old = out; int capacity = old.capacity(); out = ByteBuffer.allocate(Math.max(capacity + size, 2*capacity)); out.order(ByteOrder.BIG_ENDIAN); + old.flip(); out.put(old); } @@ -229,4 +239,96 @@ public final class BBEncoder extends AbstractEncoder out.putInt(pos, (cur - pos - 4)); } -} + public void writeDouble(double aDouble) + { + try + { + out.putDouble(aDouble); + } catch(BufferOverflowException exception) + { + grow(8); + out.putDouble(aDouble); + } + } + + public void writeInt16(short aShort) + { + try + { + out.putShort(aShort); + } catch(BufferOverflowException exception) + { + grow(2); + out.putShort(aShort); + } + } + + public void writeInt32(int anInt) + { + try + { + out.putInt(anInt); + } catch(BufferOverflowException exception) + { + grow(4); + out.putInt(anInt); + } + } + + public void writeInt64(long aLong) + { + try + { + out.putLong(aLong); + } catch(BufferOverflowException exception) + { + grow(8); + out.putLong(aLong); + } + } + + public void writeInt8(byte aByte) + { + try + { + out.put(aByte); + } catch(BufferOverflowException exception) + { + grow(1); + out.put(aByte); + } + } + + public void writeBin128(byte[] byteArray) + { + byteArray = (byteArray != null) ? byteArray : new byte [16]; + + assert byteArray.length == 16; + + try + { + out.put(byteArray); + } catch(BufferOverflowException exception) + { + grow(16); + out.put(byteArray); + } + } + + public void writeFloat(float aFloat) + { + try + { + out.putFloat(aFloat); + } catch(BufferOverflowException exception) + { + grow(4); + out.putFloat(aFloat); + } + } + + public void writeMagicNumber() + { + out.put("AM2".getBytes()); + } +}
\ No newline at end of file diff --git a/java/common/src/main/java/org/apache/qpid/transport/codec/Decoder.java b/java/common/src/main/java/org/apache/qpid/transport/codec/Decoder.java index 50e787ccb2..a4df5b5fcb 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/codec/Decoder.java +++ b/java/common/src/main/java/org/apache/qpid/transport/codec/Decoder.java @@ -29,40 +29,255 @@ import org.apache.qpid.transport.Struct; /** - * Decoder - * + * Decoder interface. + * Each concrete implementor must specify how to decode given values. + * * @author Rafael H. Schloming */ - public interface Decoder { - + /** + * Tells whether there are any remaining byte(s) to be read. + * + * @return true if there are remaining bytes, false otherwise. + */ boolean hasRemaining(); + /** + * The uint8 type is an 8-bit unsigned integral value. + * + * @return an 8-bit unsigned integral value. + */ short readUint8(); + + /** + *The uint16 type is a 16-bit unsigned integral value encoded in network byte order. + * + * @return a 16-bit unsigned integral value encoded in network byte order. + */ int readUint16(); + + /** + *The uint32 type is a 32-bit unsigned integral value encoded in network byte order. + * + * @return a 32-bit unsigned integral value encoded in network byte order. + */ long readUint32(); + + /** + * The uint64 type is a 64-bit unsigned integral value encoded in network byte order. + * + * @return a 64-bit unsigned integral value encoded in network byte order. + */ long readUint64(); + /** + * The datetime type encodes a date and time using the 64 bit POSIX time_t format. + * + * @return a date and time using the 64 bit POSIX time_t format. + */ long readDatetime(); + + /** + * The uuid type encodes a universally unique id as defined by RFC-4122. + * The format and operations for this type can be found in section 4.1.2 of RFC-4122. + * + * return a universally unique id as defined by RFC-4122. + */ UUID readUuid(); + /** +// *The sequence-no type encodes, in network byte order, a serial number as defined in RFC-1982. + * + * @return a serial number as defined in RFC-1982. + */ int readSequenceNo(); + RangeSet readSequenceSet(); // XXX RangeSet readByteRanges(); // XXX + /** + * The str8 type encodes up to 255 octets worth of UTF-8 unicode. + * The number of octets of unicode is first encoded as an 8-bit unsigned integral value. + * This is followed by the actual UTF-8 unicode. + * Note that the encoded size refers to the number of octets of unicode, not necessarily the number of characters since + * the UTF-8 unicode may include multi-byte character sequences. + * + * @return a string. + */ String readStr8(); + + /** + * The str16 type encodes up to 65535 octets worth of UTF-8 unicode. + * The number of octets is first encoded as a 16-bit unsigned integral value in network byte order. + * This is followed by the actual UTF-8 unicode. + * Note that the encoded size refers to the number of octets of unicode, not necessarily the number of unicode + * characters since the UTF-8 unicode may include multi-byte character sequences. + * + * return a string. + */ String readStr16(); + /** + * The vbin8 type encodes up to 255 octets of opaque binary data. + * + * return a byte array. + */ byte[] readVbin8(); + + /** + * The vbin16 type encodes up to 65535 octets of opaque binary data. + * + * @return the corresponding byte array. + */ byte[] readVbin16(); + + /** + * The vbin32 type encodes up to 4294967295 octets of opaque binary data. + * + * @return the corresponding byte array. + */ byte[] readVbin32(); + /** + * The struct32 type describes any coded struct with a 32-bit (4 octet) size. + * The type is restricted to be only coded structs with a 32-bit size, consequently the first six octets of any encoded + * value for this type MUST always contain the size, class-code, and struct-code in that order. + * The size is encoded as a 32-bit unsigned integral value in network byte order that is equal to the size of the + * encoded field-data, packing-flags, class-code, and struct-code. The class-code is a single octet that may be set to any + * valid class code. + * The struct-code is a single octet that may be set to any valid struct code within the given class-code. + * The first six octets are then followed by the packing flags and encoded field data. + * The presence and quantity of packingflags, as well as the specific fields are determined by the struct definition + * identified with the encoded class-code and struct-code. + * + * @return the decoded struct. + */ Struct readStruct32(); + + /** + * A map is a set of distinct keys where each key has an associated (type,value) pair. + * The triple of the key, type, and value, form an entry within a map. Each entry within a given map MUST have a + * distinct key. + * A map is encoded as a size in octets, a count of the number of entries, followed by the encoded entries themselves. + * An encoded map may contain up to (4294967295 - 4) octets worth of encoded entries. + * The size is encoded as a 32-bit unsigned integral value in network byte order equal to the number of octets worth of + * encoded entries plus 4. (The extra 4 octets is added for the entry count.) + * The size is then followed by the number of entries encoded as a 32-bit unsigned integral value in network byte order. + * Finally the entries are encoded sequentially. + * An entry is encoded as the key, followed by the type, and then the value. The key is always a string encoded as a str8. + * The type is a single octet that may contain any valid AMQP type code. + * The value is encoded according to the rules defined by the type code for that entry. + * + * @return the decoded map. + */ Map<String,Object> readMap(); + + /** + * A list is an ordered sequence of (type, value) pairs. The (type, value) pair forms an item within the list. + * The list may contain items of many distinct types. A list is encoded as a size in octets, followed by a count of the + * number of items, followed by the items themselves encoded in their defined order. + * An encoded list may contain up to (4294967295 - 4) octets worth of encoded items. + * The size is encoded as a 32-bit unsigned integral value in network byte order equal to the number of octets worth + * of encoded items plus 4. (The extra4 octets is added for the item count.) + * The size is then followed by the number of items encoded as a 32-bit unsigned integral value in network byte order. + * Finally the items are encoded sequentially in their defined order. + * An item is encoded as the type followed by the value. The type is a single octet that may contain any valid AMQP type + * code. + * The value is encoded according to the rules defined by the type code for that item. + * + * @return the decoded list. + */ List<Object> readList(); + + /** + * An array is an ordered sequence of values of the same type. + * The array is encoded in as a size in octets, followed by a type code, then a count of the number values in the array, + * and finally the values encoded in their defined order. + * An encoded array may contain up to (4294967295 - 5) octets worth of encoded values. + * The size is encoded as a 32-bit unsigned integral value in network byte order equal to the number of octets worth of + * encoded values plus 5. (The extra 5 octets consist of 4 octets for the count of the number of values, and one octet to + * hold the type code for the items inthe array.) + * The size is then followed by a single octet that may contain any valid AMQP type code. + * The type code is then followed by the number of values encoded as a 32-bit unsigned integral value in network byte + * order. + * Finally the values are encoded sequentially in their defined order according to the rules defined by the type code for + * the array. + * + * @return the decoded array. + */ List<Object> readArray(); + /** + * + * @param type the type of the struct. + * @return the decoded struct. + */ Struct readStruct(int type); - -} + + /** + * The float type encodes a single precision 32-bit floating point number. + * The format and operations are defined by the IEEE 754 standard for 32-bit single precision floating point numbers. + * + * @return the decoded float. + */ + float readFloat(); + + /** + * The double type encodes a double precision 64-bit floating point number. + * The format and operations are defined by the IEEE 754 standard for 64-bit double precision floating point numbers. + * + * @return the decoded double + */ + double readDouble(); + + /** + * The int8 type is a signed integral value encoded using an 8-bit two's complement representation. + * + * @return the decoded integer. + */ + byte readInt8(); + + /** + * The int16 type is a signed integral value encoded using a 16-bit two's complement representation in network byte order. + * + * @return the decoded integer. + */ + short readInt16(); + + /** + * The int32 type is a signed integral value encoded using a 32-bit two's complement representation in network byte order. + * + * @return the decoded integer. + */ + int readInt32(); + + /** + * The int64 type is a signed integral value encoded using a 64-bit two's complement representation in network byte order. + * + * @return the decoded integer (as long). + */ + long readInt64(); + + /** + * The bin128 type consists of 16 consecutive octets of opaque binary data. + * + * @return the decoded byte array. + */ + byte [] readBin128(); + + /** + * Reads the remaining bytes on the underlying buffer. + * + * @return the remaining bytes on the underlying buffer. + */ + byte[] readReaminingBytes (); + + /** + * Reads the given number of bytes. + * + * @param howManyBytes how many bytes need to be read? + * @return a byte array containing the requested data. + */ + byte[] readBytes (int howManyBytes); +}
\ No newline at end of file diff --git a/java/common/src/main/java/org/apache/qpid/transport/codec/Encodable.java b/java/common/src/main/java/org/apache/qpid/transport/codec/Encodable.java index 2aefafd19c..37ce8a5cb7 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/codec/Encodable.java +++ b/java/common/src/main/java/org/apache/qpid/transport/codec/Encodable.java @@ -26,12 +26,19 @@ package org.apache.qpid.transport.codec; * * @author Rafael H. Schloming */ - public interface Encodable { + /** + * Encodes this encodable using the given encoder. + * + * @param encoder the encoder. + */ + void write(Encoder encoder); - void write(Encoder enc); - - void read(Decoder dec); - -} + /** + * Decodes this encodable using the given decoder. + * + * @param decoder the decoder. + */ + void read(Decoder decoder); +}
\ No newline at end of file diff --git a/java/common/src/main/java/org/apache/qpid/transport/codec/Encoder.java b/java/common/src/main/java/org/apache/qpid/transport/codec/Encoder.java index 2d8d13e80a..7d4f02af31 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/codec/Encoder.java +++ b/java/common/src/main/java/org/apache/qpid/transport/codec/Encoder.java @@ -29,38 +29,254 @@ import org.apache.qpid.transport.Struct; /** - * Encoder + * Encoder interface. + * Each concrete implementor must specify how to encode given values. * * @author Rafael H. Schloming */ - public interface Encoder { - + /** + * The uint8 type is an 8-bit unsigned integral value. + * + * @param b the unsigned integer to be encoded. + */ void writeUint8(short b); + + /** + *The uint16 type is a 16-bit unsigned integral value encoded in network byte order. + * + * @param s the unsigned integer to be encoded. + */ void writeUint16(int s); + + /** + *The uint32 type is a 32-bit unsigned integral value encoded in network byte order. + * + * @param i the unsigned integer to be encoded. + */ void writeUint32(long i); + + /** + * The uint64 type is a 64-bit unsigned integral value encoded in network byte order. + * + * @param b the unsigned integer to be encoded. + */ void writeUint64(long l); + /** + * The datetime type encodes a date and time using the 64 bit POSIX time_t format. + * + * @param l the datetime (as long) to be encoded. + */ void writeDatetime(long l); + + /** + * The uuid type encodes a universally unique id as defined by RFC-4122. + * The format and operations for this type can be found in section 4.1.2 of RFC-4122. + * + * @param uuid the uuid to be encoded. + */ void writeUuid(UUID uuid); + /** + *The sequence-no type encodes, in network byte order, a serial number as defined in RFC-1982. + * + * @param s the sequence number to be encoded. + */ void writeSequenceNo(int s); + void writeSequenceSet(RangeSet ranges); // XXX void writeByteRanges(RangeSet ranges); // XXX + /** + * The str8 type encodes up to 255 octets worth of UTF-8 unicode. + * The number of octets of unicode is first encoded as an 8-bit unsigned integral value. + * This is followed by the actual UTF-8 unicode. + * Note that the encoded size refers to the number of octets of unicode, not necessarily the number of characters since + * the UTF-8 unicode may include multi-byte character sequences. + * + * @param s the string to be encoded. + */ void writeStr8(String s); + + /** + * The str16 type encodes up to 65535 octets worth of UTF-8 unicode. + * The number of octets is first encoded as a 16-bit unsigned integral value in network byte order. + * This is followed by the actual UTF-8 unicode. + * Note that the encoded size refers to the number of octets of unicode, not necessarily the number of unicode + * characters since the UTF-8 unicode may include multi-byte character sequences. + * + * @param s the string to be encoded. + */ void writeStr16(String s); + /** + * The vbin8 type encodes up to 255 octets of opaque binary data. + * The number of octets is first encoded as an 8-bit unsigned integral value. + * This is followed by the actual data. + * + * @param bytes the byte array to be encoded. + */ void writeVbin8(byte[] bytes); + + /** + * The vbin16 type encodes up to 65535 octets of opaque binary data. + * The number of octets is first encoded as a 16-bit unsigned integral value in network byte order. + * This is followed by the actual data. + * + * @param bytes the byte array to be encoded. + */ void writeVbin16(byte[] bytes); + + /** + * The vbin32 type encodes up to 4294967295 octets of opaque binary data. + * The number of octets is first encoded as a 32-bit unsigned integral value in network byte order. + * This is followed by the actual data. + * + * @param bytes the byte array to be encoded. + */ void writeVbin32(byte[] bytes); - void writeStruct32(Struct s); + /** + * The struct32 type describes any coded struct with a 32-bit (4 octet) size. + * The type is restricted to be only coded structs with a 32-bit size, consequently the first six octets of any encoded + * value for this type MUST always contain the size, class-code, and struct-code in that order. + * The size is encoded as a 32-bit unsigned integral value in network byte order that is equal to the size of the + * encoded field-data, packing-flags, class-code, and struct-code. The class-code is a single octet that may be set to any + * valid class code. + * The struct-code is a single octet that may be set to any valid struct code within the given class-code. + * The first six octets are then followed by the packing flags and encoded field data. + * The presence and quantity of packingflags, as well as the specific fields are determined by the struct definition + * identified with the encoded class-code and struct-code. + * + * @param struct the struct to be encoded. + */ + void writeStruct32(Struct struct); + + /** + * A map is a set of distinct keys where each key has an associated (type,value) pair. + * The triple of the key, type, and value, form an entry within a map. Each entry within a given map MUST have a + * distinct key. + * A map is encoded as a size in octets, a count of the number of entries, followed by the encoded entries themselves. + * An encoded map may contain up to (4294967295 - 4) octets worth of encoded entries. + * The size is encoded as a 32-bit unsigned integral value in network byte order equal to the number of octets worth of + * encoded entries plus 4. (The extra 4 octets is added for the entry count.) + * The size is then followed by the number of entries encoded as a 32-bit unsigned integral value in network byte order. + * Finally the entries are encoded sequentially. + * An entry is encoded as the key, followed by the type, and then the value. The key is always a string encoded as a str8. + * The type is a single octet that may contain any valid AMQP type code. + * The value is encoded according to the rules defined by the type code for that entry. + * + * @param map the map to be encoded. + */ void writeMap(Map<String,Object> map); + + /** + * A list is an ordered sequence of (type, value) pairs. The (type, value) pair forms an item within the list. + * The list may contain items of many distinct types. A list is encoded as a size in octets, followed by a count of the + * number of items, followed by the items themselves encoded in their defined order. + * An encoded list may contain up to (4294967295 - 4) octets worth of encoded items. + * The size is encoded as a 32-bit unsigned integral value in network byte order equal to the number of octets worth + * of encoded items plus 4. (The extra4 octets is added for the item count.) + * The size is then followed by the number of items encoded as a 32-bit unsigned integral value in network byte order. + * Finally the items are encoded sequentially in their defined order. + * An item is encoded as the type followed by the value. The type is a single octet that may contain any valid AMQP type + * code. + * The value is encoded according to the rules defined by the type code for that item. + * + * @param list the list to be encoded. + */ void writeList(List<Object> list); + + /** + * An array is an ordered sequence of values of the same type. + * The array is encoded in as a size in octets, followed by a type code, then a count of the number values in the array, + * and finally the values encoded in their defined order. + * An encoded array may contain up to (4294967295 - 5) octets worth of encoded values. + * The size is encoded as a 32-bit unsigned integral value in network byte order equal to the number of octets worth of + * encoded values plus 5. (The extra 5 octets consist of 4 octets for the count of the number of values, and one octet to + * hold the type code for the items inthe array.) + * The size is then followed by a single octet that may contain any valid AMQP type code. + * The type code is then followed by the number of values encoded as a 32-bit unsigned integral value in network byte + * order. + * Finally the values are encoded sequentially in their defined order according to the rules defined by the type code for + * the array. + * + * @param array the array to be encoded. + */ void writeArray(List<Object> array); - void writeStruct(int type, Struct s); - -} + /** + * The struct32 type describes any coded struct with a 32-bit (4 octet) size. + * The type is restricted to be only coded structs with a 32-bit size, consequently the first six octets of any encoded + * value for this type MUST always contain the size, class-code, and struct-code in that order. + * The size is encoded as a 32-bit unsigned integral value in network byte order that is equal to the size of the + * encoded field-data, packing-flags, class-code, and struct-code. The class-code is a single octet that may be set to any + * valid class code. + * The struct-code is a single octet that may be set to any valid struct code within the given class-code. + * The first six octets are then followed by the packing flags and encoded field data. + * The presence and quantity of packingflags, as well as the specific fields are determined by the struct definition + * identified with the encoded class-code and struct-code. + * + * @param type the type of the struct. + * @param struct the struct to be encoded. + */ + void writeStruct(int type, Struct struct); + + /** + * The float type encodes a single precision 32-bit floating point number. + * The format and operations are defined by the IEEE 754 standard for 32-bit single precision floating point numbers. + * + * @param aFloat the float to be encoded. + */ + void writeFloat(float aFloat); + + /** + * The double type encodes a double precision 64-bit floating point number. + * The format and operations are defined by the IEEE 754 standard for 64-bit double precision floating point numbers. + * + * @param aDouble the double to be encoded. + */ + void writeDouble(double aDouble); + + /** + * The int8 type is a signed integral value encoded using an 8-bit two's complement representation. + * + * @param aByte the integer to be encoded. + */ + void writeInt8(byte aByte); + + /** + * The int16 type is a signed integral value encoded using a 16-bit two's complement representation in network byte order. + * + * @param aShort the integer to be encoded. + */ + void writeInt16(short aShort); + + /** + * The int32 type is a signed integral value encoded using a 32-bit two's complement representation in network byte order. + * + * @param anInt the integer to be encoded. + */ + void writeInt32(int anInt); + + /** + * The int64 type is a signed integral value encoded using a 64-bit two's complement representation in network byte order. + * + * @param aLong the integer to be encoded. + */ + void writeInt64(long aLong); + + /** + * The bin128 type consists of 16 consecutive octets of opaque binary data. + * + * @param bytes the bytes array to be encoded. + */ + void writeBin128(byte [] bytes); + + /** + * Encodes the AMQP magic number. + */ + void writeMagicNumber(); +}
\ No newline at end of file diff --git a/java/common/src/main/java/org/apache/qpid/transport/network/Assembler.java b/java/common/src/main/java/org/apache/qpid/transport/network/Assembler.java index 4ff8fec206..357caa26e1 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/network/Assembler.java +++ b/java/common/src/main/java/org/apache/qpid/transport/network/Assembler.java @@ -186,10 +186,11 @@ public class Assembler implements Receiver<NetworkEvent>, NetworkDelegate case COMMAND: int commandType = dec.readUint16(); // read in the session header, right now we don't use it - dec.readUint16(); + int hdr = dec.readUint16(); command = Method.create(commandType); + command.setSync((0x0001 & hdr) != 0); command.read(dec); - if (command.hasPayloadSegment()) + if (command.hasPayload()) { incomplete[channel] = command; } diff --git a/java/common/src/main/java/org/apache/qpid/transport/network/ConnectionBinding.java b/java/common/src/main/java/org/apache/qpid/transport/network/ConnectionBinding.java index 6886cb3a5a..8a2aba2e6d 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/network/ConnectionBinding.java +++ b/java/common/src/main/java/org/apache/qpid/transport/network/ConnectionBinding.java @@ -33,23 +33,46 @@ import org.apache.qpid.transport.Sender; * */ -public class ConnectionBinding implements Binding<Connection,ByteBuffer> +public abstract class ConnectionBinding + implements Binding<Connection,ByteBuffer> { - private static final int MAX_FRAME_SIZE = 64 * 1024 - 1; - - private final ConnectionDelegate delegate; + public static Binding<Connection,ByteBuffer> get(final Connection connection) + { + return new ConnectionBinding() + { + public Connection connection() + { + return connection; + } + }; + } - public ConnectionBinding(ConnectionDelegate delegate) + public static Binding<Connection,ByteBuffer> get(final ConnectionDelegate delegate) { - this.delegate = delegate; + return new ConnectionBinding() + { + public Connection connection() + { + Connection conn = new Connection(); + conn.setConnectionDelegate(delegate); + return conn; + } + }; } + public static final int MAX_FRAME_SIZE = 64 * 1024 - 1; + + public abstract Connection connection(); + public Connection endpoint(Sender<ByteBuffer> sender) { + Connection conn = connection(); + // XXX: hardcoded max-frame - return new Connection - (new Disassembler(sender, MAX_FRAME_SIZE), delegate); + Disassembler dis = new Disassembler(sender, MAX_FRAME_SIZE); + conn.setSender(dis); + return conn; } public Receiver<ByteBuffer> receiver(Connection conn) diff --git a/java/common/src/main/java/org/apache/qpid/transport/network/Disassembler.java b/java/common/src/main/java/org/apache/qpid/transport/network/Disassembler.java index 09586b357a..d99ee72d14 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/network/Disassembler.java +++ b/java/common/src/main/java/org/apache/qpid/transport/network/Disassembler.java @@ -20,7 +20,15 @@ */ package org.apache.qpid.transport.network; -import org.apache.qpid.transport.codec.BBEncoder; +import static java.lang.Math.min; +import static org.apache.qpid.transport.network.Frame.FIRST_FRAME; +import static org.apache.qpid.transport.network.Frame.FIRST_SEG; +import static org.apache.qpid.transport.network.Frame.HEADER_SIZE; +import static org.apache.qpid.transport.network.Frame.LAST_FRAME; +import static org.apache.qpid.transport.network.Frame.LAST_SEG; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import org.apache.qpid.transport.Header; import org.apache.qpid.transport.Method; @@ -31,13 +39,7 @@ import org.apache.qpid.transport.ProtocolHeader; import org.apache.qpid.transport.SegmentType; import org.apache.qpid.transport.Sender; import org.apache.qpid.transport.Struct; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -import static org.apache.qpid.transport.network.Frame.*; - -import static java.lang.Math.*; +import org.apache.qpid.transport.codec.BBEncoder; /** @@ -198,7 +200,7 @@ public final class Disassembler implements Sender<ProtocolEvent>, byte flags = FIRST_SEG; - boolean payload = method.hasPayloadSegment(); + boolean payload = method.hasPayload(); if (!payload) { flags |= LAST_SEG; @@ -208,11 +210,14 @@ public final class Disassembler implements Sender<ProtocolEvent>, if (payload) { final Header hdr = method.getHeader(); - final Struct[] structs = hdr.getStructs(); - - for (Struct st : structs) + if (hdr != null) { - enc.writeStruct32(st); + final Struct[] structs = hdr.getStructs(); + + for (Struct st : structs) + { + enc.writeStruct32(st); + } } headerSeg = enc.segment(); } @@ -232,5 +237,9 @@ public final class Disassembler implements Sender<ProtocolEvent>, { throw new IllegalArgumentException("" + error); } - + + public void setIdleTimeout(long l) + { + sender.setIdleTimeout(l); + } } diff --git a/java/common/src/main/java/org/apache/qpid/transport/network/InputHandler.java b/java/common/src/main/java/org/apache/qpid/transport/network/InputHandler.java index 408c95e075..a2885f97bc 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/network/InputHandler.java +++ b/java/common/src/main/java/org/apache/qpid/transport/network/InputHandler.java @@ -39,7 +39,7 @@ import static org.apache.qpid.transport.network.InputHandler.State.*; * @author Rafael H. Schloming */ -public final class InputHandler implements Receiver<ByteBuffer> +public class InputHandler implements Receiver<ByteBuffer> { public enum State @@ -144,10 +144,11 @@ public final class InputHandler implements Receiver<ByteBuffer> return ERROR; } + byte protoClass = input.get(pos + 4); byte instance = input.get(pos + 5); byte major = input.get(pos + 6); byte minor = input.get(pos + 7); - receiver.received(new ProtocolHeader(instance, major, minor)); + receiver.received(new ProtocolHeader(protoClass, instance, major, minor)); needed = Frame.HEADER_SIZE; return FRAME_HDR; case FRAME_HDR: diff --git a/java/common/src/main/java/org/apache/qpid/transport/network/io/InputHandler_0_9.java b/java/common/src/main/java/org/apache/qpid/transport/network/io/InputHandler_0_9.java index b63020913b..ecc5f6d07c 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/network/io/InputHandler_0_9.java +++ b/java/common/src/main/java/org/apache/qpid/transport/network/io/InputHandler_0_9.java @@ -1,4 +1,25 @@ package org.apache.qpid.transport.network.io; +/* + * + * 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. + * + */ + import java.nio.ByteBuffer; diff --git a/java/common/src/main/java/org/apache/qpid/transport/network/io/IoAcceptor.java b/java/common/src/main/java/org/apache/qpid/transport/network/io/IoAcceptor.java index c4559ae6b4..8530240dcc 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/network/io/IoAcceptor.java +++ b/java/common/src/main/java/org/apache/qpid/transport/network/io/IoAcceptor.java @@ -56,6 +56,17 @@ public class IoAcceptor<E> extends Thread setName(String.format("IoAcceptor - %s", socket.getInetAddress())); } + /** + Close the underlying ServerSocket if it has not already been closed. + */ + public void close() throws IOException + { + if (!socket.isClosed()) + { + socket.close(); + } + } + public IoAcceptor(String host, int port, Binding<E,ByteBuffer> binding) throws IOException { @@ -69,7 +80,7 @@ public class IoAcceptor<E> extends Thread try { Socket sock = socket.accept(); - IoTransport<E> transport = new IoTransport<E>(sock, binding); + IoTransport<E> transport = new IoTransport<E>(sock, binding,false); } catch (IOException e) { diff --git a/java/common/src/main/java/org/apache/qpid/transport/network/io/IoReceiver.java b/java/common/src/main/java/org/apache/qpid/transport/network/io/IoReceiver.java index a1fb0371fd..6144edb947 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/network/io/IoReceiver.java +++ b/java/common/src/main/java/org/apache/qpid/transport/network/io/IoReceiver.java @@ -20,13 +20,15 @@ */ package org.apache.qpid.transport.network.io; +import org.apache.qpid.thread.Threading; import org.apache.qpid.transport.Receiver; import org.apache.qpid.transport.TransportException; import org.apache.qpid.transport.util.Logger; -import java.io.InputStream; import java.io.IOException; +import java.io.InputStream; import java.net.Socket; +import java.net.SocketException; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicBoolean; @@ -35,7 +37,7 @@ import java.util.concurrent.atomic.AtomicBoolean; * */ -final class IoReceiver extends Thread +final class IoReceiver implements Runnable { private static final Logger log = Logger.get(IoReceiver.class); @@ -46,6 +48,9 @@ final class IoReceiver extends Thread private final Socket socket; private final long timeout; private final AtomicBoolean closed = new AtomicBoolean(false); + private final Thread receiverThread; + private final boolean shutdownBroken = + ((String) System.getProperties().get("os.name")).matches("(?i).*windows.*"); public IoReceiver(IoTransport transport, Receiver<ByteBuffer> receiver, int bufferSize, long timeout) @@ -55,19 +60,27 @@ final class IoReceiver extends Thread this.bufferSize = bufferSize; this.socket = transport.getSocket(); this.timeout = timeout; - - setDaemon(true); - setName(String.format("IoReceiver - %s", socket.getRemoteSocketAddress())); - start(); + + try + { + receiverThread = Threading.getThreadFactory().createThread(this); + } + catch(Exception e) + { + throw new Error("Error creating IOReceiver thread",e); + } + receiverThread.setDaemon(true); + receiverThread.setName(String.format("IoReceiver - %s", socket.getRemoteSocketAddress())); + receiverThread.start(); } - void close() + void close(boolean block) { if (!closed.getAndSet(true)) { try { - if (((String) System.getProperties().get("os.name")).matches("(?i).*windows.*")) + if (shutdownBroken) { socket.close(); } @@ -75,10 +88,10 @@ final class IoReceiver extends Thread { socket.shutdownInput(); } - if (Thread.currentThread() != this) + if (block && Thread.currentThread() != receiverThread) { - join(timeout); - if (isAlive()) + receiverThread.join(timeout); + if (receiverThread.isAlive()) { throw new TransportException("join timed out"); } @@ -124,12 +137,25 @@ final class IoReceiver extends Thread } catch (Throwable t) { - receiver.exception(t); + if (!(shutdownBroken && + t instanceof SocketException && + t.getMessage().equalsIgnoreCase("socket closed") && + closed.get())) + { + receiver.exception(t); + } } finally { receiver.closed(); - transport.getSender().close(); + try + { + socket.close(); + } + catch(Exception e) + { + log.warn(e, "Error closing socket"); + } } } diff --git a/java/common/src/main/java/org/apache/qpid/transport/network/io/IoSender.java b/java/common/src/main/java/org/apache/qpid/transport/network/io/IoSender.java index ef892744ab..00652e2927 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/network/io/IoSender.java +++ b/java/common/src/main/java/org/apache/qpid/transport/network/io/IoSender.java @@ -18,20 +18,22 @@ */ package org.apache.qpid.transport.network.io; +import static org.apache.qpid.transport.util.Functions.mod; + import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicBoolean; +import org.apache.qpid.thread.Threading; import org.apache.qpid.transport.Sender; +import org.apache.qpid.transport.SenderException; import org.apache.qpid.transport.TransportException; import org.apache.qpid.transport.util.Logger; -import static org.apache.qpid.transport.util.Functions.*; - -public final class IoSender extends Thread implements Sender<ByteBuffer> +public final class IoSender implements Runnable, Sender<ByteBuffer> { private static final Logger log = Logger.get(IoSender.class); @@ -53,7 +55,9 @@ public final class IoSender extends Thread implements Sender<ByteBuffer> private final Object notFull = new Object(); private final Object notEmpty = new Object(); private final AtomicBoolean closed = new AtomicBoolean(false); - + private final Thread senderThread; + private long idleTimeout; + private volatile Throwable exception = null; @@ -73,9 +77,18 @@ public final class IoSender extends Thread implements Sender<ByteBuffer> throw new TransportException("Error getting output stream for socket", e); } - setDaemon(true); - setName(String.format("IoSender - %s", socket.getRemoteSocketAddress())); - start(); + try + { + senderThread = Threading.getThreadFactory().createThread(this); + } + catch(Exception e) + { + throw new Error("Error creating IOSender thread",e); + } + + senderThread.setDaemon(true); + senderThread.setName(String.format("IoSender - %s", socket.getRemoteSocketAddress())); + senderThread.start(); } private static final int pof2(int n) @@ -88,17 +101,11 @@ public final class IoSender extends Thread implements Sender<ByteBuffer> return result; } - private static final int mod(int n, int m) - { - int r = n % m; - return r < 0 ? m + r : r; - } - public void send(ByteBuffer buf) { if (closed.get()) { - throw new TransportException("sender is closed", exception); + throw new SenderException("sender is closed", exception); } final int size = buffer.length; @@ -131,12 +138,12 @@ public final class IoSender extends Thread implements Sender<ByteBuffer> if (closed.get()) { - throw new TransportException("sender is closed", exception); + throw new SenderException("sender is closed", exception); } if (head - tail >= size) { - throw new TransportException(String.format("write timed out: %s, %s", head, tail)); + throw new SenderException(String.format("write timed out: %s, %s", head, tail)); } } continue; @@ -181,6 +188,11 @@ public final class IoSender extends Thread implements Sender<ByteBuffer> { if (!closed.getAndSet(true)) { + synchronized (notFull) + { + notFull.notify(); + } + synchronized (notEmpty) { notEmpty.notify(); @@ -188,37 +200,31 @@ public final class IoSender extends Thread implements Sender<ByteBuffer> try { - if (Thread.currentThread() != this) + if (Thread.currentThread() != senderThread) { - join(timeout); - if (isAlive()) + senderThread.join(timeout); + if (senderThread.isAlive()) { - throw new TransportException("join timed out"); + throw new SenderException("join timed out"); } } - transport.getReceiver().close(); - socket.close(); + transport.getReceiver().close(false); } catch (InterruptedException e) { - throw new TransportException(e); - } - catch (IOException e) - { - throw new TransportException(e); + throw new SenderException(e); } if (reportException && exception != null) { - throw new TransportException(exception); + throw new SenderException(exception); } } } public void run() { - final int size = buffer.length; - + final int size = buffer.length; while (true) { final int hd = head; @@ -288,4 +294,16 @@ public final class IoSender extends Thread implements Sender<ByteBuffer> } } + public void setIdleTimeout(long l) + { + try + { + socket.setSoTimeout((int)l*2); + idleTimeout = l; + } + catch (Exception e) + { + throw new SenderException(e); + } + } } diff --git a/java/common/src/main/java/org/apache/qpid/transport/network/io/IoTransport.java b/java/common/src/main/java/org/apache/qpid/transport/network/io/IoTransport.java index 70fd8a3c06..3615461e9f 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/network/io/IoTransport.java +++ b/java/common/src/main/java/org/apache/qpid/transport/network/io/IoTransport.java @@ -26,17 +26,20 @@ import java.net.Socket; import java.net.SocketException; import java.nio.ByteBuffer; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; + import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; +import org.apache.qpid.ssl.SSLContextFactory; import org.apache.qpid.transport.Binding; import org.apache.qpid.transport.Connection; import org.apache.qpid.transport.ConnectionDelegate; import org.apache.qpid.transport.Receiver; import org.apache.qpid.transport.Sender; import org.apache.qpid.transport.TransportException; -import org.apache.qpid.transport.network.Assembler; import org.apache.qpid.transport.network.ConnectionBinding; -import org.apache.qpid.transport.network.Disassembler; -import org.apache.qpid.transport.network.InputHandler; +import org.apache.qpid.transport.network.ssl.SSLReceiver; +import org.apache.qpid.transport.network.ssl.SSLSender; import org.apache.qpid.transport.util.Logger; /** @@ -68,21 +71,53 @@ public final class IoTransport<E> ("amqj.sendBufferSize", DEFAULT_READ_WRITE_BUFFER_SIZE); private Socket socket; - private IoSender sender; + private Sender<ByteBuffer> sender; private E endpoint; private IoReceiver receiver; private long timeout = 60000; - IoTransport(Socket socket, Binding<E,ByteBuffer> binding) + IoTransport(Socket socket, Binding<E,ByteBuffer> binding, boolean ssl) { this.socket = socket; - this.sender = new IoSender(this, 2*writeBufferSize, timeout); - this.endpoint = binding.endpoint(sender); - this.receiver = new IoReceiver(this, binding.receiver(endpoint), - 2*readBufferSize, timeout); + + if (ssl) + { + SSLEngine engine = null; + SSLContext sslCtx; + try + { + sslCtx = createSSLContext(); + } + catch (Exception e) + { + throw new TransportException("Error creating SSL Context", e); + } + + try + { + engine = sslCtx.createSSLEngine(); + engine.setUseClientMode(true); + } + catch(Exception e) + { + throw new TransportException("Error creating SSL Engine", e); + } + + this.sender = new SSLSender(engine,new IoSender(this, 2*writeBufferSize, timeout)); + this.endpoint = binding.endpoint(sender); + this.receiver = new IoReceiver(this, new SSLReceiver(engine,binding.receiver(endpoint),(SSLSender)sender), + 2*readBufferSize, timeout); + } + else + { + this.sender = new IoSender(this, 2*writeBufferSize, timeout); + this.endpoint = binding.endpoint(sender); + this.receiver = new IoReceiver(this, binding.receiver(endpoint), + 2*readBufferSize, timeout); + } } - IoSender getSender() + Sender<ByteBuffer> getSender() { return sender; } @@ -98,22 +133,24 @@ public final class IoTransport<E> } public static final <E> E connect(String host, int port, - Binding<E,ByteBuffer> binding) + Binding<E,ByteBuffer> binding, + boolean ssl) { Socket socket = createSocket(host, port); - IoTransport<E> transport = new IoTransport<E>(socket, binding); + IoTransport<E> transport = new IoTransport<E>(socket, binding,ssl); return transport.endpoint; } public static final Connection connect(String host, int port, - ConnectionDelegate delegate) + ConnectionDelegate delegate, + boolean ssl) { - return connect(host, port, new ConnectionBinding(delegate)); + return connect(host, port, ConnectionBinding.get(delegate),ssl); } - public static void connect_0_9(AMQVersionAwareProtocolSession session, String host, int port) + public static void connect_0_9(AMQVersionAwareProtocolSession session, String host, int port, boolean ssl) { - connect(host, port, new Binding_0_9(session)); + connect(host, port, new Binding_0_9(session),ssl); } private static class Binding_0_9 @@ -170,5 +207,23 @@ public final class IoTransport<E> throw new TransportException("Error connecting to broker", e); } } + + private SSLContext createSSLContext() throws Exception + { + String trustStorePath = System.getProperty("javax.net.ssl.trustStore"); + String trustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword"); + String trustStoreCertType = System.getProperty("qpid.ssl.trustStoreCertType","SunX509"); + + String keyStorePath = System.getProperty("javax.net.ssl.keyStore",trustStorePath); + String keyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword",trustStorePassword); + String keyStoreCertType = System.getProperty("qpid.ssl.keyStoreCertType","SunX509"); + + SSLContextFactory sslContextFactory = new SSLContextFactory(trustStorePath,trustStorePassword, + trustStoreCertType,keyStorePath, + keyStorePassword,keyStoreCertType); + + return sslContextFactory.buildServerContext(); + + } } diff --git a/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java b/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java new file mode 100644 index 0000000000..3838bf76be --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java @@ -0,0 +1,421 @@ +/* + * + * 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.transport.network.mina; + +import java.io.IOException; +import java.net.BindException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; + +import org.apache.mina.common.ConnectFuture; +import org.apache.mina.common.IdleStatus; +import org.apache.mina.common.IoAcceptor; +import org.apache.mina.common.IoConnector; +import org.apache.mina.common.IoFilterChain; +import org.apache.mina.common.IoHandlerAdapter; +import org.apache.mina.common.IoSession; +import org.apache.mina.common.SimpleByteBufferAllocator; +import org.apache.mina.common.WriteFuture; +import org.apache.mina.filter.ReadThrottleFilterBuilder; +import org.apache.mina.filter.SSLFilter; +import org.apache.mina.filter.WriteBufferLimitFilterBuilder; +import org.apache.mina.filter.executor.ExecutorFilter; +import org.apache.mina.transport.socket.nio.MultiThreadSocketConnector; +import org.apache.mina.transport.socket.nio.SocketAcceptorConfig; +import org.apache.mina.transport.socket.nio.SocketConnector; +import org.apache.mina.transport.socket.nio.SocketConnectorConfig; +import org.apache.mina.transport.socket.nio.SocketSessionConfig; +import org.apache.mina.util.NewThreadExecutor; +import org.apache.mina.util.SessionUtil; +import org.apache.qpid.protocol.ProtocolEngine; +import org.apache.qpid.protocol.ProtocolEngineFactory; +import org.apache.qpid.ssl.SSLContextFactory; +import org.apache.qpid.thread.QpidThreadExecutor; +import org.apache.qpid.transport.NetworkDriver; +import org.apache.qpid.transport.NetworkDriverConfiguration; +import org.apache.qpid.transport.OpenException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver +{ + + private static final int DEFAULT_BUFFER_SIZE = 32 * 1024; + + ProtocolEngine _protocolEngine; + private boolean _useNIO = false; + private int _processors = 4; + private boolean _executorPool = false; + private SSLContextFactory _sslFactory = null; + private IoConnector _socketConnector; + private IoAcceptor _acceptor; + private IoSession _ioSession; + private ProtocolEngineFactory _factory; + private boolean _protectIO; + private NetworkDriverConfiguration _config; + private Throwable _lastException; + private boolean _acceptingConnections = false; + + private WriteFuture _lastWriteFuture; + + private static final Logger _logger = LoggerFactory.getLogger(MINANetworkDriver.class); + + public MINANetworkDriver(boolean useNIO, int processors, boolean executorPool, boolean protectIO) + { + _useNIO = useNIO; + _processors = processors; + _executorPool = executorPool; + _protectIO = protectIO; + } + + public MINANetworkDriver(boolean useNIO, int processors, boolean executorPool, boolean protectIO, + ProtocolEngine protocolEngine, IoSession session) + { + _useNIO = useNIO; + _processors = processors; + _executorPool = executorPool; + _protectIO = protectIO; + _protocolEngine = protocolEngine; + _ioSession = session; + _ioSession.setAttachment(_protocolEngine); + } + + public MINANetworkDriver() + { + + } + + public MINANetworkDriver(IoConnector ioConnector) + { + _socketConnector = ioConnector; + } + + public MINANetworkDriver(IoConnector ioConnector, ProtocolEngine engine) + { + _socketConnector = ioConnector; + _protocolEngine = engine; + } + + public void bind(int port, InetAddress[] addresses, ProtocolEngineFactory factory, + NetworkDriverConfiguration config, SSLContextFactory sslFactory) throws BindException + { + + _factory = factory; + _config = config; + + if (_useNIO) + { + _acceptor = new org.apache.mina.transport.socket.nio.MultiThreadSocketAcceptor(_processors, + new NewThreadExecutor()); + } + else + { + _acceptor = new org.apache.mina.transport.socket.nio.SocketAcceptor(_processors, new NewThreadExecutor()); + } + + SocketAcceptorConfig sconfig = (SocketAcceptorConfig) _acceptor.getDefaultConfig(); + SocketSessionConfig sc = (SocketSessionConfig) sconfig.getSessionConfig(); + + if (config != null) + { + sc.setReceiveBufferSize(config.getReceiveBufferSize()); + sc.setSendBufferSize(config.getSendBufferSize()); + sc.setTcpNoDelay(config.getTcpNoDelay()); + } + + if (sslFactory != null) + { + _sslFactory = sslFactory; + } + + if (addresses != null && addresses.length > 0) + { + for (InetAddress addr : addresses) + { + try + { + _acceptor.bind(new InetSocketAddress(addr, port), this, sconfig); + } + catch (IOException e) + { + throw new BindException(String.format("Could not bind to %1s:%2s", addr, port)); + } + } + } + else + { + try + { + _acceptor.bind(new InetSocketAddress(port), this, sconfig); + } + catch (IOException e) + { + throw new BindException(String.format("Could not bind to *:%1s", port)); + } + } + _acceptingConnections = true; + } + + public SocketAddress getRemoteAddress() + { + return _ioSession.getRemoteAddress(); + } + + public SocketAddress getLocalAddress() + { + return _ioSession.getLocalAddress(); + } + + + public void open(int port, InetAddress destination, ProtocolEngine engine, NetworkDriverConfiguration config, + SSLContextFactory sslFactory) throws OpenException + { + if (sslFactory != null) + { + _sslFactory = sslFactory; + } + + if (_useNIO) + { + _socketConnector = new MultiThreadSocketConnector(1, new QpidThreadExecutor()); + } + else + { + _socketConnector = new SocketConnector(1, new QpidThreadExecutor()); // non-blocking + // connector + } + + org.apache.mina.common.ByteBuffer.setUseDirectBuffers(Boolean.getBoolean("amqj.enableDirectBuffers")); + // the MINA default is currently to use the pooled allocator although this may change in future + // once more testing of the performance of the simple allocator has been done + if (!Boolean.getBoolean("amqj.enablePooledAllocator")) + { + org.apache.mina.common.ByteBuffer.setAllocator(new SimpleByteBufferAllocator()); + } + + SocketConnectorConfig cfg = (SocketConnectorConfig) _socketConnector.getDefaultConfig(); + + SocketSessionConfig scfg = (SocketSessionConfig) cfg.getSessionConfig(); + scfg.setTcpNoDelay((config != null) ? config.getTcpNoDelay() : true); + scfg.setSendBufferSize((config != null) ? config.getSendBufferSize() : DEFAULT_BUFFER_SIZE); + scfg.setReceiveBufferSize((config != null) ? config.getReceiveBufferSize() : DEFAULT_BUFFER_SIZE); + + // Don't have the connector's worker thread wait around for other + // connections (we only use + // one SocketConnector per connection at the moment anyway). This allows + // short-running + // clients (like unit tests) to complete quickly. + if (_socketConnector instanceof SocketConnector) + { + ((SocketConnector) _socketConnector).setWorkerTimeout(0); + } + + ConnectFuture future = _socketConnector.connect(new InetSocketAddress(destination, port), this, cfg); + future.join(); + if (!future.isConnected()) + { + throw new OpenException("Could not open connection", _lastException); + } + _ioSession = future.getSession(); + _ioSession.setAttachment(engine); + engine.setNetworkDriver(this); + _protocolEngine = engine; + } + + public void setMaxReadIdle(int idleTime) + { + _ioSession.setIdleTime(IdleStatus.READER_IDLE, idleTime); + } + + public void setMaxWriteIdle(int idleTime) + { + _ioSession.setIdleTime(IdleStatus.WRITER_IDLE, idleTime); + } + + public void close() + { + if (_lastWriteFuture != null) + { + _lastWriteFuture.join(); + } + if (_acceptor != null) + { + _acceptor.unbindAll(); + } + if (_ioSession != null) + { + _ioSession.close(); + } + } + + public void flush() + { + if (_lastWriteFuture != null) + { + _lastWriteFuture.join(); + } + } + + public void send(ByteBuffer msg) + { + org.apache.mina.common.ByteBuffer minaBuf = org.apache.mina.common.ByteBuffer.allocate(msg.capacity()); + minaBuf.put(msg); + minaBuf.flip(); + _lastWriteFuture = _ioSession.write(minaBuf); + } + + public void setIdleTimeout(long l) + { + // MINA doesn't support setting SO_TIMEOUT + } + + public void exceptionCaught(IoSession protocolSession, Throwable throwable) throws Exception + { + if (_protocolEngine != null) + { + _protocolEngine.exception(throwable); + } + else + { + _logger.error("Exception thrown and no ProtocolEngine to handle it", throwable); + } + _lastException = throwable; + } + + /** + * Invoked when a message is received on a particular protocol session. Note + * that a protocol session is directly tied to a particular physical + * connection. + * + * @param protocolSession + * the protocol session that received the message + * @param message + * the message itself (i.e. a decoded frame) + * + * @throws Exception + * if the message cannot be processed + */ + public void messageReceived(IoSession protocolSession, Object message) throws Exception + { + if (message instanceof org.apache.mina.common.ByteBuffer) + { + ((ProtocolEngine) protocolSession.getAttachment()).received(((org.apache.mina.common.ByteBuffer) message).buf()); + } + else + { + throw new IllegalStateException("Handed unhandled message. message.class = " + message.getClass() + " message = " + message); + } + } + + public void sessionClosed(IoSession protocolSession) throws Exception + { + ((ProtocolEngine) protocolSession.getAttachment()).closed(); + } + + public void sessionCreated(IoSession protocolSession) throws Exception + { + // Configure the session with SSL if necessary + SessionUtil.initialize(protocolSession); + if (_executorPool) + { + if (_sslFactory != null) + { + protocolSession.getFilterChain().addAfter("AsynchronousReadFilter", "sslFilter", + new SSLFilter(_sslFactory.buildServerContext())); + } + } + else + { + if (_sslFactory != null) + { + protocolSession.getFilterChain().addBefore("protocolFilter", "sslFilter", + new SSLFilter(_sslFactory.buildServerContext())); + } + } + // Do we want to have read/write buffer limits? + if (_protectIO) + { + //Add IO Protection Filters + IoFilterChain chain = protocolSession.getFilterChain(); + + protocolSession.getFilterChain().addLast("tempExecutorFilterForFilterBuilder", new ExecutorFilter()); + + ReadThrottleFilterBuilder readfilter = new ReadThrottleFilterBuilder(); + readfilter.setMaximumConnectionBufferSize(_config.getReceiveBufferSize()); + readfilter.attach(chain); + + WriteBufferLimitFilterBuilder writefilter = new WriteBufferLimitFilterBuilder(); + writefilter.setMaximumConnectionBufferSize(_config.getSendBufferSize()); + writefilter.attach(chain); + + protocolSession.getFilterChain().remove("tempExecutorFilterForFilterBuilder"); + } + + if (_ioSession == null) + { + _ioSession = protocolSession; + } + + if (_acceptingConnections) + { + // Set up the protocol engine + ProtocolEngine protocolEngine = _factory.newProtocolEngine(this); + MINANetworkDriver newDriver = new MINANetworkDriver(_useNIO, _processors, _executorPool, _protectIO, protocolEngine, protocolSession); + protocolEngine.setNetworkDriver(newDriver); + } + } + + public void sessionIdle(IoSession session, IdleStatus status) throws Exception + { + if (IdleStatus.WRITER_IDLE.equals(status)) + { + ((ProtocolEngine) session.getAttachment()).writerIdle(); + } + else if (IdleStatus.READER_IDLE.equals(status)) + { + ((ProtocolEngine) session.getAttachment()).readerIdle(); + } + } + + private ProtocolEngine getProtocolEngine() + { + return _protocolEngine; + } + + public void setProtocolEngineFactory(ProtocolEngineFactory engineFactory, boolean acceptingConnections) + { + _factory = engineFactory; + _acceptingConnections = acceptingConnections; + } + + public void setProtocolEngine(ProtocolEngine protocolEngine) + { + _protocolEngine = protocolEngine; + if (_ioSession != null) + { + _ioSession.setAttachment(protocolEngine); + } + } + +} diff --git a/java/common/src/main/java/org/apache/qpid/transport/network/mina/MinaHandler.java b/java/common/src/main/java/org/apache/qpid/transport/network/mina/MinaHandler.java index f8dbec3c3d..b89eed48b0 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/network/mina/MinaHandler.java +++ b/java/common/src/main/java/org/apache/qpid/transport/network/mina/MinaHandler.java @@ -262,13 +262,13 @@ public class MinaHandler<E> implements IoHandler ConnectionDelegate delegate) throws IOException { - accept(host, port, new ConnectionBinding(delegate)); + accept(host, port, ConnectionBinding.get(delegate)); } public static final Connection connect(String host, int port, ConnectionDelegate delegate) { - return connect(host, port, new ConnectionBinding(delegate)); + return connect(host, port, ConnectionBinding.get(delegate)); } } diff --git a/java/common/src/main/java/org/apache/qpid/transport/network/mina/MinaSender.java b/java/common/src/main/java/org/apache/qpid/transport/network/mina/MinaSender.java index 69d4061e0c..fbedf14312 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/network/mina/MinaSender.java +++ b/java/common/src/main/java/org/apache/qpid/transport/network/mina/MinaSender.java @@ -24,7 +24,6 @@ import org.apache.mina.common.ByteBuffer; import org.apache.mina.common.CloseFuture; import org.apache.mina.common.IoSession; import org.apache.mina.common.WriteFuture; - import org.apache.qpid.transport.Sender; import org.apache.qpid.transport.TransportException; @@ -77,5 +76,15 @@ public class MinaSender implements Sender<java.nio.ByteBuffer> CloseFuture closed = session.close(); closed.join(); } - + + public void setIdleTimeout(long l) + { + //noop + } + + public long getIdleTimeout() + { + return 0; + } + } diff --git a/java/common/src/main/java/org/apache/qpid/transport/network/nio/NioHandler.java b/java/common/src/main/java/org/apache/qpid/transport/network/nio/NioHandler.java index 51e41b26f7..3bc6730623 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/network/nio/NioHandler.java +++ b/java/common/src/main/java/org/apache/qpid/transport/network/nio/NioHandler.java @@ -1,4 +1,25 @@ package org.apache.qpid.transport.network.nio; +/* + * + * 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. + * + */ + import java.io.EOFException; import java.io.IOException; @@ -66,8 +87,9 @@ public class NioHandler implements Runnable } NioSender sender = new NioSender(_ch); - Connection con = new Connection - (new Disassembler(sender, 64*1024 - 1), delegate); + Connection con = new Connection(); + con.setSender(new Disassembler(sender, 64*1024 - 1)); + con.setConnectionDelegate(delegate); con.setConnectionId(_count.incrementAndGet()); _handlers.put(con.getConnectionId(),sender); diff --git a/java/common/src/main/java/org/apache/qpid/transport/network/nio/NioSender.java b/java/common/src/main/java/org/apache/qpid/transport/network/nio/NioSender.java index 33e888cc56..5196505c2d 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/network/nio/NioSender.java +++ b/java/common/src/main/java/org/apache/qpid/transport/network/nio/NioSender.java @@ -1,4 +1,25 @@ package org.apache.qpid.transport.network.nio; +/* + * + * 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. + * + */ + import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; @@ -97,4 +118,9 @@ public class NioSender implements Sender<java.nio.ByteBuffer> } } } + + public void setIdleTimeout(long l) + { + //noop + } } diff --git a/java/common/src/main/java/org/apache/qpid/transport/network/ssl/SSLReceiver.java b/java/common/src/main/java/org/apache/qpid/transport/network/ssl/SSLReceiver.java new file mode 100644 index 0000000000..e6e6c5f791 --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/transport/network/ssl/SSLReceiver.java @@ -0,0 +1,184 @@ +/* +* + * 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.transport.network.ssl; + +import java.nio.ByteBuffer; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLEngineResult.Status; + +import org.apache.qpid.transport.Receiver; +import org.apache.qpid.transport.TransportException; +import org.apache.qpid.transport.util.Logger; + +public class SSLReceiver implements Receiver<ByteBuffer> +{ + private Receiver<ByteBuffer> delegate; + private SSLEngine engine; + private SSLSender sender; + private int sslBufSize; + private ByteBuffer appData; + private ByteBuffer localBuffer; + private boolean dataCached = false; + private final Object notificationToken; + + private static final Logger log = Logger.get(SSLReceiver.class); + + public SSLReceiver(SSLEngine engine, Receiver<ByteBuffer> delegate,SSLSender sender) + { + this.engine = engine; + this.delegate = delegate; + this.sender = sender; + this.sslBufSize = engine.getSession().getApplicationBufferSize(); + appData = ByteBuffer.allocate(sslBufSize); + localBuffer = ByteBuffer.allocate(sslBufSize); + notificationToken = sender.getNotificationToken(); + } + + public void closed() + { + delegate.closed(); + } + + public void exception(Throwable t) + { + delegate.exception(t); + } + + private ByteBuffer addPreviouslyUnreadData(ByteBuffer buf) + { + if (dataCached) + { + ByteBuffer b = ByteBuffer.allocate(localBuffer.remaining() + buf.remaining()); + b.put(localBuffer); + b.put(buf); + b.flip(); + dataCached = false; + return b; + } + else + { + return buf; + } + } + + public void received(ByteBuffer buf) + { + ByteBuffer netData = addPreviouslyUnreadData(buf); + + HandshakeStatus handshakeStatus; + Status status; + + while (netData.hasRemaining()) + { + try + { + SSLEngineResult result = engine.unwrap(netData, appData); + synchronized (notificationToken) + { + notificationToken.notifyAll(); + } + + int read = result.bytesProduced(); + status = result.getStatus(); + handshakeStatus = result.getHandshakeStatus(); + + if (read > 0) + { + int limit = appData.limit(); + appData.limit(appData.position()); + appData.position(appData.position() - read); + + ByteBuffer data = appData.slice(); + + appData.limit(limit); + appData.position(appData.position() + read); + + delegate.received(data); + } + + + switch(status) + { + case CLOSED: + synchronized(notificationToken) + { + notificationToken.notifyAll(); + } + return; + + case BUFFER_OVERFLOW: + appData = ByteBuffer.allocate(sslBufSize); + continue; + + case BUFFER_UNDERFLOW: + localBuffer.clear(); + localBuffer.put(netData); + localBuffer.flip(); + dataCached = true; + break; + + case OK: + break; // do nothing + + default: + throw new IllegalStateException("SSLReceiver: Invalid State " + status); + } + + switch (handshakeStatus) + { + case NEED_UNWRAP: + if (netData.hasRemaining()) + { + continue; + } + break; + + case NEED_TASK: + sender.doTasks(); + handshakeStatus = engine.getHandshakeStatus(); + + case NEED_WRAP: + case FINISHED: + case NOT_HANDSHAKING: + synchronized(notificationToken) + { + notificationToken.notifyAll(); + } + break; + + default: + throw new IllegalStateException("SSLReceiver: Invalid State " + status); + } + + + } + catch(SSLException e) + { + throw new TransportException("Error in SSLReceiver",e); + } + + } + } +} diff --git a/java/common/src/main/java/org/apache/qpid/transport/network/ssl/SSLSender.java b/java/common/src/main/java/org/apache/qpid/transport/network/ssl/SSLSender.java new file mode 100644 index 0000000000..0e785bb2ee --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/transport/network/ssl/SSLSender.java @@ -0,0 +1,255 @@ +/* + * 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.transport.network.ssl; + +import java.nio.ByteBuffer; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLEngineResult.Status; + +import org.apache.qpid.transport.Sender; +import org.apache.qpid.transport.SenderException; +import org.apache.qpid.transport.util.Logger; + +public class SSLSender implements Sender<ByteBuffer> +{ + private Sender<ByteBuffer> delegate; + private SSLEngine engine; + private int sslBufSize; + private ByteBuffer netData; + private long timeout = 30000; + + private final Object engineState = new Object(); + private final AtomicBoolean closed = new AtomicBoolean(false); + + private static final Logger log = Logger.get(SSLSender.class); + + public SSLSender(SSLEngine engine, Sender<ByteBuffer> delegate) + { + this.engine = engine; + this.delegate = delegate; + sslBufSize = engine.getSession().getPacketBufferSize(); + netData = ByteBuffer.allocate(sslBufSize); + timeout = Long.getLong("qpid.ssl_timeout", 60000); + } + + public void close() + { + if (!closed.getAndSet(true)) + { + if (engine.isOutboundDone()) + { + return; + } + log.debug("Closing SSL connection"); + engine.closeOutbound(); + try + { + tearDownSSLConnection(); + } + catch(Exception e) + { + throw new SenderException("Error closing SSL connection",e); + } + + while (!engine.isOutboundDone()) + { + synchronized(engineState) + { + try + { + engineState.wait(); + } + catch(InterruptedException e) + { + // pass + } + } + } + delegate.close(); + } + } + + private void tearDownSSLConnection() throws Exception + { + SSLEngineResult result = engine.wrap(ByteBuffer.allocate(0), netData); + Status status = result.getStatus(); + int read = result.bytesProduced(); + while (status != Status.CLOSED) + { + if (status == Status.BUFFER_OVERFLOW) + { + netData.clear(); + } + if(read > 0) + { + int limit = netData.limit(); + netData.limit(netData.position()); + netData.position(netData.position() - read); + + ByteBuffer data = netData.slice(); + + netData.limit(limit); + netData.position(netData.position() + read); + + delegate.send(data); + flush(); + } + result = engine.wrap(ByteBuffer.allocate(0), netData); + status = result.getStatus(); + read = result.bytesProduced(); + } + } + + public void flush() + { + delegate.flush(); + } + + public void send(ByteBuffer appData) + { + if (closed.get()) + { + throw new SenderException("SSL Sender is closed"); + } + + HandshakeStatus handshakeStatus; + Status status; + + while(appData.hasRemaining()) + { + + int read = 0; + try + { + SSLEngineResult result = engine.wrap(appData, netData); + read = result.bytesProduced(); + status = result.getStatus(); + handshakeStatus = result.getHandshakeStatus(); + + } + catch(SSLException e) + { + throw new SenderException("SSL, Error occurred while encrypting data",e); + } + + if(read > 0) + { + int limit = netData.limit(); + netData.limit(netData.position()); + netData.position(netData.position() - read); + + ByteBuffer data = netData.slice(); + + netData.limit(limit); + netData.position(netData.position() + read); + + delegate.send(data); + } + + switch(status) + { + case CLOSED: + throw new SenderException("SSLEngine is closed"); + + case BUFFER_OVERFLOW: + netData.clear(); + continue; + + case OK: + break; // do nothing + + default: + throw new IllegalStateException("SSLReceiver: Invalid State " + status); + } + + switch (handshakeStatus) + { + case NEED_WRAP: + if (netData.hasRemaining()) + { + continue; + } + + case NEED_TASK: + doTasks(); + break; + + case NEED_UNWRAP: + flush(); + synchronized(engineState) + { + switch (engine.getHandshakeStatus()) + { + case NEED_UNWRAP: + long start = System.currentTimeMillis(); + try + { + engineState.wait(timeout); + } + catch(InterruptedException e) + { + // pass + } + + if (System.currentTimeMillis()- start >= timeout) + { + throw new SenderException( + "SSL Engine timed out waiting for a response." + + "To get more info,run with -Djavax.net.debug=ssl"); + } + break; + } + } + break; + + case FINISHED: + case NOT_HANDSHAKING: + break; //do nothing + + default: + throw new IllegalStateException("SSLReceiver: Invalid State " + status); + } + + } + } + + public void doTasks() + { + Runnable runnable; + while ((runnable = engine.getDelegatedTask()) != null) { + runnable.run(); + } + } + + public Object getNotificationToken() + { + return engineState; + } + + public void setIdleTimeout(long l) + { + delegate.setIdleTimeout(l); + } +} diff --git a/java/common/src/main/java/org/apache/qpid/transport/util/Functions.java b/java/common/src/main/java/org/apache/qpid/transport/util/Functions.java index 2c6984e302..9f1c0ca9eb 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/util/Functions.java +++ b/java/common/src/main/java/org/apache/qpid/transport/util/Functions.java @@ -34,6 +34,12 @@ import static java.lang.Math.*; public class Functions { + public static final int mod(int n, int m) + { + int r = n % m; + return r < 0 ? m + r : r; + } + public static final byte lsb(int i) { return (byte) (0xFF & i); @@ -51,12 +57,17 @@ public class Functions public static final String str(ByteBuffer buf, int limit) { + return str(buf, limit,buf.position()); + } + + public static final String str(ByteBuffer buf, int limit,int start) + { StringBuilder str = new StringBuilder(); str.append('"'); for (int i = 0; i < min(buf.remaining(), limit); i++) { - byte c = buf.get(buf.position() + i); + byte c = buf.get(start + i); if (c > 31 && c < 127 && c != '\\') { diff --git a/java/common/src/main/java/org/apache/qpid/transport/util/Waiter.java b/java/common/src/main/java/org/apache/qpid/transport/util/Waiter.java new file mode 100644 index 0000000000..e034d779ca --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/transport/util/Waiter.java @@ -0,0 +1,63 @@ +/* + * + * 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.transport.util; + + +/** + * Waiter + * + */ + +public final class Waiter +{ + + private final Object lock; + private final long timeout; + private final long start; + private long elapsed; + + public Waiter(Object lock, long timeout) + { + this.lock = lock; + this.timeout = timeout; + this.start = System.currentTimeMillis(); + this.elapsed = 0; + } + + public boolean hasTime() + { + return elapsed < timeout; + } + + public void await() + { + try + { + lock.wait(timeout - elapsed); + } + catch (InterruptedException e) + { + // pass + } + elapsed = System.currentTimeMillis() - start; + } + +} diff --git a/java/common/src/main/java/org/apache/qpid/url/BindingURLImpl.java b/java/common/src/main/java/org/apache/qpid/url/BindingURLImpl.java deleted file mode 100644 index f12fb2cff2..0000000000 --- a/java/common/src/main/java/org/apache/qpid/url/BindingURLImpl.java +++ /dev/null @@ -1,261 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.qpid.url; - -import org.apache.qpid.exchange.ExchangeDefaults; -import org.slf4j.LoggerFactory; -import org.slf4j.Logger; - -import java.util.HashMap; -import java.net.URI; -import java.net.URISyntaxException; - -public class BindingURLImpl implements QpidBindingURL -{ - private static final Logger _logger = LoggerFactory.getLogger(BindingURLImpl.class); - - String _url; - String _exchangeClass; - String _exchangeName; - String _destinationName; - String _queueName; - private HashMap<String, String> _options; - - public BindingURLImpl(String url) throws URLSyntaxException - { - // format: - // <exch_class>://<exch_name>/[<destination>]/[<queue>]?<option>='<value>'[,<option>='<value>']* - if (_logger.isDebugEnabled()) - { - _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) + _exchangeName.length(), - "Destination or Queue requried", _url); - } - else - { - int slash = connection.getPath().indexOf("/", 1); - if (slash == -1) - { - throw URLHelper.parseError(_url.indexOf(_exchangeName) + _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) - if (_logger.isDebugEnabled()) - { - _logger.debug("URL Parsed: " + this); - } - } - catch (URISyntaxException uris) - { - throw URLHelper.parseError(uris.getIndex(), uris.getReason(), uris.getInput()); - } - } - - - private void processOptions() - { - // this is where we would parse any options that needed more than just storage. - } - - public String getURL() - { - return _url; - } - - public String getExchangeClass() - { - return _exchangeClass; - } - - private void setExchangeClass(String exchangeClass) - { - - _exchangeClass = exchangeClass; - if (exchangeClass.equals(ExchangeDefaults.TOPIC_EXCHANGE_CLASS)) - { - setOption(BindingURL.OPTION_EXCLUSIVE, "true"); - } - - } - - public String getExchangeName() - { - return _exchangeName; - } - - private void setExchangeName(String name) - { - _exchangeName = name; - } - - public String getDestinationName() - { - return _destinationName; - } - - private void setDestinationName(String name) - { - _destinationName = name; - } - - public String getQueueName() - { - return _queueName; - } - - public void setQueueName(String 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 = 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 String getRoutingKey() - { - if (_exchangeClass.equals(ExchangeDefaults.DIRECT_EXCHANGE_CLASS)) - { - return getQueueName(); - } - - if (containsOption(BindingURL.OPTION_ROUTING_KEY)) - { - return getOption(OPTION_ROUTING_KEY); - } - - return getDestinationName(); - } - - public void setRoutingKey(String key) - { - setOption(OPTION_ROUTING_KEY, key); - } - - 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/java/common/src/main/java/org/apache/qpid/url/BindingURLParser.java b/java/common/src/main/java/org/apache/qpid/url/BindingURLParser.java index 5d26e7e65b..7fe7d2e1da 100644 --- a/java/common/src/main/java/org/apache/qpid/url/BindingURLParser.java +++ b/java/common/src/main/java/org/apache/qpid/url/BindingURLParser.java @@ -1,4 +1,25 @@ package org.apache.qpid.url; +/* + * + * 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. + * + */ + import java.net.URISyntaxException; import java.util.ArrayList; @@ -23,7 +44,7 @@ public class BindingURLParser private static final char COLON_CHAR = ':'; private static final char END_OF_URL_MARKER_CHAR = '%'; - private static final Logger _logger = LoggerFactory.getLogger(BindingURLImpl.class); + private static final Logger _logger = LoggerFactory.getLogger(BindingURLParser.class); private char[] _url; private AMQBindingURL _bindingURL; @@ -396,7 +417,7 @@ public class BindingURLParser { if (_bindingURL.containsOption(BindingURL.OPTION_CLIENTID) && _bindingURL.containsOption(BindingURL.OPTION_SUBSCRIPTION)) { - queueName = _bindingURL.getOption(BindingURL.OPTION_CLIENTID + ":" + BindingURL.OPTION_SUBSCRIPTION); + queueName = _bindingURL.getOption(BindingURL.OPTION_CLIENTID) + ":" + _bindingURL.getOption(BindingURL.OPTION_SUBSCRIPTION); } else { diff --git a/java/common/src/main/java/org/apache/qpid/url/QpidBindingURL.java b/java/common/src/main/java/org/apache/qpid/url/QpidBindingURL.java deleted file mode 100644 index 00edbf1bc3..0000000000 --- a/java/common/src/main/java/org/apache/qpid/url/QpidBindingURL.java +++ /dev/null @@ -1,53 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.qpid.url; - -import org.apache.qpid.framing.AMQShortString; - -/* - Binding URL format: - <exch_class>://<exch_name>/[<destination>]/[<queue>]?<option>='<value>'[,<option>='<value>']* -*/ -public interface QpidBindingURL -{ - 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(); - - String getExchangeClass(); - - String getExchangeName(); - - String getDestinationName(); - - String getQueueName(); - - String getOption(String key); - - boolean containsOption(String key); - - String getRoutingKey(); - - String toString(); -} diff --git a/java/common/src/main/java/org/apache/qpid/url/QpidURL.java b/java/common/src/main/java/org/apache/qpid/url/QpidURL.java deleted file mode 100644 index 5ab4425323..0000000000 --- a/java/common/src/main/java/org/apache/qpid/url/QpidURL.java +++ /dev/null @@ -1,57 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.qpid.url; - -import org.apache.qpid.BrokerDetails; - -import java.util.List; - -/** - * The format of the Qpid URL is based on the AMQP one. - * The grammar is as follows: - * <p> qpid_url = "qpid:" [user_props] prot_addr_list ["/" future-parameters] - * <p> prot_addr_list = [prot_addr ","]* prot_addr - * <p> prot_addr = tcp_prot_addr | tls_prot_addr | future_prot_addr - * <p> tcp_prot_addr = tcp_id tcp_addr - * <p> tcp_id = "tcp:" | "" - * <p> tcp_addr = [host [":" port] ] - * <p> host = <as per [2]> - * <p> port = number - * <p> tls_prot_addr = tls_id tls_addr - * <p> tls_id = "tls:" | "" - * <p> tls_addr = [host [":" port] ] - * <p> future_prot_addr = future_prot_id future_prot_addr - * <p> future_prot_id = <placeholder, must end in ":". Example "sctp:"> - * <p> future_prot_addr = <placeholder, protocl-specific address> - * <p> future_parameters = <placeholder, not used in failover addresses> - */ -public interface QpidURL -{ - /** - * Get all the broker details - * - * @return A list of BrokerDetails. - */ - public List<BrokerDetails> getAllBrokerDetails(); - - /** - * Get this URL string form - * @return This URL string form. - */ - public String getURL(); -} diff --git a/java/common/src/main/java/org/apache/qpid/url/QpidURLImpl.java b/java/common/src/main/java/org/apache/qpid/url/QpidURLImpl.java deleted file mode 100644 index f92934db7f..0000000000 --- a/java/common/src/main/java/org/apache/qpid/url/QpidURLImpl.java +++ /dev/null @@ -1,460 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.qpid.url; - -import org.apache.qpid.BrokerDetails; -import org.apache.qpid.BrokerDetailsImpl; - -import java.net.MalformedURLException; -import java.util.ArrayList; -import java.util.List; - -/** - * The format Qpid URL is based on the AMQP one. - * The grammar is as follows: - * <p> qpid_url = "qpid:" [client_props "@"] port_addr_list ["/" future-parameters] - * <p> port_addr_list = [port_addr ","]* port_addr - * <p> port_addr = tcp_port_addr | tls_prot_addr | future_prot_addr - * <p> tcp_port_addr = tcp_id tcp_addr - * <p> tcp_id = "tcp:" | "" - * <p> tcp_addr = host [":" port] - * <p> host = <as per http://www.apps.ietf.org/> - * <p> port = number - * <p> tls_prot_addr = tls_id tls_addr - * <p> tls_id = "tls:" | "" - * <p> tls_addr = host [":" port] - * <p> future_prot_addr = future_prot_id future_prot_addr - * <p> future_prot_id = <placeholder, must end in ":". Example "sctp:"> - * <p> future_prot_addr = <placeholder, protocl-specific address> - * <p> future_parameters = <placeholder, not used in failover addresses> - * <p> client_props = [client_prop ";"]* client_prop - * <p> client_prop = prop "=" val - * <p> prop = chars as per <as per http://www.apps.ietf.org/> - * <p> val = valid as per <as per http://www.apps.ietf.org/> - * <p/> - * Ex: qpid:virtualhost=tcp:host-foo,test,client_id=foo@tcp:myhost.com:5672,virtualhost=prod; - * keystore=/opt/keystore@client_id2@tls:mysecurehost.com:5672 - */ -public class QpidURLImpl implements QpidURL -{ - private static final char[] URL_START_SEQ = new char[]{'q', 'p', 'i', 'd', ':'}; - private static final char PROPERTY_EQUALS_CHAR = '='; - private static final char PROPERTY_SEPARATOR_CHAR = ';'; - private static final char ADDRESS_SEPERATOR_CHAR = ','; - - //private static final char CLIENT_ID_TRANSPORT_SEPARATOR_CHAR = ':'; - private static final char TRANSPORT_HOST_SEPARATOR_CHAR = ':'; - private static final char HOST_PORT_SEPARATOR_CHAR = ':'; - private static final char AT_CHAR = '@'; - - enum URLParserState - { - QPID_URL_START, - ADDRESS_START, - PROPERTY_NAME, - PROPERTY_EQUALS, - PROPERTY_VALUE, - PROPERTY_SEPARATOR, - AT_CHAR, - // CLIENT_ID, - // CLIENT_ID_TRANSPORT_SEPARATOR, - TRANSPORT, - TRANSPORT_HOST_SEPARATOR, - HOST, - HOST_PORT_SEPARATOR, - PORT, - ADDRESS_END, - ADDRESS_SEPERATOR, - QPID_URL_END, - ERROR - } - - //-- Constructors - - private char[] _url; - private List<BrokerDetails> _brokerDetailList = new ArrayList<BrokerDetails>(); - private String _error; - private int _index = 0; - private BrokerDetails _currentBroker; - private String _currentPropName; - private boolean _endOfURL = false; - private URLParserState _currentParserState; - - public QpidURLImpl(String url) throws MalformedURLException - { - _url = url.toCharArray(); - _endOfURL = false; - _currentParserState = URLParserState.QPID_URL_START; - URLParserState prevState = _currentParserState; // for error handling - try - { - while (_currentParserState != URLParserState.ERROR && _currentParserState != URLParserState.QPID_URL_END) - { - prevState = _currentParserState; - _currentParserState = next(); - } - - if (_currentParserState == URLParserState.ERROR) - { - _error = - "Invalid URL format [current_state = " + prevState + ", broker details parsed so far " + _currentBroker + " ] error at (" + _index + ") due to " + _error; - MalformedURLException ex; - ex = new MalformedURLException(_error); - throw ex; - } - } - catch (ArrayIndexOutOfBoundsException e) - { - e.printStackTrace(); - _error = - "Invalid URL format [current_state = " + prevState + ", broker details parsed so far " + _currentBroker + " ] error at (" + _index + ")"; - MalformedURLException ex; - ex = new MalformedURLException(_error); - throw ex; - } - } - - //-- interface QpidURL - public List<BrokerDetails> getAllBrokerDetails() - { - return _brokerDetailList; - } - - public String getURL() - { - return new String(_url); - } - - private URLParserState next() - { - switch (_currentParserState) - { - case QPID_URL_START: - return checkSequence(URL_START_SEQ, URLParserState.ADDRESS_START); - case ADDRESS_START: - return startAddress(); - case PROPERTY_NAME: - return extractPropertyName(); - case PROPERTY_EQUALS: - _index++; // skip the equal sign - return URLParserState.PROPERTY_VALUE; - case PROPERTY_VALUE: - return extractPropertyValue(); - case PROPERTY_SEPARATOR: - _index++; // skip "," - return URLParserState.PROPERTY_NAME; - case AT_CHAR: - _index++; // skip the @ sign - return setProperties(); - // case CLIENT_ID: - // return extractClientId(); - // case CLIENT_ID_TRANSPORT_SEPARATOR: - // _index++; // skip ":" - // return URLParserState.TRANSPORT; - case TRANSPORT: - return extractTransport(); - case TRANSPORT_HOST_SEPARATOR: - _index++; // skip ":" - return URLParserState.HOST; - case HOST: - return extractHost(); - case HOST_PORT_SEPARATOR: - _index++; // skip ":" - return URLParserState.PORT; - case PORT: - return extractPort(); - case ADDRESS_END: - return endAddress(); - case ADDRESS_SEPERATOR: - _index++; // skip "," - return URLParserState.ADDRESS_START; - default: - return URLParserState.ERROR; - } - } - - private URLParserState checkSequence(char[] expected, URLParserState nextPart) - { - for (char anExpected : expected) - { - if (anExpected != _url[_index]) - { - _error = "Excepted (" + anExpected + ") at position " + _index + ", got (" + _url[_index] + ")"; - return URLParserState.ERROR; - } - _index++; - } - return nextPart; - } - - private URLParserState startAddress() - { - _currentBroker = new BrokerDetailsImpl(); - // check that there is a "@" before the nexte "," - for (int j = _index; j < _url.length; j++) - { - if (_url[j] == AT_CHAR) - { - return URLParserState.PROPERTY_NAME; - } - else if (_url[j] == ADDRESS_SEPERATOR_CHAR) - { - return URLParserState.TRANSPORT; - } - } - return URLParserState.TRANSPORT; - } - - private URLParserState endAddress() - { - _brokerDetailList.add(_currentBroker); - if (_endOfURL) - { - return URLParserState.QPID_URL_END; - } - else - { - return URLParserState.ADDRESS_SEPERATOR; - } - } - - private URLParserState extractPropertyName() - { - StringBuilder b = new StringBuilder(); - char next = _url[_index]; - while (next != PROPERTY_EQUALS_CHAR && next != AT_CHAR) - { - b.append(next); - next = _url[++_index]; - } - _currentPropName = b.toString(); - if (_currentPropName.trim().equals("")) - { - _error = "Property name cannot be empty"; - return URLParserState.ERROR; - } - else if (next == PROPERTY_EQUALS_CHAR) - { - return URLParserState.PROPERTY_EQUALS; - } - else - { - return URLParserState.AT_CHAR; - } - } - - private URLParserState extractPropertyValue() - { - StringBuilder b = new StringBuilder(); - char next = _url[_index]; - while (next != PROPERTY_SEPARATOR_CHAR && next != AT_CHAR) - { - b.append(next); - next = _url[++_index]; - } - String propValue = b.toString(); - if (propValue.trim().equals("")) - { - _error = "Property values cannot be empty"; - return URLParserState.ERROR; - } - else - { - _currentBroker.setProperty(_currentPropName, propValue); - if (next == PROPERTY_SEPARATOR_CHAR) - { - return URLParserState.PROPERTY_SEPARATOR; - } - else - { - return URLParserState.AT_CHAR; - } - } - } - - private URLParserState setProperties() - { - //Check if atleast virtualhost is there - if (_currentBroker.getProperties().get(BrokerDetails.VIRTUAL_HOST) != null) - { - _currentBroker.setVirtualHost(_currentBroker.getProperties().get(BrokerDetails.VIRTUAL_HOST)); - _currentBroker.getProperties().remove(BrokerDetails.VIRTUAL_HOST); - } - - if (_currentBroker.getProperties().get(BrokerDetails.USERNAME) != null) - { - String username = _currentBroker.getProperties().get(BrokerDetails.USERNAME); - _currentBroker.setUserName(username); - } - if (_currentBroker.getProperties().get(BrokerDetails.PASSWORD) != null) - { - String password = _currentBroker.getProperties().get(BrokerDetails.PASSWORD); - _currentBroker.setPassword(password); - } - if (_currentBroker.getProperties().get(BrokerDetails.CLIENT_ID) != null) - { - String clientID = _currentBroker.getProperties().get(BrokerDetails.CLIENT_ID); - _currentBroker.setProperty(BrokerDetails.CLIENT_ID, clientID); - } - return URLParserState.TRANSPORT; - } - - private URLParserState extractTransport() - { - String transport = buildUntil(TRANSPORT_HOST_SEPARATOR_CHAR); - if (transport.trim().equals("")) - { - _error = "Transport cannot be empty"; - return URLParserState.ERROR; - } - else if (!(transport.trim().equals(BrokerDetails.PROTOCOL_TCP) || transport.trim() - .equals(BrokerDetails.PROTOCOL_TLS))) - { - _error = "Transport cannot be " + transport + " value must be tcp or tls"; - return URLParserState.ERROR; - } - else - { - _currentBroker.setProtocol(transport); - return URLParserState.TRANSPORT_HOST_SEPARATOR; - } - } - - private URLParserState extractHost() - { - char nextSep = 'c'; - String host; - for (int i = _index; i < _url.length; i++) - { - if (_url[i] == HOST_PORT_SEPARATOR_CHAR) - { - nextSep = HOST_PORT_SEPARATOR_CHAR; - break; - } - else if (_url[i] == ADDRESS_SEPERATOR_CHAR) - { - nextSep = ADDRESS_SEPERATOR_CHAR; - break; - } - } - if (nextSep == HOST_PORT_SEPARATOR_CHAR) - { - host = buildUntil(HOST_PORT_SEPARATOR_CHAR); - if (host.trim().equals("")) - { - _error = "Host cannot be empty"; - return URLParserState.ERROR; - } - else - { - _currentBroker.setHost(host); - return URLParserState.HOST_PORT_SEPARATOR; - } - } - else if (nextSep == ADDRESS_SEPERATOR_CHAR) - { - host = buildUntil(ADDRESS_SEPERATOR_CHAR); - if (host.trim().equals("")) - { - _error = "Host cannot be empty"; - return URLParserState.ERROR; - } - else - { - _currentBroker.setHost(host); - return URLParserState.ADDRESS_END; - } - } - else - { - host = String.copyValueOf(_url, _index, _url.length - _index); - _currentBroker.setHost(host); - _endOfURL = true; - return URLParserState.ADDRESS_END; - } - } - - - private URLParserState extractPort() - { - - StringBuilder b = new StringBuilder(); - try - { - char next = _url[_index]; - while (next != ADDRESS_SEPERATOR_CHAR) - { - b.append(next); - next = _url[++_index]; - } - } - catch (ArrayIndexOutOfBoundsException e) - { - _endOfURL = true; - } - String portStr = b.toString(); - if (portStr.trim().equals("")) - { - _error = "Host cannot be empty"; - return URLParserState.ERROR; - } - else - { - try - { - int port = Integer.parseInt(portStr); - _currentBroker.setPort(port); - return URLParserState.ADDRESS_END; - } - catch (NumberFormatException e) - { - _error = "Illegal number for port"; - return URLParserState.ERROR; - } - } - } - - private String buildUntil(char c) - { - StringBuilder b = new StringBuilder(); - char next = _url[_index]; - while (next != c) - { - b.append(next); - next = _url[++_index]; - } - return b.toString(); - } - - public static void main(String[] args) - { - String testurl = "qpid:password=pass;username=name@tcp:test1,tcp:fooBroker,keystore=/usr/foo@tls:tlsBroker"; - try - { - QpidURLImpl impl = new QpidURLImpl(testurl); - for (BrokerDetails d : impl.getAllBrokerDetails()) - { - System.out.println(d); - } - } - catch (Exception e) - { - e.printStackTrace(); - } - } -} diff --git a/java/common/src/main/java/org/apache/qpid/util/FileUtils.java b/java/common/src/main/java/org/apache/qpid/util/FileUtils.java index 7494745457..7ba38f4743 100644 --- a/java/common/src/main/java/org/apache/qpid/util/FileUtils.java +++ b/java/common/src/main/java/org/apache/qpid/util/FileUtils.java @@ -20,7 +20,18 @@ */ package org.apache.qpid.util; -import java.io.*; +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.LinkedList; +import java.util.List; /** * FileUtils provides some simple helper methods for working with files. It follows the convention of wrapping all @@ -48,14 +59,31 @@ public class FileUtils try { - is = new BufferedInputStream(new FileInputStream(filename)); + try + { + is = new BufferedInputStream(new FileInputStream(filename)); + } + catch (FileNotFoundException e) + { + throw new RuntimeException(e); + } + + return readStreamAsString(is); } - catch (FileNotFoundException e) + finally { - throw new RuntimeException(e); + if (is != null) + { + try + { + is.close(); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } } - - return readStreamAsString(is); } /** @@ -168,28 +196,182 @@ public class FileUtils { try { - InputStream in = new FileInputStream(src); - if (!dst.exists()) + copyCheckedEx(src, dst); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + + /** + * Copies the specified source file to the specified destination file. If the destination file does not exist, + * it is created. + * + * @param src The source file name. + * @param dst The destination file name. + * @throws IOException + */ + public static void copyCheckedEx(File src, File dst) throws IOException + { + 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(); + } + + /* + * Deletes a given file + */ + public static boolean deleteFile(String filePath) + { + return delete(new File(filePath), false); + } + + /* + * Deletes a given empty directory + */ + public static boolean deleteDirectory(String directoryPath) + { + File directory = new File(directoryPath); + + if (directory.isDirectory()) + { + if (directory.listFiles().length == 0) { - dst.createNewFile(); + return delete(directory, true); } + } - OutputStream out = new FileOutputStream(dst); + return false; + } - // Transfer bytes from in to out - byte[] buf = new byte[1024]; - int len; - while ((len = in.read(buf)) > 0) + /** + * Delete a given file/directory, + * A directory will always require the recursive flag to be set. + * if a directory is specified and recursive set then delete the whole tree + * + * @param file the File object to start at + * @param recursive boolean to recurse if a directory is specified. + * + * @return <code>true</code> if and only if the file or directory is + * successfully deleted; <code>false</code> otherwise + */ + public static boolean delete(File file, boolean recursive) + { + boolean success = true; + + if (file.isDirectory()) + { + if (recursive) { - out.write(buf, 0, len); + File[] files = file.listFiles(); + + // This can occur if the file is deleted outside the JVM + if (files == null) + { + return false; + } + + for (int i = 0; i < files.length; i++) + { + success = delete(files[i], true) && success; + } + + return success && file.delete(); } - in.close(); - out.close(); + return false; } - catch (IOException e) + + return file.delete(); + } + + public static class UnableToCopyException extends Exception + { + UnableToCopyException(String msg) { - throw new RuntimeException(e); + super(msg); } } + + public static void copyRecursive(File source, File dst) throws FileNotFoundException, UnableToCopyException + { + + if (!source.exists()) + { + throw new FileNotFoundException("Unable to copy '" + source.toString() + "' as it does not exist."); + } + + if (dst.exists() && !dst.isDirectory()) + { + throw new IllegalArgumentException("Unable to copy '" + source.toString() + "' to '" + dst + "' a file with same name exists."); + } + + if (source.isFile()) + { + copy(source, dst); + } + + //else we have a source directory + if (!dst.isDirectory() && !dst.mkdir()) + { + throw new UnableToCopyException("Unable to create destination directory"); + } + + for (File file : source.listFiles()) + { + if (file.isFile()) + { + copy(file, new File(dst.toString() + File.separator + file.getName())); + } + else + { + copyRecursive(file, new File(dst + File.separator + file.getName())); + } + } + + } + + /** + * Checks the specified file for instances of the search string. + * + * @param file the file to search + * @param search the search String + * + * @throws java.io.IOException + * @return the list of matching entries + */ + public static List<String> searchFile(File file, String search) + throws IOException + { + + List<String> results = new LinkedList<String>(); + + BufferedReader reader = new BufferedReader(new FileReader(file)); + while (reader.ready()) + { + String line = reader.readLine(); + if (line.contains(search)) + { + results.add(line); + } + } + + return results; + } } diff --git a/java/common/src/main/java/org/apache/qpid/util/NetMatcher.java b/java/common/src/main/java/org/apache/qpid/util/NetMatcher.java new file mode 100644 index 0000000000..4c653e6ca0 --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/util/NetMatcher.java @@ -0,0 +1,264 @@ +/*********************************************************************** + * Copyright (c) 2000-2006 The Apache Software Foundation. * + * All rights reserved. * + * ------------------------------------------------------------------- * + * Licensed under the Apache License, Version 2.0 (the "License"); you * + * may not use this file except in compliance with the License. You * + * may obtain a copy of the License at: * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * + * implied. See the License for the specific language governing * + * permissions and limitations under the License. * + ***********************************************************************/ + +package org.apache.qpid.util; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +public class NetMatcher +{ + private ArrayList networks; + + public void initInetNetworks(final Collection nets) + { + networks = new ArrayList(); + for (Iterator iter = nets.iterator(); iter.hasNext(); ) try + { + InetNetwork net = InetNetwork.getFromString((String) iter.next()); + if (!networks.contains(net)) networks.add(net); + } + catch (java.net.UnknownHostException uhe) + { + log("Cannot resolve address: " + uhe.getMessage()); + } + networks.trimToSize(); + } + + public void initInetNetworks(final String[] nets) + { + networks = new ArrayList(); + for (int i = 0; i < nets.length; i++) try + { + InetNetwork net = InetNetwork.getFromString(nets[i]); + if (!networks.contains(net)) networks.add(net); + } + catch (java.net.UnknownHostException uhe) + { + log("Cannot resolve address: " + uhe.getMessage()); + } + networks.trimToSize(); + } + + public boolean matchInetNetwork(final String hostIP) + { + InetAddress ip = null; + + try + { + ip = InetAddress.getByName(hostIP); + } + catch (java.net.UnknownHostException uhe) + { + log("Cannot resolve address for " + hostIP + ": " + uhe.getMessage()); + } + + boolean sameNet = false; + + if (ip != null) for (Iterator iter = networks.iterator(); (!sameNet) && iter.hasNext(); ) + { + InetNetwork network = (InetNetwork) iter.next(); + sameNet = network.contains(ip); + } + return sameNet; + } + + public boolean matchInetNetwork(final InetAddress ip) + { + boolean sameNet = false; + + for (Iterator iter = networks.iterator(); (!sameNet) && iter.hasNext(); ) + { + InetNetwork network = (InetNetwork) iter.next(); + sameNet = network.contains(ip); + } + return sameNet; + } + + public NetMatcher() + { + } + + public NetMatcher(final String[] nets) + { + initInetNetworks(nets); + } + + public NetMatcher(final Collection nets) + { + initInetNetworks(nets); + } + + public String toString() { + return networks.toString(); + } + + protected void log(String s) { } +} + +class InetNetwork +{ + /* + * Implements network masking, and is compatible with RFC 1518 and + * RFC 1519, which describe CIDR: Classless Inter-Domain Routing. + */ + + private InetAddress network; + private InetAddress netmask; + + public InetNetwork(InetAddress ip, InetAddress netmask) + { + network = maskIP(ip, netmask); + this.netmask = netmask; + } + + public boolean contains(final String name) throws java.net.UnknownHostException + { + return network.equals(maskIP(InetAddress.getByName(name), netmask)); + } + + public boolean contains(final InetAddress ip) + { + return network.equals(maskIP(ip, netmask)); + } + + public String toString() + { + return network.getHostAddress() + "/" + netmask.getHostAddress(); + } + + public int hashCode() + { + return maskIP(network, netmask).hashCode(); + } + + public boolean equals(Object obj) + { + return (obj != null) && (obj instanceof InetNetwork) && + ((((InetNetwork)obj).network.equals(network)) && (((InetNetwork)obj).netmask.equals(netmask))); + } + + public static InetNetwork getFromString(String netspec) throws java.net.UnknownHostException + { + if (netspec.endsWith("*")) netspec = normalizeFromAsterisk(netspec); + else + { + int iSlash = netspec.indexOf('/'); + if (iSlash == -1) netspec += "/255.255.255.255"; + else if (netspec.indexOf('.', iSlash) == -1) netspec = normalizeFromCIDR(netspec); + } + + return new InetNetwork(InetAddress.getByName(netspec.substring(0, netspec.indexOf('/'))), + InetAddress.getByName(netspec.substring(netspec.indexOf('/') + 1))); + } + + public static InetAddress maskIP(final byte[] ip, final byte[] mask) + { + try + { + return getByAddress(new byte[] + { + (byte) (mask[0] & ip[0]), + (byte) (mask[1] & ip[1]), + (byte) (mask[2] & ip[2]), + (byte) (mask[3] & ip[3]) + }); + } + catch(Exception _) {} + { + return null; + } + } + + public static InetAddress maskIP(final InetAddress ip, final InetAddress mask) + { + return maskIP(ip.getAddress(), mask.getAddress()); + } + + /* + * This converts from an uncommon "wildcard" CIDR format + * to "address + mask" format: + * + * * => 000.000.000.0/000.000.000.0 + * xxx.* => xxx.000.000.0/255.000.000.0 + * xxx.xxx.* => xxx.xxx.000.0/255.255.000.0 + * xxx.xxx.xxx.* => xxx.xxx.xxx.0/255.255.255.0 + */ + static private String normalizeFromAsterisk(final String netspec) + { + String[] masks = { "0.0.0.0/0.0.0.0", "0.0.0/255.0.0.0", "0.0/255.255.0.0", "0/255.255.255.0" }; + char[] srcb = netspec.toCharArray(); + int octets = 0; + for (int i = 1; i < netspec.length(); i++) { + if (srcb[i] == '.') octets++; + } + return (octets == 0) ? masks[0] : netspec.substring(0, netspec.length() -1 ).concat(masks[octets]); + } + + /* + * RFC 1518, 1519 - Classless Inter-Domain Routing (CIDR) + * This converts from "prefix + prefix-length" format to + * "address + mask" format, e.g. from xxx.xxx.xxx.xxx/yy + * to xxx.xxx.xxx.xxx/yyy.yyy.yyy.yyy. + */ + static private String normalizeFromCIDR(final String netspec) + { + final int bits = 32 - Integer.parseInt(netspec.substring(netspec.indexOf('/')+1)); + final int mask = (bits == 32) ? 0 : 0xFFFFFFFF - ((1 << bits)-1); + + return netspec.substring(0, netspec.indexOf('/') + 1) + + Integer.toString(mask >> 24 & 0xFF, 10) + "." + + Integer.toString(mask >> 16 & 0xFF, 10) + "." + + Integer.toString(mask >> 8 & 0xFF, 10) + "." + + Integer.toString(mask >> 0 & 0xFF, 10); + } + + private static java.lang.reflect.Method getByAddress = null; + + static { + try { + Class inetAddressClass = Class.forName("java.net.InetAddress"); + Class[] parameterTypes = { byte[].class }; + getByAddress = inetAddressClass.getMethod("getByAddress", parameterTypes); + } catch (Exception e) { + getByAddress = null; + } + } + + private static InetAddress getByAddress(byte[] ip) throws java.net.UnknownHostException + { + InetAddress addr = null; + if (getByAddress != null) try { + addr = (InetAddress) getByAddress.invoke(null, new Object[] { ip }); + } catch (IllegalAccessException e) { + } catch (java.lang.reflect.InvocationTargetException e) { + } + + if (addr == null) { + addr = InetAddress.getByName + ( + Integer.toString(ip[0] & 0xFF, 10) + "." + + Integer.toString(ip[1] & 0xFF, 10) + "." + + Integer.toString(ip[2] & 0xFF, 10) + "." + + Integer.toString(ip[3] & 0xFF, 10) + ); + } + return addr; + } +} diff --git a/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java b/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java deleted file mode 100644 index d90e3b1a17..0000000000 --- a/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.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/java/common/src/main/java/org/apache/qpid/util/Serial.java b/java/common/src/main/java/org/apache/qpid/util/Serial.java index 44712984c0..8ad9d00f54 100644 --- a/java/common/src/main/java/org/apache/qpid/util/Serial.java +++ b/java/common/src/main/java/org/apache/qpid/util/Serial.java @@ -1,4 +1,25 @@ package org.apache.qpid.util; +/* + * + * 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. + * + */ + import java.util.Comparator; diff --git a/java/common/src/main/java/org/apache/qpid/util/Strings.java b/java/common/src/main/java/org/apache/qpid/util/Strings.java index 4b199bafe6..04bf174ff0 100644 --- a/java/common/src/main/java/org/apache/qpid/util/Strings.java +++ b/java/common/src/main/java/org/apache/qpid/util/Strings.java @@ -22,6 +22,12 @@ package org.apache.qpid.util; import java.io.UnsupportedEncodingException; +import java.util.Map; +import java.util.Properties; +import java.util.Stack; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * Strings @@ -79,4 +85,154 @@ public final class Strings } } + public static final String fromUTF8(byte[] bytes) + { + try + { + return new String(bytes, "UTF-8"); + } + catch (UnsupportedEncodingException e) + { + throw new RuntimeException(e); + } + } + + private static final Pattern VAR = Pattern.compile("(?:\\$\\{([^\\}]*)\\})|(?:\\$(\\$))"); + + public static interface Resolver + { + String resolve(String variable); + } + + public static class MapResolver implements Resolver + { + + private final Map<String,String> map; + + public MapResolver(Map<String,String> map) + { + this.map = map; + } + + public String resolve(String variable) + { + return map.get(variable); + } + } + + public static class PropertiesResolver implements Resolver + { + + private final Properties properties; + + public PropertiesResolver(Properties properties) + { + this.properties = properties; + } + + public String resolve(String variable) + { + return properties.getProperty(variable); + } + } + + public static class ChainedResolver implements Resolver + { + private final Resolver primary; + private final Resolver secondary; + + public ChainedResolver(Resolver primary, Resolver secondary) + { + this.primary = primary; + this.secondary = secondary; + } + + public String resolve(String variable) + { + String result = primary.resolve(variable); + if (result == null) + { + result = secondary.resolve(variable); + } + return result; + } + } + + public static final Resolver SYSTEM_RESOLVER = new Resolver() + { + public String resolve(String variable) + { + String result = System.getProperty(variable); + if (result == null) + { + result = System.getenv(variable); + } + return result; + } + }; + + public static final String expand(String input) + { + return expand(input, SYSTEM_RESOLVER); + } + + public static final String expand(String input, Resolver resolver) + { + return expand(input, resolver, new Stack()); + } + + private static final String expand(String input, Resolver resolver, Stack<String> stack) + { + Matcher m = VAR.matcher(input); + StringBuffer result = new StringBuffer(); + while (m.find()) + { + String var = m.group(1); + if (var == null) + { + String esc = m.group(2); + if ("$".equals(esc)) + { + m.appendReplacement(result, Matcher.quoteReplacement("$")); + } + else + { + throw new IllegalArgumentException(esc); + } + } + else + { + m.appendReplacement(result, Matcher.quoteReplacement(resolve(var, resolver, stack))); + } + } + m.appendTail(result); + return result.toString(); + } + + private static final String resolve(String var, Resolver resolver, Stack<String> stack) + { + if (stack.contains(var)) + { + throw new IllegalArgumentException + (String.format("recursively defined variable: %s stack=%s", var, + stack)); + } + + String result = resolver.resolve(var); + if (result == null) + { + throw new IllegalArgumentException("no such variable: " + var); + } + + stack.push(var); + try + { + return expand(result, resolver, stack); + } + finally + { + stack.pop(); + } + } + } diff --git a/java/common/src/main/java/org/apache/qpid/util/concurrent/AlreadyUnblockedException.java b/java/common/src/main/java/org/apache/qpid/util/concurrent/AlreadyUnblockedException.java index ef43d1c8a8..e0c0337898 100644 --- a/java/common/src/main/java/org/apache/qpid/util/concurrent/AlreadyUnblockedException.java +++ b/java/common/src/main/java/org/apache/qpid/util/concurrent/AlreadyUnblockedException.java @@ -1,4 +1,25 @@ package org.apache.qpid.util.concurrent; +/* + * + * 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. + * + */ + /** * Used to signal that a data element and its producer cannot be requeued or sent an error message when using a diff --git a/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueue.java b/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueue.java index 261eecb561..63d8f77edb 100644 --- a/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueue.java +++ b/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueue.java @@ -1,4 +1,25 @@ package org.apache.qpid.util.concurrent; +/* + * + * 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. + * + */ + import java.util.Collection; import java.util.concurrent.BlockingQueue; diff --git a/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueueBase.java b/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueueBase.java index d1c1abd285..4564b1d686 100644 --- a/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueueBase.java +++ b/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueueBase.java @@ -1,4 +1,25 @@ package org.apache.qpid.util.concurrent; +/* + * + * 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. + * + */ + import java.util.*; import java.util.concurrent.TimeUnit; diff --git a/java/common/src/main/java/org/apache/qpid/util/concurrent/BooleanLatch.java b/java/common/src/main/java/org/apache/qpid/util/concurrent/BooleanLatch.java index 391ca686c9..0e4a07594f 100644 --- a/java/common/src/main/java/org/apache/qpid/util/concurrent/BooleanLatch.java +++ b/java/common/src/main/java/org/apache/qpid/util/concurrent/BooleanLatch.java @@ -1,4 +1,25 @@ package org.apache.qpid.util.concurrent; +/* + * + * 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. + * + */ + import java.util.concurrent.locks.AbstractQueuedSynchronizer; diff --git a/java/common/src/main/java/org/apache/qpid/util/concurrent/Capacity.java b/java/common/src/main/java/org/apache/qpid/util/concurrent/Capacity.java index e317c84971..a97ce0e172 100644 --- a/java/common/src/main/java/org/apache/qpid/util/concurrent/Capacity.java +++ b/java/common/src/main/java/org/apache/qpid/util/concurrent/Capacity.java @@ -1,4 +1,25 @@ package org.apache.qpid.util.concurrent; +/* + * + * 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. + * + */ + /** * An interface exposed by data structures that have a maximum capacity. diff --git a/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchBuffer.java b/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchBuffer.java index 49020803d7..bc63eb0353 100644 --- a/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchBuffer.java +++ b/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchBuffer.java @@ -1,4 +1,25 @@ package org.apache.qpid.util.concurrent; +/* + * + * 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. + * + */ + import java.util.Queue; diff --git a/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchException.java b/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchException.java index 77b60f2b72..99a83f96cd 100644 --- a/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchException.java +++ b/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchException.java @@ -1,4 +1,25 @@ package org.apache.qpid.util.concurrent; +/* + * + * 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. + * + */ + /** * SynchException is used to encapsulate exceptions with the data elements that caused them in order to send exceptions diff --git a/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchQueue.java b/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchQueue.java index 9d15c211f6..95833f398a 100644 --- a/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchQueue.java +++ b/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchQueue.java @@ -1,4 +1,25 @@ package org.apache.qpid.util.concurrent; +/* + * + * 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. + * + */ + import java.util.LinkedList; import java.util.Queue; diff --git a/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRecord.java b/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRecord.java index 5e002100c2..fd740c20cd 100644 --- a/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRecord.java +++ b/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRecord.java @@ -1,4 +1,25 @@ package org.apache.qpid.util.concurrent; +/* + * + * 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. + * + */ + /** * SynchRecord associates a data item from a {@link BatchSynchQueue} with its producer. This enables the data item data diff --git a/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRef.java b/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRef.java index a75f7b766d..efe2344c06 100644 --- a/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRef.java +++ b/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRef.java @@ -1,4 +1,25 @@ package org.apache.qpid.util.concurrent; +/* + * + * 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. + * + */ + /** * A SynchRef is an interface which is returned from the synchronous take and drain methods of {@link BatchSynchQueue}, diff --git a/java/common/src/test/java/org/apache/qpid/AMQExceptionTest.java b/java/common/src/test/java/org/apache/qpid/AMQExceptionTest.java new file mode 100644 index 0000000000..ef6cd41492 --- /dev/null +++ b/java/common/src/test/java/org/apache/qpid/AMQExceptionTest.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; + +import junit.framework.TestCase; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.framing.AMQFrameDecodingException; + +/** + * This test is to ensure that when an AMQException is rethrown that the specified exception is correctly wrapped up. + * + * There are three cases: + * Re-throwing an AMQException + * Re-throwing a Subclass of AMQException + * Re-throwing a Subclass of AMQException that does not have the default AMQException constructor which will force the + * creation of an AMQException. + */ +public class AMQExceptionTest extends TestCase +{ + /** + * Test that an AMQException will be correctly created and rethrown. + */ + public void testRethrowGeneric() + { + AMQException test = new AMQException(AMQConstant.ACCESS_REFUSED, "refused", new RuntimeException()); + + AMQException e = reThrowException(test); + + assertEquals("Exception not of correct class", AMQException.class, e.getClass()); + + } + + /** + * Test that a subclass of AMQException that has the default constructor will be correctly created and rethrown. + */ + public void testRethrowAMQESubclass() + { + AMQFrameDecodingException test = new AMQFrameDecodingException(AMQConstant.INTERNAL_ERROR, + "Error", + new Exception()); + AMQException e = reThrowException(test); + + assertEquals("Exception not of correct class", AMQFrameDecodingException.class, e.getClass()); + } + + /** + * Test that a subclass of AMQException that doesnot have the default constructor will be correctly rethrown as an + * AMQException + */ + public void testRethrowAMQESubclassNoConstructor() + { + AMQExceptionSubclass test = new AMQExceptionSubclass("Invalid Argument Exception"); + + AMQException e = reThrowException(test); + + assertEquals("Exception not of correct class", AMQException.class, e.getClass()); + } + + /** + * Private method to rethrown and validate the basic values of the rethrown + * @param test Exception to rethrow + * @throws AMQException the rethrown exception + */ + private AMQException reThrowException(AMQException test) + { + AMQException amqe = test.cloneForCurrentThread(); + + assertEquals("Error code does not match.", test.getErrorCode(), amqe.getErrorCode()); + assertTrue("Exception message does not start as expected.", amqe.getMessage().startsWith(test.getMessage())); + assertEquals("Test Exception is not set as the cause", test, amqe.getCause()); + assertEquals("Cause is not correct", test.getCause(), amqe.getCause().getCause()); + + return amqe; + } + + /** + * Private class that extends AMQException but does not have a default exception. + */ + private class AMQExceptionSubclass extends AMQException + { + + public AMQExceptionSubclass(String msg) + { + super(null, msg, null); + } + } +} + diff --git a/java/common/src/test/java/org/apache/qpid/codec/AMQDecoderTest.java b/java/common/src/test/java/org/apache/qpid/codec/AMQDecoderTest.java new file mode 100644 index 0000000000..46c812e265 --- /dev/null +++ b/java/common/src/test/java/org/apache/qpid/codec/AMQDecoderTest.java @@ -0,0 +1,130 @@ +package org.apache.qpid.codec; + +import java.nio.ByteBuffer; +import java.util.ArrayList; + +import junit.framework.TestCase; + +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQFrameDecodingException; +import org.apache.qpid.framing.AMQProtocolVersionException; +import org.apache.qpid.framing.HeartbeatBody; + +public class AMQDecoderTest extends TestCase +{ + + private AMQCodecFactory _factory; + private AMQDecoder _decoder; + + + public void setUp() + { + _factory = new AMQCodecFactory(false, null); + _decoder = _factory.getDecoder(); + } + + + public void testSingleFrameDecode() throws AMQProtocolVersionException, AMQFrameDecodingException + { + ByteBuffer msg = HeartbeatBody.FRAME.toNioByteBuffer(); + ArrayList<AMQDataBlock> frames = _decoder.decodeBuffer(msg); + if (frames.get(0) instanceof AMQFrame) + { + assertEquals(HeartbeatBody.FRAME.getBodyFrame().getFrameType(), ((AMQFrame) frames.get(0)).getBodyFrame().getFrameType()); + } + else + { + fail("decode was not a frame"); + } + } + + public void testPartialFrameDecode() throws AMQProtocolVersionException, AMQFrameDecodingException + { + ByteBuffer msg = HeartbeatBody.FRAME.toNioByteBuffer(); + ByteBuffer msgA = msg.slice(); + int msgbPos = msg.remaining() / 2; + int msgaLimit = msg.remaining() - msgbPos; + msgA.limit(msgaLimit); + msg.position(msgbPos); + ByteBuffer msgB = msg.slice(); + ArrayList<AMQDataBlock> frames = _decoder.decodeBuffer(msgA); + assertEquals(0, frames.size()); + frames = _decoder.decodeBuffer(msgB); + assertEquals(1, frames.size()); + if (frames.get(0) instanceof AMQFrame) + { + assertEquals(HeartbeatBody.FRAME.getBodyFrame().getFrameType(), ((AMQFrame) frames.get(0)).getBodyFrame().getFrameType()); + } + else + { + fail("decode was not a frame"); + } + } + + public void testMultipleFrameDecode() throws AMQProtocolVersionException, AMQFrameDecodingException + { + ByteBuffer msgA = HeartbeatBody.FRAME.toNioByteBuffer(); + ByteBuffer msgB = HeartbeatBody.FRAME.toNioByteBuffer(); + ByteBuffer msg = ByteBuffer.allocate(msgA.remaining() + msgB.remaining()); + msg.put(msgA); + msg.put(msgB); + msg.flip(); + ArrayList<AMQDataBlock> frames = _decoder.decodeBuffer(msg); + assertEquals(2, frames.size()); + for (AMQDataBlock frame : frames) + { + if (frame instanceof AMQFrame) + { + assertEquals(HeartbeatBody.FRAME.getBodyFrame().getFrameType(), ((AMQFrame) frame).getBodyFrame().getFrameType()); + } + else + { + fail("decode was not a frame"); + } + } + } + + public void testMultiplePartialFrameDecode() throws AMQProtocolVersionException, AMQFrameDecodingException + { + ByteBuffer msgA = HeartbeatBody.FRAME.toNioByteBuffer(); + ByteBuffer msgB = HeartbeatBody.FRAME.toNioByteBuffer(); + ByteBuffer msgC = HeartbeatBody.FRAME.toNioByteBuffer(); + + ByteBuffer sliceA = ByteBuffer.allocate(msgA.remaining() + msgB.remaining() / 2); + sliceA.put(msgA); + int limit = msgB.limit(); + int pos = msgB.remaining() / 2; + msgB.limit(pos); + sliceA.put(msgB); + sliceA.flip(); + msgB.limit(limit); + msgB.position(pos); + + ByteBuffer sliceB = ByteBuffer.allocate(msgB.remaining() + pos); + sliceB.put(msgB); + msgC.limit(pos); + sliceB.put(msgC); + sliceB.flip(); + msgC.limit(limit); + + ArrayList<AMQDataBlock> frames = _decoder.decodeBuffer(sliceA); + assertEquals(1, frames.size()); + frames = _decoder.decodeBuffer(sliceB); + assertEquals(1, frames.size()); + frames = _decoder.decodeBuffer(msgC); + assertEquals(1, frames.size()); + for (AMQDataBlock frame : frames) + { + if (frame instanceof AMQFrame) + { + assertEquals(HeartbeatBody.FRAME.getBodyFrame().getFrameType(), ((AMQFrame) frame).getBodyFrame().getFrameType()); + } + else + { + fail("decode was not a frame"); + } + } + } + +} diff --git a/java/common/src/test/java/org/apache/qpid/codec/MockAMQVersionAwareProtocolSession.java b/java/common/src/test/java/org/apache/qpid/codec/MockAMQVersionAwareProtocolSession.java new file mode 100644 index 0000000000..c3b91d5d18 --- /dev/null +++ b/java/common/src/test/java/org/apache/qpid/codec/MockAMQVersionAwareProtocolSession.java @@ -0,0 +1,84 @@ +package org.apache.qpid.codec; + +import java.nio.ByteBuffer; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.framing.ContentBody; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.HeartbeatBody; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.framing.ProtocolVersion; +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; +import org.apache.qpid.transport.Sender; + +public class MockAMQVersionAwareProtocolSession implements AMQVersionAwareProtocolSession +{ + + public void contentBodyReceived(int channelId, ContentBody body) throws AMQException + { + // TODO Auto-generated method stub + + } + + public void contentHeaderReceived(int channelId, ContentHeaderBody body) throws AMQException + { + // TODO Auto-generated method stub + + } + + public MethodRegistry getMethodRegistry() + { + return MethodRegistry.getMethodRegistry(ProtocolVersion.v0_9); + } + + public void heartbeatBodyReceived(int channelId, HeartbeatBody body) throws AMQException + { + // TODO Auto-generated method stub + + } + + public void init() + { + // TODO Auto-generated method stub + + } + + public void methodFrameReceived(int channelId, AMQMethodBody body) throws AMQException + { + // TODO Auto-generated method stub + + } + + public void setSender(Sender<ByteBuffer> sender) + { + // TODO Auto-generated method stub + + } + + public void writeFrame(AMQDataBlock frame) + { + // TODO Auto-generated method stub + + } + + public byte getProtocolMajorVersion() + { + // TODO Auto-generated method stub + return 0; + } + + public byte getProtocolMinorVersion() + { + // TODO Auto-generated method stub + return 0; + } + + public ProtocolVersion getProtocolVersion() + { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/java/common/src/test/java/org/apache/qpid/framing/abstraction/MessagePublishInfoImplTest.java b/java/common/src/test/java/org/apache/qpid/framing/abstraction/MessagePublishInfoImplTest.java new file mode 100644 index 0000000000..3243136287 --- /dev/null +++ b/java/common/src/test/java/org/apache/qpid/framing/abstraction/MessagePublishInfoImplTest.java @@ -0,0 +1,99 @@ +/* + * + * 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 junit.framework.TestCase; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.abstraction.MessagePublishInfoImpl; + +public class MessagePublishInfoImplTest extends TestCase +{ + MessagePublishInfoImpl _mpi; + final AMQShortString _exchange = new AMQShortString("exchange"); + final AMQShortString _routingKey = new AMQShortString("routingKey"); + + public void setUp() + { + _mpi = new MessagePublishInfoImpl(_exchange, true, true, _routingKey); + } + + /** Test that we can update the exchange value. */ + public void testExchange() + { + assertEquals(_exchange, _mpi.getExchange()); + AMQShortString newExchange = new AMQShortString("newExchange"); + //Check we can update the exchange + _mpi.setExchange(newExchange); + assertEquals(newExchange, _mpi.getExchange()); + //Ensure that the new exchange doesn't equal the old one + assertFalse(_exchange.equals(_mpi.getExchange())); + } + + /** + * Check that the immedate value is set correctly and defaulted correctly + */ + public void testIsImmediate() + { + //Check that the set value is correct + assertTrue("Set value for immediate not as expected", _mpi.isImmediate()); + + MessagePublishInfoImpl mpi = new MessagePublishInfoImpl(); + + assertFalse("Default value for immediate should be false", mpi.isImmediate()); + + mpi.setImmediate(true); + + assertTrue("Updated value for immediate not as expected", mpi.isImmediate()); + + } + + /** + * Check that the mandatory value is set correctly and defaulted correctly + */ + public void testIsMandatory() + { + assertTrue("Set value for mandatory not as expected", _mpi.isMandatory()); + + MessagePublishInfoImpl mpi = new MessagePublishInfoImpl(); + + assertFalse("Default value for mandatory should be false", mpi.isMandatory()); + + mpi.setMandatory(true); + + assertTrue("Updated value for mandatory not as expected", mpi.isMandatory()); + } + + /** + * Check that the routingKey value is perserved + */ + public void testRoutingKey() + { + assertEquals(_routingKey, _mpi.getRoutingKey()); + AMQShortString newRoutingKey = new AMQShortString("newRoutingKey"); + + //Check we can update the routingKey + _mpi.setRoutingKey(newRoutingKey); + assertEquals(newRoutingKey, _mpi.getRoutingKey()); + //Ensure that the new routingKey doesn't equal the old one + assertFalse(_routingKey.equals(_mpi.getRoutingKey())); + + } +} diff --git a/java/common/src/test/java/org/apache/qpid/pool/PoolingFilterTest.java b/java/common/src/test/java/org/apache/qpid/pool/PoolingFilterTest.java deleted file mode 100644 index 6383d52298..0000000000 --- a/java/common/src/test/java/org/apache/qpid/pool/PoolingFilterTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * - */ -package org.apache.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/java/common/src/test/java/org/apache/qpid/thread/ThreadFactoryTest.java b/java/common/src/test/java/org/apache/qpid/thread/ThreadFactoryTest.java new file mode 100644 index 0000000000..9932633cb9 --- /dev/null +++ b/java/common/src/test/java/org/apache/qpid/thread/ThreadFactoryTest.java @@ -0,0 +1,46 @@ +package org.apache.qpid.thread; + +import junit.framework.TestCase; + +public class ThreadFactoryTest extends TestCase +{ + public void testThreadFactory() + { + Class threadFactoryClass = null; + try + { + threadFactoryClass = Class.forName(System.getProperty("qpid.thread_factory", + "org.apache.qpid.thread.DefaultThreadFactory")); + } + // If the thread factory class was wrong it will flagged way before it gets here. + catch(Exception e) + { + fail("Invalid thread factory class"); + } + + assertEquals(threadFactoryClass, Threading.getThreadFactory().getClass()); + } + + public void testThreadCreate() + { + Runnable r = new Runnable(){ + + public void run(){ + + } + }; + + Thread t = null; + try + { + t = Threading.getThreadFactory().createThread(r,5); + } + catch(Exception e) + { + fail("Error creating thread using Qpid thread factory"); + } + + assertNotNull(t); + assertEquals(5,t.getPriority()); + } +} diff --git a/java/common/src/test/java/org/apache/qpid/transport/ConnectionTest.java b/java/common/src/test/java/org/apache/qpid/transport/ConnectionTest.java index b9ca210483..8aa8c5f647 100644 --- a/java/common/src/test/java/org/apache/qpid/transport/ConnectionTest.java +++ b/java/common/src/test/java/org/apache/qpid/transport/ConnectionTest.java @@ -26,62 +26,148 @@ import org.apache.qpid.util.concurrent.Condition; import org.apache.qpid.transport.network.ConnectionBinding; import org.apache.qpid.transport.network.io.IoAcceptor; -import org.apache.qpid.transport.network.io.IoTransport; import org.apache.qpid.transport.util.Logger; +import org.apache.qpid.transport.util.Waiter; import junit.framework.TestCase; -import java.util.Random; +import java.util.ArrayList; +import java.util.List; +import java.util.Collections; +import java.io.IOException; + +import static org.apache.qpid.transport.Option.*; /** * ConnectionTest */ -public class ConnectionTest extends TestCase +public class ConnectionTest extends TestCase implements SessionListener { private static final Logger log = Logger.get(ConnectionTest.class); private int port; + private volatile boolean queue = false; + private List<MessageTransfer> messages = new ArrayList<MessageTransfer>(); + private List<MessageTransfer> incoming = new ArrayList<MessageTransfer>(); + + private IoAcceptor _ioa = null; + protected void setUp() throws Exception { super.setUp(); port = AvailablePortFinder.getNextAvailable(12000); + } - ConnectionDelegate server = new ConnectionDelegate() { - public void init(Channel ch, ProtocolHeader hdr) { - ch.getConnection().close(); - } + protected void tearDown() throws Exception + { + if (_ioa != null) + { + _ioa.close(); + } - public SessionDelegate getSessionDelegate() { - return new SessionDelegate() {}; - } - public void exception(Throwable t) { - log.error(t, "exception caught"); - } - public void closed() {} - }; + super.tearDown(); + } + + public void opened(Session ssn) {} + + public void resumed(Session ssn) {} + + public void message(final Session ssn, MessageTransfer xfr) + { + if (queue) + { + messages.add(xfr); + ssn.processed(xfr); + return; + } - IoAcceptor ioa = new IoAcceptor - ("localhost", port, new ConnectionBinding(server)); - ioa.start(); + String body = xfr.getBodyString(); + + if (body.startsWith("CLOSE")) + { + ssn.getConnection().close(); + } + else if (body.startsWith("DELAYED_CLOSE")) + { + ssn.processed(xfr); + new Thread() + { + public void run() + { + try + { + sleep(3000); + } + catch (InterruptedException e) + { + throw new RuntimeException(e); + } + ssn.getConnection().close(); + } + }.start(); + } + else if (body.startsWith("ECHO")) + { + int id = xfr.getId(); + ssn.invoke(xfr); + ssn.processed(id); + } + else if (body.startsWith("SINK")) + { + ssn.processed(xfr); + } + else if (body.startsWith("DROP")) + { + // do nothing + } + else if (body.startsWith("EXCP")) + { + ExecutionException exc = new ExecutionException(); + exc.setDescription("intentional exception for testing"); + ssn.invoke(exc); + ssn.close(); + } + else + { + throw new IllegalArgumentException + ("unrecognized message: " + body); + } + } + + public void exception(Session ssn, SessionException exc) + { + throw exc; + } + + public void closed(Session ssn) {} + + private void send(Session ssn, String msg) + { + send(ssn, msg, false); + } + + private void send(Session ssn, String msg, boolean sync) + { + ssn.messageTransfer + ("xxx", MessageAcceptMode.NONE, MessageAcquireMode.PRE_ACQUIRED, + null, msg, sync ? SYNC : NONE); } private Connection connect(final Condition closed) { - Connection conn = IoTransport.connect("localhost", port, new ConnectionDelegate() + Connection conn = new Connection(); + conn.setConnectionListener(new ConnectionListener() { - public SessionDelegate getSessionDelegate() + public void opened(Connection conn) {} + public void exception(Connection conn, ConnectionException exc) { - return new SessionDelegate() {}; + exc.printStackTrace(); } - public void exception(Throwable t) - { - t.printStackTrace(); - } - public void closed() + public void closed(Connection conn) { if (closed != null) { @@ -89,27 +175,94 @@ public class ConnectionTest extends TestCase } } }); - - conn.send(new ProtocolHeader(1, 0, 10)); + conn.connect("localhost", port, null, "guest", "guest", false); return conn; } + public void testProtocolNegotiationExceptionOverridesCloseException() throws Exception + { + // Force os.name to be windows to exercise code in IoReceiver + // that looks for the value of os.name + System.setProperty("os.name","windows"); + + // Start server as 0-9 to froce a ProtocolVersionException + startServer(new ProtocolHeader(1, 0, 9)); + + Condition closed = new Condition(); + + try + { + connect(closed); + fail("ProtocolVersionException expected"); + } + catch (ProtocolVersionException pve) + { + //Expected code path + } + catch (Exception e) + { + fail("ProtocolVersionException expected. Got:" + e.getMessage()); + } + } + + private void startServer() + { + startServer(new ProtocolHeader(1, 0, 10)); + } + + private void startServer(final ProtocolHeader protocolHeader) + { + ConnectionDelegate server = new ServerDelegate() + { + @Override + public void init(Connection conn, ProtocolHeader hdr) + { + conn.send(protocolHeader); + List<Object> utf8 = new ArrayList<Object>(); + utf8.add("utf8"); + conn.connectionStart(null, Collections.EMPTY_LIST, utf8); + } + + @Override + public Session getSession(Connection conn, SessionAttach atc) + { + Session ssn = super.getSession(conn, atc); + ssn.setSessionListener(ConnectionTest.this); + return ssn; + } + }; + + try + { + _ioa = new IoAcceptor("localhost", port, ConnectionBinding.get(server)); + } + catch (IOException e) + { + e.printStackTrace(); + fail("Unable to start Server for test due to:" + e.getMessage()); + } + + _ioa.start(); + } + public void testClosedNotificationAndWriteToClosed() throws Exception { + startServer(); + Condition closed = new Condition(); Connection conn = connect(closed); + + Session ssn = conn.createSession(1); + send(ssn, "CLOSE"); + if (!closed.get(3000)) { fail("never got notified of connection close"); } - Channel ch = conn.getChannel(0); - Session ssn = new Session("test".getBytes()); - ssn.attach(ch); - try { - ssn.sessionAttach(ssn.getName()); + conn.connectionCloseOk(); fail("writing to a closed socket succeeded"); } catch (TransportException e) @@ -118,4 +271,176 @@ public class ConnectionTest extends TestCase } } + class FailoverConnectionListener implements ConnectionListener + { + public void opened(Connection conn) {} + + public void exception(Connection conn, ConnectionException e) + { + throw e; + } + + public void closed(Connection conn) + { + queue = true; + conn.connect("localhost", port, null, "guest", "guest"); + conn.resume(); + } + } + + class TestSessionListener implements SessionListener + { + public void opened(Session s) {} + public void resumed(Session s) {} + public void exception(Session s, SessionException e) {} + public void message(Session s, MessageTransfer xfr) + { + synchronized (incoming) + { + incoming.add(xfr); + incoming.notifyAll(); + } + + s.processed(xfr); + } + public void closed(Session s) {} + } + + public void testResumeNonemptyReplayBuffer() throws Exception + { + startServer(); + + Connection conn = new Connection(); + conn.setConnectionListener(new FailoverConnectionListener()); + conn.connect("localhost", port, null, "guest", "guest"); + Session ssn = conn.createSession(1); + ssn.setSessionListener(new TestSessionListener()); + + send(ssn, "SINK 0"); + send(ssn, "ECHO 1"); + send(ssn, "ECHO 2"); + + ssn.sync(); + + String[] msgs = { "DROP 3", "DROP 4", "DROP 5", "CLOSE 6", "SINK 7" }; + for (String m : msgs) + { + send(ssn, m); + } + + ssn.sync(); + + assertEquals(msgs.length, messages.size()); + for (int i = 0; i < msgs.length; i++) + { + assertEquals(msgs[i], messages.get(i).getBodyString()); + } + + queue = false; + + send(ssn, "ECHO 8"); + send(ssn, "ECHO 9"); + + synchronized (incoming) + { + Waiter w = new Waiter(incoming, 30000); + while (w.hasTime() && incoming.size() < 4) + { + w.await(); + } + + assertEquals(4, incoming.size()); + assertEquals("ECHO 1", incoming.get(0).getBodyString()); + assertEquals(0, incoming.get(0).getId()); + assertEquals("ECHO 2", incoming.get(1).getBodyString()); + assertEquals(1, incoming.get(1).getId()); + assertEquals("ECHO 8", incoming.get(2).getBodyString()); + assertEquals(0, incoming.get(0).getId()); + assertEquals("ECHO 9", incoming.get(3).getBodyString()); + assertEquals(1, incoming.get(1).getId()); + } + } + + public void testResumeEmptyReplayBuffer() throws InterruptedException + { + startServer(); + + Connection conn = new Connection(); + conn.setConnectionListener(new FailoverConnectionListener()); + conn.connect("localhost", port, null, "guest", "guest"); + Session ssn = conn.createSession(1); + ssn.setSessionListener(new TestSessionListener()); + + send(ssn, "SINK 0"); + send(ssn, "SINK 1"); + send(ssn, "DELAYED_CLOSE 2"); + ssn.sync(); + Thread.sleep(6000); + send(ssn, "SINK 3"); + ssn.sync(); + System.out.println(messages); + assertEquals(1, messages.size()); + assertEquals("SINK 3", messages.get(0).getBodyString()); + } + + public void testFlushExpected() throws InterruptedException + { + startServer(); + + Connection conn = new Connection(); + conn.connect("localhost", port, null, "guest", "guest"); + Session ssn = conn.createSession(); + ssn.sessionFlush(EXPECTED); + send(ssn, "SINK 0"); + ssn.sessionFlush(EXPECTED); + send(ssn, "SINK 1"); + ssn.sync(); + } + + public void testHeartbeat() + { + startServer(); + Connection conn = new Connection(); + conn.connect("localhost", port, null, "guest", "guest"); + conn.connectionHeartbeat(); + conn.close(); + } + + public void testExecutionExceptionInvoke() throws Exception + { + startServer(); + + Connection conn = new Connection(); + conn.connect("localhost", port, null, "guest", "guest"); + Session ssn = conn.createSession(); + send(ssn, "EXCP 0"); + Thread.sleep(3000); + try + { + send(ssn, "SINK 1"); + } + catch (SessionException exc) + { + assertNotNull(exc.getException()); + } + } + + public void testExecutionExceptionSync() throws Exception + { + startServer(); + + Connection conn = new Connection(); + conn.connect("localhost", port, null, "guest", "guest"); + Session ssn = conn.createSession(); + send(ssn, "EXCP 0", true); + try + { + ssn.sync(); + } + catch (SessionException exc) + { + assertNotNull(exc.getException()); + } + } + } diff --git a/java/common/src/test/java/org/apache/qpid/transport/GenTest.java b/java/common/src/test/java/org/apache/qpid/transport/GenTest.java new file mode 100644 index 0000000000..512a0a29a6 --- /dev/null +++ b/java/common/src/test/java/org/apache/qpid/transport/GenTest.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.transport; + +import junit.framework.TestCase; + +/** + * GenTest + * + */ + +public class GenTest extends TestCase +{ + + public void testBooleans() + { + QueueDeclare qd = new QueueDeclare().queue("test-queue").durable(false); + assertEquals(qd.getQueue(), "test-queue"); + assertFalse("durable should be false", qd.getDurable()); + qd.setDurable(true); + assertTrue("durable should be true", qd.getDurable()); + qd.setDurable(false); + assertFalse("durable should be false again", qd.getDurable()); + } + +} diff --git a/java/common/src/test/java/org/apache/qpid/transport/TestNetworkDriver.java b/java/common/src/test/java/org/apache/qpid/transport/TestNetworkDriver.java new file mode 100644 index 0000000000..a4c4b59cdd --- /dev/null +++ b/java/common/src/test/java/org/apache/qpid/transport/TestNetworkDriver.java @@ -0,0 +1,122 @@ +/* + * + * 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.transport; + +import java.net.BindException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.apache.qpid.protocol.ProtocolEngine; +import org.apache.qpid.protocol.ProtocolEngineFactory; +import org.apache.qpid.ssl.SSLContextFactory; + +/** + * Test implementation of IoSession, which is required for some tests. Methods not being used are not implemented, + * so if this class is being used and some methods are to be used, then please update those. + */ +public class TestNetworkDriver implements NetworkDriver +{ + private final ConcurrentMap attributes = new ConcurrentHashMap(); + private String _remoteAddress = "127.0.0.1"; + private String _localAddress = "127.0.0.1"; + private int _port = 1; + + public TestNetworkDriver() + { + } + + public void setRemoteAddress(String string) + { + this._remoteAddress = string; + } + + public void setPort(int _port) + { + this._port = _port; + } + + public int getPort() + { + return _port; + } + + public void bind(int port, InetAddress[] addresses, ProtocolEngineFactory protocolFactory, + NetworkDriverConfiguration config, SSLContextFactory sslFactory) throws BindException + { + + } + + public SocketAddress getLocalAddress() + { + return new InetSocketAddress(_localAddress, _port); + } + + public SocketAddress getRemoteAddress() + { + return new InetSocketAddress(_remoteAddress, _port); + } + + public void open(int port, InetAddress destination, ProtocolEngine engine, NetworkDriverConfiguration config, + SSLContextFactory sslFactory) throws OpenException + { + + } + + public void setMaxReadIdle(int idleTime) + { + + } + + public void setMaxWriteIdle(int idleTime) + { + + } + + public void close() + { + + } + + public void flush() + { + + } + + public void send(ByteBuffer msg) + { + + } + + public void setIdleTimeout(long l) + { + + } + + public void setLocalAddress(String localAddress) + { + _localAddress = localAddress; + } + +} diff --git a/java/common/src/test/java/org/apache/qpid/transport/codec/BBEncoderTest.java b/java/common/src/test/java/org/apache/qpid/transport/codec/BBEncoderTest.java new file mode 100644 index 0000000000..79bf184fe2 --- /dev/null +++ b/java/common/src/test/java/org/apache/qpid/transport/codec/BBEncoderTest.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.transport.codec; + +import junit.framework.TestCase; + +import java.nio.ByteBuffer; + +/** + * BBEncoderTest + * + */ + +public class BBEncoderTest extends TestCase +{ + + public void testGrow() + { + BBEncoder enc = new BBEncoder(4); + enc.writeInt32(0xDEADBEEF); + ByteBuffer buf = enc.buffer(); + assertEquals(0xDEADBEEF, buf.getInt(0)); + enc.writeInt32(0xBEEFDEAD); + buf = enc.buffer(); + assertEquals(0xDEADBEEF, buf.getInt(0)); + assertEquals(0xBEEFDEAD, buf.getInt(4)); + } + +} diff --git a/java/common/src/test/java/org/apache/qpid/transport/network/mina/MINANetworkDriverTest.java b/java/common/src/test/java/org/apache/qpid/transport/network/mina/MINANetworkDriverTest.java new file mode 100644 index 0000000000..5af07d9735 --- /dev/null +++ b/java/common/src/test/java/org/apache/qpid/transport/network/mina/MINANetworkDriverTest.java @@ -0,0 +1,491 @@ +/* + * + * 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.transport.network.mina; + +import java.net.BindException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import junit.framework.TestCase; + +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.protocol.ProtocolEngine; +import org.apache.qpid.protocol.ProtocolEngineFactory; +import org.apache.qpid.transport.NetworkDriver; +import org.apache.qpid.transport.OpenException; + +public class MINANetworkDriverTest extends TestCase +{ + + private static final String TEST_DATA = "YHALOTHAR"; + private static final int TEST_PORT = 2323; + private NetworkDriver _server; + private NetworkDriver _client; + private CountingProtocolEngine _countingEngine; // Keeps a count of how many bytes it's read + private Exception _thrownEx; + + @Override + public void setUp() + { + _server = new MINANetworkDriver(); + _client = new MINANetworkDriver(); + _thrownEx = null; + _countingEngine = new CountingProtocolEngine(); + } + + @Override + public void tearDown() + { + if (_server != null) + { + _server.close(); + } + + if (_client != null) + { + _client.close(); + } + } + + /** + * Tests that a socket can't be opened if a driver hasn't been bound + * to the port and can be opened if a driver has been bound. + * @throws BindException + * @throws UnknownHostException + * @throws OpenException + */ + public void testBindOpen() throws BindException, UnknownHostException, OpenException + { + try + { + _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); + } + catch (OpenException e) + { + _thrownEx = e; + } + + assertNotNull("Open should have failed since no engine bound", _thrownEx); + + _server.bind(TEST_PORT, null, new EchoProtocolEngineSingletonFactory(), null, null); + + _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); + } + + /** + * Tests that a socket can't be opened after a bound NetworkDriver has been closed + * @throws BindException + * @throws UnknownHostException + * @throws OpenException + */ + public void testBindOpenCloseOpen() throws BindException, UnknownHostException, OpenException + { + _server.bind(TEST_PORT, null, new EchoProtocolEngineSingletonFactory(), null, null); + _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); + _client.close(); + _server.close(); + + try + { + _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); + } + catch (OpenException e) + { + _thrownEx = e; + } + assertNotNull("Open should have failed", _thrownEx); + } + + /** + * Checks that the right exception is thrown when binding a NetworkDriver to an already + * existing socket. + */ + public void testBindPortInUse() + { + try + { + _server.bind(TEST_PORT, null, new EchoProtocolEngineSingletonFactory(), null, null); + } + catch (BindException e) + { + fail("First bind should not fail"); + } + + try + { + _client.bind(TEST_PORT, null, new EchoProtocolEngineSingletonFactory(), null, null); + } + catch (BindException e) + { + _thrownEx = e; + } + assertNotNull("Second bind should throw BindException", _thrownEx); + } + + /** + * tests that bytes sent on a network driver are received at the other end + * + * @throws UnknownHostException + * @throws OpenException + * @throws InterruptedException + * @throws BindException + */ + public void testSend() throws UnknownHostException, OpenException, InterruptedException, BindException + { + // Open a connection from a counting engine to an echo engine + _server.bind(TEST_PORT, null, new EchoProtocolEngineSingletonFactory(), null, null); + _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); + + // Tell the counting engine how much data we're sending + _countingEngine.setNewLatch(TEST_DATA.getBytes().length); + + // Send the data and wait for up to 2 seconds to get it back + _client.send(ByteBuffer.wrap(TEST_DATA.getBytes())); + _countingEngine.getLatch().await(2, TimeUnit.SECONDS); + + // Check what we got + assertEquals("Wrong amount of data recieved", TEST_DATA.getBytes().length, _countingEngine.getReadBytes()); + } + + /** + * Opens a connection with a low read idle and check that it gets triggered + * @throws BindException + * @throws OpenException + * @throws UnknownHostException + * + */ + public void testSetReadIdle() throws BindException, UnknownHostException, OpenException + { + // Open a connection from a counting engine to an echo engine + _server.bind(TEST_PORT, null, new EchoProtocolEngineSingletonFactory(), null, null); + _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); + assertFalse("Reader should not have been idle", _countingEngine.getReaderHasBeenIdle()); + _client.setMaxReadIdle(1); + sleepForAtLeast(1500); + assertTrue("Reader should have been idle", _countingEngine.getReaderHasBeenIdle()); + } + + /** + * Opens a connection with a low write idle and check that it gets triggered + * @throws BindException + * @throws OpenException + * @throws UnknownHostException + * + */ + public void testSetWriteIdle() throws BindException, UnknownHostException, OpenException + { + // Open a connection from a counting engine to an echo engine + _server.bind(TEST_PORT, null, new EchoProtocolEngineSingletonFactory(), null, null); + _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); + assertFalse("Reader should not have been idle", _countingEngine.getWriterHasBeenIdle()); + _client.setMaxWriteIdle(1); + sleepForAtLeast(1500); + assertTrue("Reader should have been idle", _countingEngine.getWriterHasBeenIdle()); + } + + + /** + * Creates and then closes a connection from client to server and checks that the server + * has its closed() method called. Then creates a new client and closes the server to check + * that the client has its closed() method called. + * @throws BindException + * @throws UnknownHostException + * @throws OpenException + */ + public void testClosed() throws BindException, UnknownHostException, OpenException + { + // Open a connection from a counting engine to an echo engine + EchoProtocolEngineSingletonFactory factory = new EchoProtocolEngineSingletonFactory(); + _server.bind(TEST_PORT, null, factory, null, null); + _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); + EchoProtocolEngine serverEngine = null; + while (serverEngine == null) + { + serverEngine = factory.getEngine(); + if (serverEngine == null) + { + try + { + Thread.sleep(10); + } + catch (InterruptedException e) + { + } + } + } + assertFalse("Server should not have been closed", serverEngine.getClosed()); + serverEngine.setNewLatch(1); + _client.close(); + try + { + serverEngine.getLatch().await(2, TimeUnit.SECONDS); + } + catch (InterruptedException e) + { + } + assertTrue("Server should have been closed", serverEngine.getClosed()); + + _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); + _countingEngine.setClosed(false); + assertFalse("Client should not have been closed", _countingEngine.getClosed()); + _countingEngine.setNewLatch(1); + _server.close(); + try + { + _countingEngine.getLatch().await(2, TimeUnit.SECONDS); + } + catch (InterruptedException e) + { + } + assertTrue("Client should have been closed", _countingEngine.getClosed()); + } + + /** + * Create a connection and instruct the client to throw an exception when it gets some data + * and that the latch gets counted down. + * @throws BindException + * @throws UnknownHostException + * @throws OpenException + * @throws InterruptedException + */ + public void testExceptionCaught() throws BindException, UnknownHostException, OpenException, InterruptedException + { + _server.bind(TEST_PORT, null, new EchoProtocolEngineSingletonFactory(), null, null); + _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); + + + assertEquals("Exception should not have been thrown", 1, + _countingEngine.getExceptionLatch().getCount()); + _countingEngine.setErrorOnNextRead(true); + _countingEngine.setNewLatch(TEST_DATA.getBytes().length); + _client.send(ByteBuffer.wrap(TEST_DATA.getBytes())); + _countingEngine.getExceptionLatch().await(2, TimeUnit.SECONDS); + assertEquals("Exception should have been thrown", 0, + _countingEngine.getExceptionLatch().getCount()); + } + + /** + * Opens a connection and checks that the remote address is the one that was asked for + * @throws BindException + * @throws UnknownHostException + * @throws OpenException + */ + public void testGetRemoteAddress() throws BindException, UnknownHostException, OpenException + { + _server.bind(TEST_PORT, null, new EchoProtocolEngineSingletonFactory(), null, null); + _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); + assertEquals(new InetSocketAddress(InetAddress.getLocalHost(), TEST_PORT), + _client.getRemoteAddress()); + } + + private class EchoProtocolEngineSingletonFactory implements ProtocolEngineFactory + { + EchoProtocolEngine _engine = null; + + public ProtocolEngine newProtocolEngine(NetworkDriver driver) + { + if (_engine == null) + { + _engine = new EchoProtocolEngine(); + _engine.setNetworkDriver(driver); + } + return getEngine(); + } + + public EchoProtocolEngine getEngine() + { + return _engine; + } + } + + public class CountingProtocolEngine implements ProtocolEngine + { + + protected NetworkDriver _driver; + public ArrayList<ByteBuffer> _receivedBytes = new ArrayList<ByteBuffer>(); + private int _readBytes; + private CountDownLatch _latch = new CountDownLatch(0); + private boolean _readerHasBeenIdle; + private boolean _writerHasBeenIdle; + private boolean _closed = false; + private boolean _nextReadErrors = false; + private CountDownLatch _exceptionLatch = new CountDownLatch(1); + + public void closed() + { + setClosed(true); + _latch.countDown(); + } + + public void setErrorOnNextRead(boolean b) + { + _nextReadErrors = b; + } + + public void setNewLatch(int length) + { + _latch = new CountDownLatch(length); + } + + public long getReadBytes() + { + return _readBytes; + } + + public SocketAddress getRemoteAddress() + { + if (_driver != null) + { + return _driver.getRemoteAddress(); + } + else + { + return null; + } + } + + public SocketAddress getLocalAddress() + { + if (_driver != null) + { + return _driver.getLocalAddress(); + } + else + { + return null; + } + } + + public long getWrittenBytes() + { + return 0; + } + + public void readerIdle() + { + _readerHasBeenIdle = true; + } + + public void setNetworkDriver(NetworkDriver driver) + { + _driver = driver; + } + + public void writeFrame(AMQDataBlock frame) + { + + } + + public void writerIdle() + { + _writerHasBeenIdle = true; + } + + public void exception(Throwable t) + { + _exceptionLatch.countDown(); + } + + public CountDownLatch getExceptionLatch() + { + return _exceptionLatch; + } + + public void received(ByteBuffer msg) + { + // increment read bytes and count down the latch for that many + int bytes = msg.remaining(); + _readBytes += bytes; + for (int i = 0; i < bytes; i++) + { + _latch.countDown(); + } + + // Throw an error if we've been asked too, but we can still count + if (_nextReadErrors) + { + throw new RuntimeException("Was asked to error"); + } + } + + public CountDownLatch getLatch() + { + return _latch; + } + + public boolean getWriterHasBeenIdle() + { + return _writerHasBeenIdle; + } + + public boolean getReaderHasBeenIdle() + { + return _readerHasBeenIdle; + } + + public void setClosed(boolean _closed) + { + this._closed = _closed; + } + + public boolean getClosed() + { + return _closed; + } + + } + + private class EchoProtocolEngine extends CountingProtocolEngine + { + + public void received(ByteBuffer msg) + { + super.received(msg); + msg.rewind(); + _driver.send(msg); + } + } + + public static void sleepForAtLeast(long period) + { + long start = System.currentTimeMillis(); + long timeLeft = period; + while (timeLeft > 0) + { + try + { + Thread.sleep(timeLeft); + } + catch (InterruptedException e) + { + // Ignore it + } + timeLeft = period - (System.currentTimeMillis() - start); + } + } +}
\ No newline at end of file diff --git a/java/common/src/test/java/org/apache/qpid/util/FileUtilsTest.java b/java/common/src/test/java/org/apache/qpid/util/FileUtilsTest.java new file mode 100644 index 0000000000..7eba5f092e --- /dev/null +++ b/java/common/src/test/java/org/apache/qpid/util/FileUtilsTest.java @@ -0,0 +1,612 @@ +/* + * + * 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.TestCase; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.util.List; + +public class FileUtilsTest extends TestCase +{ + private static final String COPY = "-Copy"; + private static final String SUB = "-Sub"; + + /** + * Additional test for the copy method. + * Ensures that the directory count did increase by more than 1 after the copy. + */ + public void testCopyFile() + { + final String TEST_DATA = "FileUtilsTest-testCopy-TestDataTestDataTestDataTestDataTestDataTestData"; + String fileName = "FileUtilsTest-testCopy"; + String fileNameCopy = fileName + COPY; + + File[] beforeCopyFileList = null; + + //Create initial file + File test = createTestFile(fileName, TEST_DATA); + + try + { + //Check number of files before copy + beforeCopyFileList = test.getAbsoluteFile().getParentFile().listFiles(); + int beforeCopy = beforeCopyFileList.length; + + //Perform Copy + File destination = new File(fileNameCopy); + FileUtils.copy(test, destination); + //Ensure the JVM cleans up if cleanup failues + destination.deleteOnExit(); + + //Retrieve counts after copy + int afterCopy = test.getAbsoluteFile().getParentFile().listFiles().length; + + int afterCopyFromCopy = new File(fileNameCopy).getAbsoluteFile().getParentFile().listFiles().length; + + // Validate the copy counts + assertEquals("The file listing from the original and the copy differ in length.", afterCopy, afterCopyFromCopy); + assertEquals("The number of files did not increase.", beforeCopy + 1, afterCopy); + assertEquals("The number of files did not increase.", beforeCopy + 1, afterCopyFromCopy); + + //Validate copy + // Load content + String copiedFileContent = FileUtils.readFileAsString(fileNameCopy); + assertEquals(TEST_DATA, copiedFileContent); + } + finally // Ensure clean + { + //Clean up + assertTrue("Unable to cleanup", FileUtils.deleteFile(fileNameCopy)); + + //Check file list after cleanup + File[] afterCleanup = new File(test.getAbsoluteFile().getParent()).listFiles(); + checkFileLists(beforeCopyFileList, afterCleanup); + + //Remove original file + assertTrue("Unable to cleanup", test.delete()); + } + } + + /** + * Create and Copy the following structure: + * + * testDirectory --+ + * +-- testSubDirectory --+ + * +-- testSubFile + * +-- File + * + * to testDirectory-Copy + * + * Validate that the file count in the copy is correct and contents of the copied files is correct. + */ + public void testCopyRecursive() + { + final String TEST_DATA = "FileUtilsTest-testDirectoryCopy-TestDataTestDataTestDataTestDataTestDataTestData"; + String fileName = "FileUtilsTest-testCopy"; + String TEST_DIR = "testDirectoryCopy"; + + //Create Initial Structure + File testDir = new File(TEST_DIR); + + //Check number of files before copy + File[] beforeCopyFileList = testDir.getAbsoluteFile().getParentFile().listFiles(); + + try + { + //Create Directories + assertTrue("Test directory already exists cannot test.", !testDir.exists()); + + if (!testDir.mkdir()) + { + fail("Unable to make test Directory"); + } + + File testSubDir = new File(TEST_DIR + File.separator + TEST_DIR + SUB); + if (!testSubDir.mkdir()) + { + fail("Unable to make test sub Directory"); + } + + //Create Files + createTestFile(testDir.toString() + File.separator + fileName, TEST_DATA); + createTestFile(testSubDir.toString() + File.separator + fileName + SUB, TEST_DATA); + + //Ensure the JVM cleans up if cleanup failues + testSubDir.deleteOnExit(); + testDir.deleteOnExit(); + + //Perform Copy + File copyDir = new File(testDir.toString() + COPY); + try + { + FileUtils.copyRecursive(testDir, copyDir); + } + catch (FileNotFoundException e) + { + fail(e.getMessage()); + } + catch (FileUtils.UnableToCopyException e) + { + fail(e.getMessage()); + } + + //Validate Copy + assertEquals("Copied directory should only have one file and one directory in it.", 2, copyDir.listFiles().length); + + //Validate Copy File Contents + String copiedFileContent = FileUtils.readFileAsString(copyDir.toString() + File.separator + fileName); + assertEquals(TEST_DATA, copiedFileContent); + + //Validate Name of Sub Directory + assertTrue("Expected subdirectory is not a directory", new File(copyDir.toString() + File.separator + TEST_DIR + SUB).isDirectory()); + + //Assert that it contains only one item + assertEquals("Copied sub directory should only have one directory in it.", 1, new File(copyDir.toString() + File.separator + TEST_DIR + SUB).listFiles().length); + + //Validate content of Sub file + copiedFileContent = FileUtils.readFileAsString(copyDir.toString() + File.separator + TEST_DIR + SUB + File.separator + fileName + SUB); + assertEquals(TEST_DATA, copiedFileContent); + } + finally + { + //Clean up source and copy directory. + assertTrue("Unable to cleanup", FileUtils.delete(testDir, true)); + assertTrue("Unable to cleanup", FileUtils.delete(new File(TEST_DIR + COPY), true)); + + //Check file list after cleanup + File[] afterCleanup = testDir.getAbsoluteFile().getParentFile().listFiles(); + checkFileLists(beforeCopyFileList, afterCleanup); + } + } + + /** + * Helper method to create a test file with a string content + * + * @param fileName The fileName to use in the creation + * @param test_data The data to store in the file + * + * @return The File reference + */ + private File createTestFile(String fileName, String test_data) + { + File test = new File(fileName); + + try + { + test.createNewFile(); + //Ensure the JVM cleans up if cleanup failues + test.deleteOnExit(); + } + catch (IOException e) + { + fail(e.getMessage()); + } + + BufferedWriter writer = null; + try + { + writer = new BufferedWriter(new FileWriter(test)); + try + { + writer.write(test_data); + } + catch (IOException e) + { + fail(e.getMessage()); + } + } + catch (IOException e) + { + fail(e.getMessage()); + } + finally + { + try + { + if (writer != null) + { + writer.close(); + } + } + catch (IOException e) + { + fail(e.getMessage()); + } + } + + return test; + } + + /** Test that deleteFile only deletes the specified file */ + public void testDeleteFile() + { + File test = new File("FileUtilsTest-testDelete"); + //Record file count in parent directory to check it is not changed by delete + String path = test.getAbsolutePath(); + File[] filesBefore = new File(path.substring(0, path.lastIndexOf(File.separator))).listFiles(); + int fileCountBefore = filesBefore.length; + + try + { + test.createNewFile(); + //Ensure the JVM cleans up if cleanup failues + test.deleteOnExit(); + } + catch (IOException e) + { + fail(e.getMessage()); + } + + assertTrue("File does not exists", test.exists()); + assertTrue("File is not a file", test.isFile()); + + //Check that file creation can be seen on disk + int fileCountCreated = new File(path.substring(0, path.lastIndexOf(File.separator))).listFiles().length; + assertEquals("File creation was no registered", fileCountBefore + 1, fileCountCreated); + + //Perform Delete + assertTrue("Unable to cleanup", FileUtils.deleteFile("FileUtilsTest-testDelete")); + + assertTrue("File exists after delete", !test.exists()); + + //Check that after deletion the file count is now accurate + File[] filesAfter = new File(path.substring(0, path.lastIndexOf(File.separator))).listFiles(); + int fileCountAfter = filesAfter.length; + assertEquals("File creation was no registered", fileCountBefore, fileCountAfter); + + checkFileLists(filesBefore, filesAfter); + } + + public void testDeleteNonExistentFile() + { + File test = new File("FileUtilsTest-testDelete-" + System.currentTimeMillis()); + + assertTrue("File exists", !test.exists()); + assertFalse("File is a directory", test.isDirectory()); + + assertTrue("Delete Succeeded ", !FileUtils.delete(test, true)); + } + + public void testDeleteNull() + { + try + { + FileUtils.delete(null, true); + fail("Delete with null value should throw NPE."); + } + catch (NullPointerException npe) + { + // expected path + } + } + + /** + * Given two lists of File arrays ensure they are the same length and all entries in Before are in After + * + * @param filesBefore File[] + * @param filesAfter File[] + */ + private void checkFileLists(File[] filesBefore, File[] filesAfter) + { + assertNotNull("Before file list cannot be null", filesBefore); + assertNotNull("After file list cannot be null", filesAfter); + + assertEquals("File lists are unequal", filesBefore.length, filesAfter.length); + + for (File fileBefore : filesBefore) + { + boolean found = false; + + for (File fileAfter : filesAfter) + { + if (fileBefore.getAbsolutePath().equals(fileAfter.getAbsolutePath())) + { + found = true; + break; + } + } + + assertTrue("File'" + fileBefore.getName() + "' was not in directory afterwards", found); + } + } + + public void testNonRecursiveNonEmptyDirectoryDeleteFails() + { + String directoryName = "FileUtilsTest-testRecursiveDelete"; + File test = new File(directoryName); + + //Record file count in parent directory to check it is not changed by delete + String path = test.getAbsolutePath(); + File[] filesBefore = new File(path.substring(0, path.lastIndexOf(File.separator))).listFiles(); + int fileCountBefore = filesBefore.length; + + assertTrue("Directory exists", !test.exists()); + + test.mkdir(); + + //Create a file in the directory + String fileName = test.getAbsolutePath() + File.separatorChar + "testFile"; + File subFile = new File(fileName); + try + { + subFile.createNewFile(); + //Ensure the JVM cleans up if cleanup failues + subFile.deleteOnExit(); + } + catch (IOException e) + { + fail(e.getMessage()); + } + //Ensure the JVM cleans up if cleanup failues + // This must be after the subFile as the directory must be empty before + // the delete is performed + test.deleteOnExit(); + + //Try and delete the non-empty directory + assertFalse("Non Empty Directory was successfully deleted.", FileUtils.deleteDirectory(directoryName)); + + //Check directory is still there + assertTrue("Directory was deleted.", test.exists()); + + // Clean up + assertTrue("Unable to cleanup", FileUtils.delete(test, true)); + + //Check that after deletion the file count is now accurate + File[] filesAfter = new File(path.substring(0, path.lastIndexOf(File.separator))).listFiles(); + int fileCountAfter = filesAfter.length; + assertEquals("File creation was no registered", fileCountBefore, fileCountAfter); + + checkFileLists(filesBefore, filesAfter); + } + + /** Test that an empty directory can be deleted with deleteDirectory */ + public void testEmptyDirectoryDelete() + { + String directoryName = "FileUtilsTest-testRecursiveDelete"; + File test = new File(directoryName); + + //Record file count in parent directory to check it is not changed by delete + String path = test.getAbsolutePath(); + File[] filesBefore = new File(path.substring(0, path.lastIndexOf(File.separator))).listFiles(); + int fileCountBefore = filesBefore.length; + + assertTrue("Directory exists", !test.exists()); + + test.mkdir(); + //Ensure the JVM cleans up if cleanup failues + test.deleteOnExit(); + + //Try and delete the empty directory + assertTrue("Non Empty Directory was successfully deleted.", FileUtils.deleteDirectory(directoryName)); + + //Check directory is still there + assertTrue("Directory was deleted.", !test.exists()); + + //Check that after deletion the file count is now accurate + File[] filesAfter = new File(path.substring(0, path.lastIndexOf(File.separator))).listFiles(); + int fileCountAfter = filesAfter.length; + assertEquals("File creation was no registered", fileCountBefore, fileCountAfter); + + checkFileLists(filesBefore, filesAfter); + + } + + /** Test that deleteDirectory on a non empty directory to complete */ + public void testNonEmptyDirectoryDelete() + { + String directoryName = "FileUtilsTest-testRecursiveDelete"; + File test = new File(directoryName); + + assertTrue("Directory exists", !test.exists()); + + //Record file count in parent directory to check it is not changed by delete + String path = test.getAbsolutePath(); + File[] filesBefore = new File(path.substring(0, path.lastIndexOf(File.separator))).listFiles(); + int fileCountBefore = filesBefore.length; + + test.mkdir(); + + //Create a file in the directory + String fileName = test.getAbsolutePath() + File.separatorChar + "testFile"; + File subFile = new File(fileName); + try + { + subFile.createNewFile(); + //Ensure the JVM cleans up if cleanup failues + subFile.deleteOnExit(); + } + catch (IOException e) + { + fail(e.getMessage()); + } + + // Ensure the JVM cleans up if cleanup failues + // This must be after the subFile as the directory must be empty before + // the delete is performed + test.deleteOnExit(); + + //Try and delete the non-empty directory non-recursively + assertFalse("Non Empty Directory was successfully deleted.", FileUtils.delete(test, false)); + + //Check directory is still there + assertTrue("Directory was deleted.", test.exists()); + + // Clean up + assertTrue("Unable to cleanup", FileUtils.delete(test, true)); + + //Check that after deletion the file count is now accurate + File[] filesAfter = new File(path.substring(0, path.lastIndexOf(File.separator))).listFiles(); + int fileCountAfter = filesAfter.length; + assertEquals("File creation was no registered", fileCountBefore, fileCountAfter); + + checkFileLists(filesBefore, filesAfter); + + } + + /** Test that a recursive delete successeds */ + public void testRecursiveDelete() + { + String directoryName = "FileUtilsTest-testRecursiveDelete"; + File test = new File(directoryName); + + assertTrue("Directory exists", !test.exists()); + + //Record file count in parent directory to check it is not changed by delete + String path = test.getAbsolutePath(); + File[] filesBefore = new File(path.substring(0, path.lastIndexOf(File.separator))).listFiles(); + int fileCountBefore = filesBefore.length; + + test.mkdir(); + + createSubDir(directoryName, 2, 4); + + //Ensure the JVM cleans up if cleanup failues + // This must be after the sub dir creation as the delete order is + // recorded and the directory must be empty to be deleted. + test.deleteOnExit(); + + assertFalse("Non recursive delete was able to directory", FileUtils.delete(test, false)); + + assertTrue("File does not exist after non recursive delete", test.exists()); + + assertTrue("Unable to cleanup", FileUtils.delete(test, true)); + + assertTrue("File exist after recursive delete", !test.exists()); + + //Check that after deletion the file count is now accurate + File[] filesAfter = new File(path.substring(0, path.lastIndexOf(File.separator))).listFiles(); + int fileCountAfter = filesAfter.length; + assertEquals("File creation was no registered", fileCountBefore, fileCountAfter); + + checkFileLists(filesBefore, filesAfter); + + } + + private void createSubDir(String path, int directories, int files) + { + File directory = new File(path); + + assertTrue("Directory" + path + " does not exists", directory.exists()); + + for (int dir = 0; dir < directories; dir++) + { + String subDirName = path + File.separatorChar + "sub" + dir; + File subDir = new File(subDirName); + + subDir.mkdir(); + + createSubDir(subDirName, directories - 1, files); + //Ensure the JVM cleans up if cleanup failues + // This must be after the sub dir creation as the delete order is + // recorded and the directory must be empty to be deleted. + subDir.deleteOnExit(); + } + + for (int file = 0; file < files; file++) + { + String subDirName = path + File.separatorChar + "file" + file; + File subFile = new File(subDirName); + try + { + subFile.createNewFile(); + //Ensure the JVM cleans up if cleanup failues + subFile.deleteOnExit(); + } + catch (IOException e) + { + fail(e.getMessage()); + } + } + } + + public static final String SEARCH_STRING = "testSearch"; + + /** + * Test searchFile(File file, String search) will find a match when it + * exists. + * + * @throws java.io.IOException if unable to perform test setup + */ + public void testSearchSucceed() throws IOException + { + File _logfile = File.createTempFile("FileUtilsTest-testSearchSucceed", ".out"); + + prepareFileForSearchTest(_logfile); + + List<String> results = FileUtils.searchFile(_logfile, SEARCH_STRING); + + assertNotNull("Null result set returned", results); + + assertEquals("Results do not contain expected count", 1, results.size()); + } + + /** + * Test searchFile(File file, String search) will not find a match when the + * test string does not exist. + * + * @throws java.io.IOException if unable to perform test setup + */ + public void testSearchFail() throws IOException + { + File _logfile = File.createTempFile("FileUtilsTest-testSearchFail", ".out"); + + prepareFileForSearchTest(_logfile); + + List<String> results = FileUtils.searchFile(_logfile, "Hello"); + + assertNotNull("Null result set returned", results); + + //Validate we only got one message + if (results.size() > 0) + { + System.err.println("Unexpected messages"); + + for (String msg : results) + { + System.err.println(msg); + } + } + + assertEquals("Results contains data when it was not expected", + 0, results.size()); + } + + /** + * Write the SEARCH_STRING in to the given file. + * + * @param logfile The file to write the SEARCH_STRING into + * + * @throws IOException if an error occurs + */ + private void prepareFileForSearchTest(File logfile) throws IOException + { + BufferedWriter writer = new BufferedWriter(new FileWriter(logfile)); + writer.append(SEARCH_STRING); + writer.flush(); + writer.close(); + } + +} diff --git a/java/common/src/test/java/org/apache/qpid/util/SerialTest.java b/java/common/src/test/java/org/apache/qpid/util/SerialTest.java index d3ab817b5d..b2578563e0 100644 --- a/java/common/src/test/java/org/apache/qpid/util/SerialTest.java +++ b/java/common/src/test/java/org/apache/qpid/util/SerialTest.java @@ -1,4 +1,25 @@ package org.apache.qpid.util; +/* + * + * 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. + * + */ + import junit.framework.TestCase; diff --git a/java/common/templates/model/ProtocolVersionListClass.vm b/java/common/templates/model/ProtocolVersionListClass.vm index 9ac6adfdf5..78605c70ff 100644 --- a/java/common/templates/model/ProtocolVersionListClass.vm +++ b/java/common/templates/model/ProtocolVersionListClass.vm @@ -41,12 +41,14 @@ public class ProtocolVersion implements Comparable { private final byte _majorVersion; private final byte _minorVersion; + private final String _stringFormat; public ProtocolVersion(byte majorVersion, byte minorVersion) { _majorVersion = majorVersion; _minorVersion = minorVersion; + _stringFormat = _majorVersion+"-"+_minorVersion; } public byte getMajorVersion() @@ -59,6 +61,22 @@ public class ProtocolVersion implements Comparable return _minorVersion; } + public byte getActualMinorVersion() + { + return _minorVersion > 90 ? (byte) (_minorVersion / 10) : _minorVersion; + } + + + public byte getRevisionVersion() + { + return _minorVersion > 90 ? (byte) (_minorVersion % 10) : (byte) 0; + } + + public String toString() + { + return _stringFormat; + } + public int compareTo(Object o) { @@ -131,15 +149,20 @@ public class ProtocolVersion implements Comparable private static final ProtocolVersion _defaultVersion; + public static final ProtocolVersion v0_10 = new ProtocolVersion((byte)0,(byte)10); + #foreach( $version in $model.getVersionSet() ) #set( $versionId = "v$version.getMajor()_$version.getMinor()" ) - public static final ProtocolVersion $versionId = new ProtocolVersion((byte)$version.getMajor(),(byte)$version.getMinor()); + public static final ProtocolVersion $versionId = new ProtocolVersion((byte)$version.getMajor(),(byte)$version.getMinor()); #end + static { SortedSet<ProtocolVersion> versions = new TreeSet<ProtocolVersion>(); + versions.add(v0_10); + _nameToVersionMap.put("0-10", v0_10); #foreach( $version in $model.getVersionSet() ) #set( $versionId = "v$version.getMajor()_$version.getMinor()" ) versions.add($versionId); diff --git a/java/common/templating.py b/java/common/templating.py index 832b7ecb9c..732e96fa60 100644 --- a/java/common/templating.py +++ b/java/common/templating.py @@ -1,3 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# class Parser: |
