http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/e4decdc1/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/MessageHeaderSectionMatcher.java ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/MessageHeaderSectionMatcher.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/MessageHeaderSectionMatcher.java new file mode 100644 index 0000000..eed6826 --- /dev/null +++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/MessageHeaderSectionMatcher.java @@ -0,0 +1,118 @@ +/* + * 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.jms.test.testpeer.matchers.sections; + +import java.util.HashMap; +import org.apache.qpid.proton.amqp.Symbol; +import org.apache.qpid.proton.amqp.UnsignedLong; +import org.apache.qpid.jms.test.testpeer.matchers.sections.MessageListSectionMatcher; +import org.hamcrest.Matcher; + +/** + * Generated by generate-message-section-matchers.xsl, which resides in this package. + */ +public class MessageHeaderSectionMatcher extends MessageListSectionMatcher +{ + + public static final Symbol DESCRIPTOR_SYMBOL = Symbol.valueOf("amqp:header:list"); + public static final UnsignedLong DESCRIPTOR_CODE = UnsignedLong.valueOf(0x0000000000000070L); + + /** Note that the ordinals of the Field enums match the order specified in the AMQP spec */ + public enum Field + { + DURABLE, + PRIORITY, + TTL, + FIRST_ACQUIRER, + DELIVERY_COUNT, + } + + public MessageHeaderSectionMatcher(boolean expectTrailingBytes) + { + super(DESCRIPTOR_CODE, + DESCRIPTOR_SYMBOL, + new HashMap<Object, Matcher<?>>(), + expectTrailingBytes); + } + + + public MessageHeaderSectionMatcher withDurable(Matcher<?> m) + { + getMatchers().put(Field.DURABLE, m); + return this; + } + + public MessageHeaderSectionMatcher withPriority(Matcher<?> m) + { + getMatchers().put(Field.PRIORITY, m); + return this; + } + + public MessageHeaderSectionMatcher withTtl(Matcher<?> m) + { + getMatchers().put(Field.TTL, m); + return this; + } + + public MessageHeaderSectionMatcher withFirstAcquirer(Matcher<?> m) + { + getMatchers().put(Field.FIRST_ACQUIRER, m); + return this; + } + + public MessageHeaderSectionMatcher withDeliveryCount(Matcher<?> m) + { + getMatchers().put(Field.DELIVERY_COUNT, m); + return this; + } + + public Object getReceivedDurable() + { + return getReceivedFields().get(Field.DURABLE); + } + + public Object getReceivedPriority() + { + return getReceivedFields().get(Field.PRIORITY); + } + + public Object getReceivedTtl() + { + return getReceivedFields().get(Field.TTL); + } + + public Object getReceivedFirstAcquirer() + { + return getReceivedFields().get(Field.FIRST_ACQUIRER); + } + + public Object getReceivedDeliveryCount() + { + return getReceivedFields().get(Field.DELIVERY_COUNT); + } + + @Override + protected Enum<?> getField(int fieldIndex) + { + return Field.values()[fieldIndex]; + } +} +
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/e4decdc1/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/MessageListSectionMatcher.java ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/MessageListSectionMatcher.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/MessageListSectionMatcher.java new file mode 100644 index 0000000..01d8ab0 --- /dev/null +++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/MessageListSectionMatcher.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.jms.test.testpeer.matchers.sections; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.qpid.proton.amqp.Symbol; +import org.apache.qpid.proton.amqp.UnsignedLong; +import org.hamcrest.Matcher; + +public abstract class MessageListSectionMatcher extends AbstractMessageSectionMatcher +{ + public MessageListSectionMatcher(UnsignedLong numericDescriptor, + Symbol symbolicDescriptor, + Map<Object, Matcher<?>> fieldMatchers, + boolean expectTrailingBytes) + { + super(numericDescriptor, symbolicDescriptor, fieldMatchers, expectTrailingBytes); + } + + @SuppressWarnings("unchecked") + @Override + protected void verifyReceivedDescribedObject(Object described) + { + if(!(described instanceof List)) + { + throw new IllegalArgumentException("Unexpected section contents. Expected List, but got: " + + (described == null ? "null" : described.getClass())); + } + + int fieldNumber = 0; + Map<Object, Object> valueMap = new HashMap<Object, Object>(); + for(Object value : (List<Object>)described) + { + valueMap.put(getField(fieldNumber++), value); + } + + verifyReceivedFields(valueMap); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/e4decdc1/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/MessageMapSectionMatcher.java ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/MessageMapSectionMatcher.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/MessageMapSectionMatcher.java new file mode 100644 index 0000000..be3f0a4 --- /dev/null +++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/MessageMapSectionMatcher.java @@ -0,0 +1,55 @@ +/* + * 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.jms.test.testpeer.matchers.sections; + +import java.util.Map; + +import org.apache.qpid.proton.amqp.Symbol; +import org.apache.qpid.proton.amqp.UnsignedLong; +import org.hamcrest.Matcher; + +public abstract class MessageMapSectionMatcher extends AbstractMessageSectionMatcher +{ + public MessageMapSectionMatcher(UnsignedLong numericDescriptor, + Symbol symbolicDescriptor, + Map<Object, Matcher<?>> fieldMatchers, + boolean expectTrailingBytes) + { + super(numericDescriptor, symbolicDescriptor, fieldMatchers, expectTrailingBytes); + } + + @SuppressWarnings("unchecked") + @Override + protected void verifyReceivedDescribedObject(Object described) + { + if(!(described instanceof Map)) + { + throw new IllegalArgumentException("Unexpected section contents. Expected Map, but got: " + + (described == null ? "null" : described.getClass())); + } + + verifyReceivedFields((Map<Object,Object>) described); + } + + public MessageMapSectionMatcher withEntry(Object key, Matcher<?> m) + { + getMatchers().put(key, m); + return this; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/e4decdc1/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/MessagePropertiesSectionMatcher.java ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/MessagePropertiesSectionMatcher.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/MessagePropertiesSectionMatcher.java new file mode 100644 index 0000000..35a523a --- /dev/null +++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/MessagePropertiesSectionMatcher.java @@ -0,0 +1,214 @@ +/* + * 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.jms.test.testpeer.matchers.sections; + +import java.util.HashMap; +import org.apache.qpid.proton.amqp.Symbol; +import org.apache.qpid.proton.amqp.UnsignedLong; +import org.apache.qpid.jms.test.testpeer.matchers.sections.MessageListSectionMatcher; +import org.hamcrest.Matcher; + +/** + * Generated by generate-message-section-matchers.xsl, which resides in this package. + */ +public class MessagePropertiesSectionMatcher extends MessageListSectionMatcher +{ + + public static final Symbol DESCRIPTOR_SYMBOL = Symbol.valueOf("amqp:properties:list"); + public static final UnsignedLong DESCRIPTOR_CODE = UnsignedLong.valueOf(0x0000000000000073L); + + /** Note that the ordinals of the Field enums match the order specified in the AMQP spec */ + public enum Field + { + MESSAGE_ID, + USER_ID, + TO, + SUBJECT, + REPLY_TO, + CORRELATION_ID, + CONTENT_TYPE, + CONTENT_ENCODING, + ABSOLUTE_EXPIRY_TIME, + CREATION_TIME, + GROUP_ID, + GROUP_SEQUENCE, + REPLY_TO_GROUP_ID, + } + + public MessagePropertiesSectionMatcher(boolean expectTrailingBytes) + { + super(DESCRIPTOR_CODE, + DESCRIPTOR_SYMBOL, + new HashMap<Object, Matcher<?>>(), + expectTrailingBytes); + } + + + public MessagePropertiesSectionMatcher withMessageId(Matcher<?> m) + { + getMatchers().put(Field.MESSAGE_ID, m); + return this; + } + + public MessagePropertiesSectionMatcher withUserId(Matcher<?> m) + { + getMatchers().put(Field.USER_ID, m); + return this; + } + + public MessagePropertiesSectionMatcher withTo(Matcher<?> m) + { + getMatchers().put(Field.TO, m); + return this; + } + + public MessagePropertiesSectionMatcher withSubject(Matcher<?> m) + { + getMatchers().put(Field.SUBJECT, m); + return this; + } + + public MessagePropertiesSectionMatcher withReplyTo(Matcher<?> m) + { + getMatchers().put(Field.REPLY_TO, m); + return this; + } + + public MessagePropertiesSectionMatcher withCorrelationId(Matcher<?> m) + { + getMatchers().put(Field.CORRELATION_ID, m); + return this; + } + + public MessagePropertiesSectionMatcher withContentType(Matcher<?> m) + { + getMatchers().put(Field.CONTENT_TYPE, m); + return this; + } + + public MessagePropertiesSectionMatcher withContentEncoding(Matcher<?> m) + { + getMatchers().put(Field.CONTENT_ENCODING, m); + return this; + } + + public MessagePropertiesSectionMatcher withAbsoluteExpiryTime(Matcher<?> m) + { + getMatchers().put(Field.ABSOLUTE_EXPIRY_TIME, m); + return this; + } + + public MessagePropertiesSectionMatcher withCreationTime(Matcher<?> m) + { + getMatchers().put(Field.CREATION_TIME, m); + return this; + } + + public MessagePropertiesSectionMatcher withGroupId(Matcher<?> m) + { + getMatchers().put(Field.GROUP_ID, m); + return this; + } + + public MessagePropertiesSectionMatcher withGroupSequence(Matcher<?> m) + { + getMatchers().put(Field.GROUP_SEQUENCE, m); + return this; + } + + public MessagePropertiesSectionMatcher withReplyToGroupId(Matcher<?> m) + { + getMatchers().put(Field.REPLY_TO_GROUP_ID, m); + return this; + } + + public Object getReceivedMessageId() + { + return getReceivedFields().get(Field.MESSAGE_ID); + } + + public Object getReceivedUserId() + { + return getReceivedFields().get(Field.USER_ID); + } + + public Object getReceivedTo() + { + return getReceivedFields().get(Field.TO); + } + + public Object getReceivedSubject() + { + return getReceivedFields().get(Field.SUBJECT); + } + + public Object getReceivedReplyTo() + { + return getReceivedFields().get(Field.REPLY_TO); + } + + public Object getReceivedCorrelationId() + { + return getReceivedFields().get(Field.CORRELATION_ID); + } + + public Object getReceivedContentType() + { + return getReceivedFields().get(Field.CONTENT_TYPE); + } + + public Object getReceivedContentEncoding() + { + return getReceivedFields().get(Field.CONTENT_ENCODING); + } + + public Object getReceivedAbsoluteExpiryTime() + { + return getReceivedFields().get(Field.ABSOLUTE_EXPIRY_TIME); + } + + public Object getReceivedCreationTime() + { + return getReceivedFields().get(Field.CREATION_TIME); + } + + public Object getReceivedGroupId() + { + return getReceivedFields().get(Field.GROUP_ID); + } + + public Object getReceivedGroupSequence() + { + return getReceivedFields().get(Field.GROUP_SEQUENCE); + } + + public Object getReceivedReplyToGroupId() + { + return getReceivedFields().get(Field.REPLY_TO_GROUP_ID); + } + + @Override + protected Enum<?> getField(int fieldIndex) + { + return Field.values()[fieldIndex]; + } +} + http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/e4decdc1/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/TransferPayloadCompositeMatcher.java ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/TransferPayloadCompositeMatcher.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/TransferPayloadCompositeMatcher.java new file mode 100644 index 0000000..a49b8a4 --- /dev/null +++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/TransferPayloadCompositeMatcher.java @@ -0,0 +1,224 @@ +/* + * + * 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.jms.test.testpeer.matchers.sections; + + +import org.apache.qpid.proton.amqp.Binary; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.StringDescription; +import org.hamcrest.TypeSafeMatcher; + +/** + * Used to verify the Transfer frame payload, i.e the sections of the AMQP message + * such as the header, properties, and body sections. + */ +public class TransferPayloadCompositeMatcher extends TypeSafeMatcher<Binary> +{ + private MessageHeaderSectionMatcher _msgHeadersMatcher; + private String _msgHeaderMatcherFailureDescription; + + private MessageAnnotationsSectionMatcher _msgAnnotationsMatcher; + private String _msgAnnotationsMatcherFailureDescription; + private MessagePropertiesSectionMatcher _propsMatcher; + private String _propsMatcherFailureDescription; + private Matcher<Binary> _msgContentMatcher; + private String _msgContentMatcherFailureDescription; + private ApplicationPropertiesSectionMatcher _appPropsMatcher; + private String _appPropsMatcherFailureDescription; + + public TransferPayloadCompositeMatcher() + { + } + + @Override + protected boolean matchesSafely(final Binary receivedBinary) + { + int origLength = receivedBinary.getLength(); + int bytesConsumed = 0; + + //MessageHeader Section + if(_msgHeadersMatcher != null) + { + Binary msgHeaderEtcSubBinary = receivedBinary.subBinary(bytesConsumed, origLength - bytesConsumed); + try + { + bytesConsumed += _msgHeadersMatcher.verify(msgHeaderEtcSubBinary); + } + catch(Throwable t) + { + _msgHeaderMatcherFailureDescription = "\nActual encoded form of remaining bytes passed to MessageHeaderMatcher: " + msgHeaderEtcSubBinary; + _msgHeaderMatcherFailureDescription += "\nMessageHeaderMatcher generated throwable: " + t; + + return false; + } + } + + //MessageAnnotations Section + if(_msgAnnotationsMatcher != null) + { + Binary msgAnnotationsEtcSubBinary = receivedBinary.subBinary(bytesConsumed, origLength - bytesConsumed); + try + { + bytesConsumed += _msgAnnotationsMatcher.verify(msgAnnotationsEtcSubBinary); + } + catch(Throwable t) + { + _msgAnnotationsMatcherFailureDescription = "\nActual encoded form of remaining bytes passed to MessageAnnotationsMatcher: " + msgAnnotationsEtcSubBinary; + _msgAnnotationsMatcherFailureDescription += "\nMessageAnnotationsMatcher generated throwable: " + t; + + return false; + } + } + + //Properties Section + if(_propsMatcher != null) + { + Binary propsEtcSubBinary = receivedBinary.subBinary(bytesConsumed, origLength - bytesConsumed); + try + { + bytesConsumed += _propsMatcher.verify(propsEtcSubBinary); + } + catch(Throwable t) + { + _propsMatcherFailureDescription = "\nActual encoded form of remaining bytes passed to PropertiesMatcher: " + propsEtcSubBinary; + _propsMatcherFailureDescription += "\nPropertiesMatcher generated throwable: " + t; + + return false; + } + } + + //Application Properties Section + if(_appPropsMatcher != null) + { + Binary appPropsEtcSubBinary = receivedBinary.subBinary(bytesConsumed, origLength - bytesConsumed); + try + { + bytesConsumed += _appPropsMatcher.verify(appPropsEtcSubBinary); + } + catch(Throwable t) + { + _appPropsMatcherFailureDescription = "\nActual encoded form of remaining bytes passed to ApplicationPropertiesMatcher: " + appPropsEtcSubBinary; + _appPropsMatcherFailureDescription += "\nApplicationPropertiesMatcher generated throwable: " + t; + + return false; + } + } + //Message Content Body Section, already a Matcher<Binary> + if(_msgContentMatcher != null) + { + Binary msgContentBodyEtcSubBinary = receivedBinary.subBinary(bytesConsumed, origLength - bytesConsumed); + boolean contentMatches = _msgContentMatcher.matches(msgContentBodyEtcSubBinary); + if(!contentMatches) + { + Description desc = new StringDescription(); + _msgContentMatcher.describeTo(desc); + _msgContentMatcher.describeMismatch(msgContentBodyEtcSubBinary, desc); + + _msgContentMatcherFailureDescription = "\nMessageContentMatcher mismatch Description:"; + _msgContentMatcherFailureDescription += desc.toString(); + + return false; + } + } + + //TODO: we will need figure out a way to determine how many bytes the + //MessageContentMatcher did/should consume when it comes time to handle footers + return true; + } + + @Override + public void describeTo(Description description) + { + description.appendText("a Binary encoding of a Transfer frames payload, containing an AMQP message"); + } + + @Override + protected void describeMismatchSafely(Binary item, Description mismatchDescription) + { + mismatchDescription.appendText("\nActual encoded form of the full Transfer frame payload: ").appendValue(item); + + //MessageHeaders Section + if(_msgHeaderMatcherFailureDescription != null) + { + mismatchDescription.appendText("\nMessageHeadersMatcherFailed!"); + mismatchDescription.appendText(_msgHeaderMatcherFailureDescription); + return; + } + + //MessageAnnotations Section + if(_msgAnnotationsMatcherFailureDescription != null) + { + mismatchDescription.appendText("\nMessageAnnotationsMatcherFailed!"); + mismatchDescription.appendText(_msgAnnotationsMatcherFailureDescription); + return; + } + + //Properties Section + if(_propsMatcherFailureDescription != null) + { + mismatchDescription.appendText("\nPropertiesMatcherFailed!"); + mismatchDescription.appendText(_propsMatcherFailureDescription); + return; + } + + //Application Properties Section + if(_appPropsMatcherFailureDescription != null) + { + mismatchDescription.appendText("\nApplicationPropertiesMatcherFailed!"); + mismatchDescription.appendText(_appPropsMatcherFailureDescription); + return; + } + + //Message Content Body Section + if(_msgContentMatcherFailureDescription != null) + { + mismatchDescription.appendText("\nContentMatcherFailed!"); + mismatchDescription.appendText(_msgContentMatcherFailureDescription); + return; + } + } + + public void setHeadersMatcher(MessageHeaderSectionMatcher msgHeadersMatcher) + { + _msgHeadersMatcher = msgHeadersMatcher; + } + + public void setMessageAnnotationsMatcher(MessageAnnotationsSectionMatcher msgAnnotationsMatcher) + { + _msgAnnotationsMatcher = msgAnnotationsMatcher; + } + + public void setPropertiesMatcher(MessagePropertiesSectionMatcher propsMatcher) + { + _propsMatcher = propsMatcher; + } + + public void setApplicationPropertiesMatcher(ApplicationPropertiesSectionMatcher appPropsMatcher) + { + _appPropsMatcher = appPropsMatcher; + } + + public void setMessageContentMatcher(Matcher<Binary> msgContentMatcher) + { + _msgContentMatcher = msgContentMatcher; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/e4decdc1/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/generate-message-section-matchers.xsl ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/generate-message-section-matchers.xsl b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/generate-message-section-matchers.xsl new file mode 100644 index 0000000..22277d3 --- /dev/null +++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/sections/generate-message-section-matchers.xsl @@ -0,0 +1,166 @@ +<?xml version="1.0" encoding="utf-8"?> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" + xmlns:exsl="http://exslt.org/common" + extension-element-prefixes="exsl"> + +<!-- Used to generate the Java classes in this package. + Changes to these classes should be effected by modifying this stylesheet then re-running it, + using a stylesheet processor that understands the exsl directives such as xsltproc --> + +<xsl:template match="/"> + <xsl:variable name="license">/* + * 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. + * + */ +</xsl:variable> + + <xsl:for-each select="descendant-or-self::node()[name()='type']"> + <xsl:variable name="classname">Message<xsl:call-template name="dashToCamel"><xsl:with-param name="input" select="@name"/></xsl:call-template>SectionMatcher</xsl:variable> + <xsl:variable name="superclass"> + <xsl:choose> + <xsl:when test="@name = 'header' or @name='properties'">MessageListSectionMatcher</xsl:when> + <xsl:otherwise>NotYetImplemented</xsl:otherwise> + </xsl:choose> + </xsl:variable> + + <xsl:if test="@provides = 'section'"> + <xsl:if test="@name = 'header' or @name='properties'"> + <xsl:call-template name="typeClass"> + <xsl:with-param name="license" select="$license"/> + <xsl:with-param name="classname" select="$classname"/> + <xsl:with-param name="superclass" select="$superclass"/> + </xsl:call-template> + </xsl:if> + </xsl:if> + + </xsl:for-each> +</xsl:template> + + +<!-- *************************************************************************************************************** --> + +<xsl:template name="typeClass"> + <xsl:param name="license"/> + <xsl:param name="classname"/> + <xsl:param name="superclass"/> + <exsl:document href="{$classname}.java" method="text"> + <xsl:value-of select="$license"/> +package org.apache.qpid.jms.test.testpeer.matchers.sections; + +import java.util.HashMap; +import org.apache.qpid.proton.amqp.Symbol; +import org.apache.qpid.proton.amqp.UnsignedLong; +import org.apache.qpid.jms.test.testpeer.matchers.sections.<xsl:value-of select="$superclass"/>; +import org.hamcrest.Matcher; + +/** + * Generated by generate-message-section-matchers.xsl, which resides in this package. + */ +public class <xsl:value-of select="$classname"/> extends <xsl:value-of select="$superclass"/> +{ + + public static final Symbol DESCRIPTOR_SYMBOL = Symbol.valueOf("<xsl:value-of select="descendant::node()[name()='descriptor']/@name"/>"); + public static final UnsignedLong DESCRIPTOR_CODE = UnsignedLong.valueOf(<xsl:value-of select="concat(substring(descendant::node()[name()='descriptor']/@code,1,10),substring(descendant::node()[name()='descriptor']/@code,14))"/>L); + + /** Note that the ordinals of the Field enums match the order specified in the AMQP spec */ + public enum Field + { +<xsl:for-each select="descendant::node()[name()='field']"> +<xsl:text> </xsl:text><xsl:call-template name="toUpperDashToUnderscore"><xsl:with-param name="input" select="@name"/></xsl:call-template>, +</xsl:for-each> } + + public <xsl:value-of select="$classname"/>(boolean expectTrailingBytes) + { + super(DESCRIPTOR_CODE, + DESCRIPTOR_SYMBOL, + new HashMap<Object, Matcher<?>>(), + expectTrailingBytes); + } + +<xsl:for-each select="descendant::node()[name()='field']"> + public <xsl:value-of select="$classname"/> with<xsl:call-template name="dashToCamel"><xsl:with-param name="input" select="@name"/></xsl:call-template>(Matcher<?> m) + { + getMatchers().put(Field.<xsl:call-template name="toUpperDashToUnderscore"><xsl:with-param name="input" select="@name"/></xsl:call-template>, m); + return this; + } +</xsl:for-each> +<xsl:for-each select="descendant::node()[name()='field']"> + public Object getReceived<xsl:call-template name="dashToCamel"><xsl:with-param name="input" select="@name"/></xsl:call-template>() + { + return getReceivedFields().get(Field.<xsl:call-template name="toUpperDashToUnderscore"><xsl:with-param name="input" select="@name"/></xsl:call-template>); + } +</xsl:for-each> + @Override + protected Enum<?> getField(int fieldIndex) + { + return Field.values()[fieldIndex]; + } +} + +</exsl:document> + +</xsl:template> + +<!-- *************************************************************************************************************** --> + +<xsl:template name="constructFromLiteral"> + <xsl:param name="type"/> + <xsl:param name="value"/> + <xsl:choose> + <xsl:when test="$type = 'string'">"<xsl:value-of select="$value"/></xsl:when> + <xsl:when test="$type = 'symbol'">Symbol.valueOf("<xsl:value-of select="$value"/>")</xsl:when> + <xsl:when test="$type = 'ubyte'">UnsignedByte.valueOf((byte) <xsl:value-of select="$value"/>)</xsl:when> + <xsl:when test="$type = 'ushort'">UnsignedShort.valueOf((short) <xsl:value-of select="$value"/>)</xsl:when> + <xsl:when test="$type = 'uint'">UnsignedInteger.valueOf(<xsl:value-of select="$value"/>)</xsl:when> + <xsl:when test="$type = 'ulong'">UnsignedLong.valueOf(<xsl:value-of select="$value"/>L)</xsl:when> + <xsl:when test="$type = 'long'"><xsl:value-of select="$value"/>L</xsl:when> + <xsl:when test="$type = 'short'">(short)<xsl:value-of select="$value"/></xsl:when> + <xsl:when test="$type = 'short'">(byte)<xsl:value-of select="$value"/></xsl:when> + <xsl:otherwise><xsl:value-of select="$value"/></xsl:otherwise> + </xsl:choose> +</xsl:template> + +<!-- *************************************************************************************************************** --> +<xsl:template name="substringAfterLast"><xsl:param name="input"/><xsl:param name="arg"/> + <xsl:choose> + <xsl:when test="contains($input,$arg)"><xsl:call-template name="substringAfterLast"><xsl:with-param name="input"><xsl:value-of select="substring-after($input,$arg)"/></xsl:with-param><xsl:with-param name="arg"><xsl:value-of select="$arg"/></xsl:with-param></xsl:call-template></xsl:when> + <xsl:otherwise><xsl:value-of select="$input"/></xsl:otherwise> + </xsl:choose> + </xsl:template> + + <xsl:template name="initCap"><xsl:param name="input"/><xsl:value-of select="translate(substring($input,1,1),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/><xsl:value-of select="substring($input,2)"/></xsl:template> + + <xsl:template name="initLower"><xsl:param name="input"/><xsl:value-of select="translate(substring($input,1,1),'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')"/><xsl:value-of select="substring($input,2)"/></xsl:template> + + <xsl:template name="toUpper"><xsl:param name="input"/><xsl:value-of select="translate($input,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/></xsl:template> + + <xsl:template name="toUpperDashToUnderscore"><xsl:param name="input"/><xsl:value-of select="translate($input,'abcdefghijklmnopqrstuvwxyz-','ABCDEFGHIJKLMNOPQRSTUVWXYZ_')"/></xsl:template> + + <xsl:template name="dashToCamel"> + <xsl:param name="input"/> + <xsl:choose> + <xsl:when test="contains($input,'-')"><xsl:call-template name="initCap"><xsl:with-param name="input" select="substring-before($input,'-')"/></xsl:call-template><xsl:call-template name="dashToCamel"><xsl:with-param name="input" select="substring-after($input,'-')"/></xsl:call-template></xsl:when> + <xsl:otherwise><xsl:call-template name="initCap"><xsl:with-param name="input" select="$input"/></xsl:call-template></xsl:otherwise> + </xsl:choose> + </xsl:template> + + <xsl:template name="dashToLowerCamel"> + <xsl:param name="input"/> + <xsl:call-template name="initLower"><xsl:with-param name="input"><xsl:call-template name="dashToCamel"><xsl:with-param name="input" select="$input"/></xsl:call-template></xsl:with-param></xsl:call-template> + </xsl:template> +</xsl:stylesheet> http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/e4decdc1/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/types/EncodedAmqpTypeMatcher.java ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/types/EncodedAmqpTypeMatcher.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/types/EncodedAmqpTypeMatcher.java new file mode 100644 index 0000000..797ee4a --- /dev/null +++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/types/EncodedAmqpTypeMatcher.java @@ -0,0 +1,114 @@ +/* + * 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.jms.test.testpeer.matchers.types; + +import org.apache.qpid.proton.Proton; +import org.apache.qpid.proton.amqp.Binary; +import org.apache.qpid.proton.amqp.DescribedType; +import org.apache.qpid.proton.amqp.Symbol; +import org.apache.qpid.proton.amqp.UnsignedLong; +import org.apache.qpid.proton.codec.Data; +import org.hamcrest.Description; +import org.hamcrest.TypeSafeMatcher; + +public abstract class EncodedAmqpTypeMatcher extends TypeSafeMatcher<Binary> +{ + private final Symbol _descriptorSymbol; + private final UnsignedLong _descriptorCode; + private final Object _expectedValue; + private boolean _permitTrailingBytes; + private DescribedType _decodedDescribedType; + private boolean _unexpectedTrailingBytes; + + public EncodedAmqpTypeMatcher(Symbol symbol, UnsignedLong code, Object expectedValue) + { + this(symbol, code, expectedValue, false); + } + + public EncodedAmqpTypeMatcher(Symbol symbol, UnsignedLong code, Object expectedValue, boolean permitTrailingBytes) + { + _descriptorSymbol = symbol; + _descriptorCode = code; + _expectedValue = expectedValue; + _permitTrailingBytes = permitTrailingBytes; + } + + protected Object getExpectedValue() + { + return _expectedValue; + } + + @Override + protected boolean matchesSafely(Binary receivedBinary) + { + int length = receivedBinary.getLength(); + Data data = Proton.data(length); + long decoded = data.decode(receivedBinary.asByteBuffer()); + _decodedDescribedType = data.getDescribedType(); + Object descriptor = _decodedDescribedType.getDescriptor(); + + if(!(_descriptorCode.equals(descriptor) || _descriptorSymbol.equals(descriptor))) + { + return false; + } + + if(_expectedValue == null && _decodedDescribedType.getDescribed() != null) + { + return false; + } + else if(_expectedValue != null && !_expectedValue.equals(_decodedDescribedType.getDescribed())) + { + return false; + } + + if(decoded < length && !_permitTrailingBytes) + { + _unexpectedTrailingBytes = true; + return false; + } + + return true; + } + + @Override + protected void describeMismatchSafely(Binary item, Description mismatchDescription) + { + mismatchDescription.appendText("\nActual encoded form: ").appendValue(item); + + if(_decodedDescribedType != null) + { + mismatchDescription.appendText("\nExpected descriptor: ") + .appendValue(_descriptorSymbol) + .appendText(" / ") + .appendValue(_descriptorCode); + + mismatchDescription.appendText("\nActual described type: ").appendValue(_decodedDescribedType); + } + + if(_unexpectedTrailingBytes) + { + mismatchDescription.appendText("\nUnexpected trailing bytes in provided bytes after decoding!"); + } + } + + /** + * Provide a description of this matcher. + */ + public abstract void describeTo(Description description); +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/e4decdc1/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/types/EncodedAmqpValueMatcher.java ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/types/EncodedAmqpValueMatcher.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/types/EncodedAmqpValueMatcher.java new file mode 100644 index 0000000..93dcc36 --- /dev/null +++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/types/EncodedAmqpValueMatcher.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.jms.test.testpeer.matchers.types; + +import org.apache.qpid.proton.amqp.Symbol; +import org.apache.qpid.proton.amqp.UnsignedLong; +import org.hamcrest.Description; + +public class EncodedAmqpValueMatcher extends EncodedAmqpTypeMatcher +{ + private static final Symbol DESCRIPTOR_SYMBOL = Symbol.valueOf("amqp:amqp-value:*"); + private static final UnsignedLong DESCRIPTOR_CODE = UnsignedLong.valueOf(0x0000000000000077L); + + /** + * @param expectedValue the value that is expected to be IN the + * received {@link AmqpValue} + */ + public EncodedAmqpValueMatcher(Object expectedValue) + { + this(expectedValue,false); + } + + /** + * @param expectedValue the value that is expected to be IN the + * received {@link AmqpValue} + * @param permitTrailingBytes if it is permitted for bytes to be left in the Binary after consuming the {@link AmqpValue} + */ + public EncodedAmqpValueMatcher(Object expectedValue, boolean permitTrailingBytes) + { + super(DESCRIPTOR_SYMBOL, DESCRIPTOR_CODE, expectedValue, permitTrailingBytes); + } + + @Override + public void describeTo(Description description) + { + description + .appendText("a Binary encoding of an AmqpValue that wraps: ") + .appendValue(getExpectedValue()); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/e4decdc1/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/types/EncodedDataMatcher.java ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/types/EncodedDataMatcher.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/types/EncodedDataMatcher.java new file mode 100644 index 0000000..94cea5e --- /dev/null +++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/matchers/types/EncodedDataMatcher.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.jms.test.testpeer.matchers.types; + +import org.apache.qpid.proton.amqp.Binary; +import org.apache.qpid.proton.amqp.Symbol; +import org.apache.qpid.proton.amqp.UnsignedLong; +import org.hamcrest.Description; + +public class EncodedDataMatcher extends EncodedAmqpTypeMatcher +{ + private static final Symbol DESCRIPTOR_SYMBOL = Symbol.valueOf("amqp:data:binary"); + private static final UnsignedLong DESCRIPTOR_CODE = UnsignedLong.valueOf(0x0000000000000075L); + + /** + * @param expectedValue the value that is expected to be IN the + * received {@link org.apache.qpid.proton.amqp.messaging.Data} + */ + public EncodedDataMatcher(Binary expectedValue) + { + this(expectedValue, false); + } + + /** + * @param expectedValue the value that is expected to be IN the + * received {@link org.apache.qpid.proton.amqp.messaging.Data} + * @param permitTrailingBytes if it is permitted for bytes to be left in the Binary after consuming the {@link AmqpValue} + */ + public EncodedDataMatcher(Binary expectedValue, boolean permitTrailingBytes) + { + super(DESCRIPTOR_SYMBOL, DESCRIPTOR_CODE, expectedValue, permitTrailingBytes); + } + + @Override + public void describeTo(Description description) + { + description + .appendText("a Binary encoding of a Data that wraps a Binary containing: ") + .appendValue(getExpectedValue()); + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/e4decdc1/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/URISupportTest.java ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/URISupportTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/URISupportTest.java new file mode 100644 index 0000000..7459e82 --- /dev/null +++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/URISupportTest.java @@ -0,0 +1,216 @@ +/** + * 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.jms.util; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.qpid.jms.util.PropertyUtil; +import org.apache.qpid.jms.util.URISupport; +import org.apache.qpid.jms.util.URISupport.CompositeData; + +import junit.framework.TestCase; + +public class URISupportTest extends TestCase { + + public void testEmptyCompositePath() throws Exception { + CompositeData data = URISupport.parseComposite(new URI("broker:()/localhost?persistent=false")); + assertEquals(0, data.getComponents().length); + } + + public void testCompositePath() throws Exception { + CompositeData data = URISupport.parseComposite(new URI("test:(path)/path")); + assertEquals("path", data.getPath()); + data = URISupport.parseComposite(new URI("test:path")); + assertNull(data.getPath()); + } + + public void testSimpleComposite() throws Exception { + CompositeData data = URISupport.parseComposite(new URI("test:part1")); + assertEquals(1, data.getComponents().length); + } + + public void testComposite() throws Exception { + URI uri = new URI("test:(part1://host,part2://(sub1://part,sube2:part))"); + CompositeData data = URISupport.parseComposite(uri); + assertEquals(2, data.getComponents().length); + } + + public void testEmptyCompositeWithParenthesisInParam() throws Exception { + URI uri = new URI("failover://()?updateURIsURL=file:/C:/Dir(1)/a.csv"); + CompositeData data = URISupport.parseComposite(uri); + assertEquals(0, data.getComponents().length); + assertEquals(1, data.getParameters().size()); + assertTrue(data.getParameters().containsKey("updateURIsURL")); + assertEquals("file:/C:/Dir(1)/a.csv", data.getParameters().get("updateURIsURL")); + } + + public void testCompositeWithParenthesisInParam() throws Exception { + URI uri = new URI("failover://(test)?updateURIsURL=file:/C:/Dir(1)/a.csv"); + CompositeData data = URISupport.parseComposite(uri); + assertEquals(1, data.getComponents().length); + assertEquals(1, data.getParameters().size()); + assertTrue(data.getParameters().containsKey("updateURIsURL")); + assertEquals("file:/C:/Dir(1)/a.csv", data.getParameters().get("updateURIsURL")); + } + + public void testCompositeWithComponentParam() throws Exception { + CompositeData data = URISupport.parseComposite(new URI("test:(part1://host?part1=true)?outside=true")); + assertEquals(1, data.getComponents().length); + assertEquals(1, data.getParameters().size()); + Map<String, String> part1Params = URISupport.parseParameters(data.getComponents()[0]); + assertEquals(1, part1Params.size()); + assertTrue(part1Params.containsKey("part1")); + } + + public void testParsingURI() throws Exception { + URI source = new URI("tcp://localhost:61626/foo/bar?cheese=Edam&x=123"); + + Map<String, String> map = PropertyUtil.parseParameters(source); + + assertEquals("Size: " + map, 2, map.size()); + assertMapKey(map, "cheese", "Edam"); + assertMapKey(map, "x", "123"); + + URI result = URISupport.removeQuery(source); + + assertEquals("result", new URI("tcp://localhost:61626/foo/bar"), result); + } + + protected void assertMapKey(Map<String, String> map, String key, Object expected) { + assertEquals("Map key: " + key, map.get(key), expected); + } + + public void testParsingCompositeURI() throws URISyntaxException { + CompositeData data = URISupport.parseComposite(new URI("broker://(tcp://localhost:61616)?name=foo")); + assertEquals("one component", 1, data.getComponents().length); + assertEquals("Size: " + data.getParameters(), 1, data.getParameters().size()); + } + + public void testCheckParenthesis() throws Exception { + String str = "fred:(((ddd))"; + assertFalse(URISupport.checkParenthesis(str)); + str += ")"; + assertTrue(URISupport.checkParenthesis(str)); + } + + public void testCreateWithQuery() throws Exception { + URI source = new URI("vm://localhost"); + URI dest = PropertyUtil.replaceQuery(source, "network=true&one=two"); + + assertEquals("correct param count", 2, URISupport.parseParameters(dest).size()); + assertEquals("same uri, host", source.getHost(), dest.getHost()); + assertEquals("same uri, scheme", source.getScheme(), dest.getScheme()); + assertFalse("same uri, ssp", dest.getQuery().equals(source.getQuery())); + } + + public void testParsingParams() throws Exception { + URI uri = new URI("static:(http://localhost:61617?proxyHost=jo&proxyPort=90)?proxyHost=localhost&proxyPort=80"); + Map<String,String>parameters = URISupport.parseParameters(uri); + verifyParams(parameters); + uri = new URI("static://http://localhost:61617?proxyHost=localhost&proxyPort=80"); + parameters = URISupport.parseParameters(uri); + verifyParams(parameters); + uri = new URI("http://0.0.0.0:61616"); + parameters = URISupport.parseParameters(uri); + } + + public void testCompositeCreateURIWithQuery() throws Exception { + String queryString = "query=value"; + URI originalURI = new URI("outerscheme:(innerscheme:innerssp)"); + URI querylessURI = originalURI; + assertEquals(querylessURI, PropertyUtil.eraseQuery(originalURI)); + assertEquals(querylessURI, PropertyUtil.replaceQuery(originalURI, "")); + assertEquals(new URI(querylessURI + "?" + queryString), PropertyUtil.replaceQuery(originalURI, queryString)); + originalURI = new URI("outerscheme:(innerscheme:innerssp)?outerquery=0"); + assertEquals(querylessURI, PropertyUtil.eraseQuery(originalURI)); + assertEquals(querylessURI, PropertyUtil.replaceQuery(originalURI, "")); + assertEquals(new URI(querylessURI + "?" + queryString), PropertyUtil.replaceQuery(originalURI, queryString)); + originalURI = new URI("outerscheme:(innerscheme:innerssp?innerquery=0)"); + querylessURI = originalURI; + assertEquals(querylessURI, PropertyUtil.eraseQuery(originalURI)); + assertEquals(querylessURI, PropertyUtil.replaceQuery(originalURI, "")); + assertEquals(new URI(querylessURI + "?" + queryString), PropertyUtil.replaceQuery(originalURI, queryString)); + originalURI = new URI("outerscheme:(innerscheme:innerssp?innerquery=0)?outerquery=0"); + assertEquals(querylessURI, PropertyUtil.eraseQuery(originalURI)); + assertEquals(querylessURI, PropertyUtil.replaceQuery(originalURI, "")); + assertEquals(new URI(querylessURI + "?" + queryString), PropertyUtil.replaceQuery(originalURI, queryString)); + } + + public void testApplyParameters() throws Exception { + + URI uri = new URI("http://0.0.0.0:61616"); + Map<String,String> parameters = new HashMap<String, String>(); + parameters.put("t.proxyHost", "localhost"); + parameters.put("t.proxyPort", "80"); + + uri = URISupport.applyParameters(uri, parameters); + Map<String,String> appliedParameters = URISupport.parseParameters(uri); + assertEquals("all params applied with no prefix", 2, appliedParameters.size()); + + // strip off params again + uri = PropertyUtil.eraseQuery(uri); + + uri = URISupport.applyParameters(uri, parameters, "joe"); + appliedParameters = URISupport.parseParameters(uri); + assertTrue("no params applied as none match joe", appliedParameters.isEmpty()); + + uri = URISupport.applyParameters(uri, parameters, "t."); + verifyParams(URISupport.parseParameters(uri)); + } + + private void verifyParams(Map<String,String> parameters) { + assertEquals(parameters.get("proxyHost"), "localhost"); + assertEquals(parameters.get("proxyPort"), "80"); + } + + public void testIsCompositeURIWithQueryNoSlashes() throws URISyntaxException { + URI[] compositeURIs = new URI[] { new URI("test:(part1://host?part1=true)?outside=true"), new URI("broker:(tcp://localhost:61616)?name=foo") }; + for (URI uri : compositeURIs) { + assertTrue(uri + " must be detected as composite URI", URISupport.isCompositeURI(uri)); + } + } + + public void testIsCompositeURIWithQueryAndSlashes() throws URISyntaxException { + URI[] compositeURIs = new URI[] { new URI("test://(part1://host?part1=true)?outside=true"), new URI("broker://(tcp://localhost:61616)?name=foo") }; + for (URI uri : compositeURIs) { + assertTrue(uri + " must be detected as composite URI", URISupport.isCompositeURI(uri)); + } + } + + public void testIsCompositeURINoQueryNoSlashes() throws URISyntaxException { + URI[] compositeURIs = new URI[] { new URI("test:(part1://host,part2://(sub1://part,sube2:part))"), new URI("test:(path)/path") }; + for (URI uri : compositeURIs) { + assertTrue(uri + " must be detected as composite URI", URISupport.isCompositeURI(uri)); + } + } + + public void testIsCompositeURINoQueryNoSlashesNoParentheses() throws URISyntaxException { + assertFalse("test:part1" + " must be detected as non-composite URI", URISupport.isCompositeURI(new URI("test:part1"))); + } + + public void testIsCompositeURINoQueryWithSlashes() throws URISyntaxException { + URI[] compositeURIs = new URI[] { new URI("failover://(tcp://bla:61616,tcp://bla:61617)"), + new URI("failover://(tcp://localhost:61616,ssl://anotherhost:61617)") }; + for (URI uri : compositeURIs) { + assertTrue(uri + " must be detected as composite URI", URISupport.isCompositeURI(uri)); + } + } + +} http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/e4decdc1/qpid-jms-client/src/test/resources/keystore ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/test/resources/keystore b/qpid-jms-client/src/test/resources/keystore new file mode 100644 index 0000000..9ee6adf Binary files /dev/null and b/qpid-jms-client/src/test/resources/keystore differ http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/e4decdc1/qpid-jms-client/src/test/resources/log4j.properties ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/test/resources/log4j.properties b/qpid-jms-client/src/test/resources/log4j.properties new file mode 100644 index 0000000..8df5a9e --- /dev/null +++ b/qpid-jms-client/src/test/resources/log4j.properties @@ -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. +## --------------------------------------------------------------------------- + +# +# The logging properties used during tests.. +# +log4j.rootLogger=INFO, out, stdout + +log4j.logger.org.apache.qpid.jms=DEBUG + +# Tune the TestPeer as needed for debugging. +log4j.logger.org.apache.qpid.jms.test.testpeer=TRACE + +# CONSOLE appender not used by default +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] - %-5p %-30.30c{1} - %m%n + +# File appender +log4j.appender.out=org.apache.log4j.FileAppender +log4j.appender.out.layout=org.apache.log4j.PatternLayout +log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] - %-5p %-30.30c{1} - %m%n +log4j.appender.out.file=target/activemq-test.log +log4j.appender.out.append=true http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/e4decdc1/qpid-jms-discovery/.gitignore ---------------------------------------------------------------------- diff --git a/qpid-jms-discovery/.gitignore b/qpid-jms-discovery/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/qpid-jms-discovery/.gitignore @@ -0,0 +1 @@ +/target http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/e4decdc1/qpid-jms-discovery/pom.xml ---------------------------------------------------------------------- diff --git a/qpid-jms-discovery/pom.xml b/qpid-jms-discovery/pom.xml new file mode 100644 index 0000000..58204cf --- /dev/null +++ b/qpid-jms-discovery/pom.xml @@ -0,0 +1,93 @@ +<?xml version="1.0"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.qpid</groupId> + <artifactId>qpid-jms-parent</artifactId> + <version>1.0-SNAPSHOT</version> + </parent> + + <artifactId>qpid-jms-discovery</artifactId> + <name>QpidJMS Discovery Library</name> + <description>The Broker Discovery module for QpidJMS</description> + <packaging>jar</packaging> + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <dependencies> + <!-- =================================== --> + <!-- Required Dependencies --> + <!-- =================================== --> + <dependency> + <groupId>org.apache.qpid</groupId> + <artifactId>qpid-jms-client</artifactId> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + + <!-- =================================== --> + <!-- Testing Dependencies --> + <!-- =================================== --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.apache.activemq</groupId> + <artifactId>activemq-broker</artifactId> + <version>${activemq-version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.activemq</groupId> + <artifactId>activemq-kahadb-store</artifactId> + <version>${activemq-version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.activemq</groupId> + <artifactId>activemq-amqp</artifactId> + <version>${activemq-version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.activemq</groupId> + <artifactId>activemq-jaas</artifactId> + <version>${activemq-version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.activemq</groupId> + <artifactId>activemq-spring</artifactId> + <version>${activemq-version}</version> + <scope>test</scope> + </dependency> + </dependencies> + +</project> http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/e4decdc1/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryAgent.java ---------------------------------------------------------------------- diff --git a/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryAgent.java b/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryAgent.java new file mode 100644 index 0000000..051f567 --- /dev/null +++ b/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryAgent.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.jms.provider.discovery; + +import java.io.IOException; + +/** + * Interface for all agents used to detect instances of remote peers on the network. + */ +public interface DiscoveryAgent { + + /** + * Sets the discovery listener + * + * @param listener + * the listener to notify on discovery events, or null to clear. + */ + void setDiscoveryListener(DiscoveryListener listener); + + /** + * Starts the agent after which new remote peers can start to be found. + * + * @throws IOException if an IO error occurs while starting the agent. + * @throws IllegalStateException if the agent is not properly configured. + */ + void start() throws IOException, IllegalStateException; + + /** + * Stops the agent after which no new remote peers will be found. This + * method should attempt to close any agent resources and if an error occurs + * it should handle it and not re-throw to the calling entity. + */ + void close(); + + /** + * Suspends the Agent which suppresses any new attempts to discover remote + * peers until the agent is resumed. If the service is not able to be suspended + * then this method should not throw an Exception, simply return as if successful. + */ + void suspend(); + + /** + * Resumes discovery by this agent if it was previously suspended. If the agent + * does not support being suspended or is closed this method should simply return + * without throwing any exceptions. + */ + void resume(); + +} http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/e4decdc1/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryAgentFactory.java ---------------------------------------------------------------------- diff --git a/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryAgentFactory.java b/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryAgentFactory.java new file mode 100644 index 0000000..f37745c --- /dev/null +++ b/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryAgentFactory.java @@ -0,0 +1,112 @@ +/** + * 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.jms.provider.discovery; + +import java.io.IOException; +import java.net.URI; + +import org.apache.qpid.jms.util.FactoryFinder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Factory used to find and create instances of DiscoveryAgent using the name + * of the desired agent to locate it's factory class definition file. + */ +public abstract class DiscoveryAgentFactory { + + private static final Logger LOG = LoggerFactory.getLogger(DiscoveryAgentFactory.class); + + private static final FactoryFinder<DiscoveryAgentFactory> AGENT_FACTORY_FINDER = + new FactoryFinder<DiscoveryAgentFactory>(DiscoveryAgentFactory.class, + "META-INF/services/org/apache/qpid/jms/provider/agents/"); + + /** + * Creates an instance of the given DiscoveryAgent and configures it using the + * properties set on the given remote broker URI. + * + * @param remoteURI + * The URI used to configure remote discovery. + * + * @return a new DiscoveryAgent instance. + * + * @throws Exception if an error occurs while creating the DiscoveryAgent instance. + */ + public abstract DiscoveryAgent createDiscoveryAgent(URI remoteURI) throws Exception; + + /** + * @return the name of this discovery agent, e.g. Multicast, Zeroconf, etc. + */ + public abstract String getName(); + + /** + * Static create method that performs the DiscoveryAgent search and handles the + * configuration and setup. + * + * @param remoteURI + * the URI used to configure the discovery mechanism. + * + * @return a new DiscoveryAgent instance that is ready for use. + * + * @throws Exception if an error occurs while creating the DiscoveryAgent instance. + */ + public static DiscoveryAgent createAgent(URI remoteURI) throws Exception { + DiscoveryAgent result = null; + + try { + DiscoveryAgentFactory factory = findAgentFactory(remoteURI); + result = factory.createDiscoveryAgent(remoteURI); + } catch (Exception ex) { + LOG.error("Failed to create DiscoveryAgent instance for: {}", remoteURI.getScheme()); + LOG.trace("Error: ", ex); + throw ex; + } + + return result; + } + + /** + * Searches for a DiscoveryAgentFactory by using the scheme from the given URI. + * + * The search first checks the local cache of discovery agent factories before moving on + * to search in the classpath. + * + * @param location + * The URI whose scheme will be used to locate a DiscoveryAgentFactory. + * + * @return a DiscoveryAgentFactory instance matching the URI's scheme. + * + * @throws IOException if an error occurs while locating the factory. + */ + protected static DiscoveryAgentFactory findAgentFactory(URI location) throws IOException { + String scheme = location.getScheme(); + if (scheme == null) { + throw new IOException("No Discovery Agent scheme specified: [" + location + "]"); + } + + DiscoveryAgentFactory factory = null; + if (factory == null) { + try { + factory = AGENT_FACTORY_FINDER.newInstance(scheme); + } catch (Throwable e) { + throw new IOException("Discovery Agent scheme NOT recognized: [" + scheme + "]", e); + } + } + + return factory; + } +} http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/e4decdc1/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryEvent.java ---------------------------------------------------------------------- diff --git a/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryEvent.java b/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryEvent.java new file mode 100644 index 0000000..0fc2f29 --- /dev/null +++ b/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryEvent.java @@ -0,0 +1,45 @@ +/** + * 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.jms.provider.discovery; + +/** + * Event class used to convey discovered remote peer information to the + * DiscoveryProvider. + */ +public class DiscoveryEvent { + + public enum EventType { + ALIVE, + SHUTDOWN + }; + + private final String peerUri; + private final EventType type; + + public DiscoveryEvent(String peerUri, EventType type) { + this.peerUri = peerUri; + this.type = type; + } + + public String getPeerUri() { + return peerUri; + } + + public EventType getType() { + return type; + } +} http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/e4decdc1/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryListener.java ---------------------------------------------------------------------- diff --git a/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryListener.java b/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryListener.java new file mode 100644 index 0000000..07e9895 --- /dev/null +++ b/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryListener.java @@ -0,0 +1,40 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.qpid.jms.provider.discovery; + +/** + * A listener of services being added or removed from a network + */ +public interface DiscoveryListener { + + /** + * Called when a DiscoveryAgent becomes aware of a new remote peer. + * + * @param event + * the event data which contains the peer address and optional name. + */ + void onServiceAdd(DiscoveryEvent event); + + /** + * Called when a DiscoveryAgent can no longer detect a previously known remote peer. + * + * @param event + * the event data which contains the peer address and optional name. + */ + void onServiceRemove(DiscoveryEvent event); + +} http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/e4decdc1/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryProvider.java ---------------------------------------------------------------------- diff --git a/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryProvider.java b/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryProvider.java new file mode 100644 index 0000000..1d3a0f0 --- /dev/null +++ b/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryProvider.java @@ -0,0 +1,139 @@ +/** + * 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.jms.provider.discovery; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.qpid.jms.provider.ProviderWrapper; +import org.apache.qpid.jms.provider.failover.FailoverProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * An AsyncProvider instance that wraps the FailoverProvider and listens for + * events about discovered remote peers using a configured DiscoveryAgent + * instance. + */ +public class DiscoveryProvider extends ProviderWrapper<FailoverProvider> implements DiscoveryListener { + + private static final Logger LOG = LoggerFactory.getLogger(DiscoveryProviderFactory.class); + + private final URI discoveryUri; + private DiscoveryAgent discoveryAgent; + private final ConcurrentHashMap<String, URI> serviceURIs = new ConcurrentHashMap<String, URI>(); + + /** + * Creates a new instance of the DiscoveryProvider. + * + * The Provider is created and initialized with the original URI used to create it, + * and an instance of a FailoverProcider which it will use to initiate and maintain + * connections to the discovered peers. + * + * @param discoveryUri + * @param next + */ + public DiscoveryProvider(URI discoveryUri, FailoverProvider next) { + super(next); + this.discoveryUri = discoveryUri; + } + + @Override + public void start() throws IOException, IllegalStateException { + if (this.discoveryAgent == null) { + throw new IllegalStateException("No DiscoveryAgent configured."); + } + + discoveryAgent.setDiscoveryListener(this); + discoveryAgent.start(); + + super.start(); + } + + @Override + public void close() { + discoveryAgent.close(); + super.close(); + } + + //------------------- Property Accessors ---------------------------------// + + /** + * @return the original URI used to configure this DiscoveryProvider. + */ + public URI getDiscoveryURI() { + return this.discoveryUri; + } + + /** + * @return the configured DiscoveryAgent instance used by this DiscoveryProvider. + */ + public DiscoveryAgent getDiscoveryAgent() { + return this.discoveryAgent; + } + + /** + * Sets the discovery agent used by this provider to locate remote peer instance. + * + * @param agent + * the agent to use to discover remote peers + */ + public void setDiscoveryAgent(DiscoveryAgent agent) { + this.discoveryAgent = agent; + } + + //------------------- Discovery Event Handlers ---------------------------// + + @Override + public void onServiceAdd(DiscoveryEvent event) { + String url = event.getPeerUri(); + if (url != null) { + try { + URI uri = new URI(url); + LOG.info("Adding new peer connection URL: {}", uri); + serviceURIs.put(event.getPeerUri(), uri); + next.add(uri); + } catch (URISyntaxException e) { + LOG.warn("Could not add remote URI: {} due to bad URI syntax: {}", url, e.getMessage()); + } + } + } + + @Override + public void onServiceRemove(DiscoveryEvent event) { + URI uri = serviceURIs.get(event.getPeerUri()); + if (uri != null) { + next.remove(uri); + } + } + + //------------------- Connection State Handlers --------------------------// + + @Override + public void onConnectionInterrupted(URI remoteURI) { + this.discoveryAgent.resume(); + super.onConnectionInterrupted(remoteURI); + } + + @Override + public void onConnectionRestored(URI remoteURI) { + this.discoveryAgent.suspend(); + super.onConnectionRestored(remoteURI); + } +} http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/e4decdc1/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryProviderFactory.java ---------------------------------------------------------------------- diff --git a/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryProviderFactory.java b/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryProviderFactory.java new file mode 100644 index 0000000..cd2ab5a --- /dev/null +++ b/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryProviderFactory.java @@ -0,0 +1,65 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.qpid.jms.provider.discovery; + +import java.net.URI; +import java.util.Map; + +import org.apache.qpid.jms.provider.Provider; +import org.apache.qpid.jms.provider.ProviderFactory; +import org.apache.qpid.jms.provider.failover.FailoverProvider; +import org.apache.qpid.jms.util.PropertyUtil; +import org.apache.qpid.jms.util.URISupport; +import org.apache.qpid.jms.util.URISupport.CompositeData; + +/** + * Factory for creating the Discovery Provider + */ +public class DiscoveryProviderFactory extends ProviderFactory { + + private static final String DISCOVERED_OPTION_PREFIX = "discovered."; + + @Override + public Provider createAsyncProvider(URI remoteURI) throws Exception { + + CompositeData composite = URISupport.parseComposite(remoteURI); + Map<String, String> options = composite.getParameters(); + + // Failover will apply the nested options to each URI while attempting to connect. + Map<String, String> nested = PropertyUtil.filterProperties(options, DISCOVERED_OPTION_PREFIX); + FailoverProvider failover = new FailoverProvider(nested); + PropertyUtil.setProperties(failover, options); + + // TODO - Revisit URI options setting and enhance the ProperyUtils to provide a + // means of setting some properties on a object and obtaining the leftovers + // so we can pass those along to the next until we consume them all or we + // have leftovers which implies a bad URI. + + DiscoveryProvider discovery = new DiscoveryProvider(remoteURI, failover); + PropertyUtil.setProperties(discovery, options); + + DiscoveryAgent agent = DiscoveryAgentFactory.createAgent(composite.getComponents()[0]); + discovery.setDiscoveryAgent(agent); + + return discovery; + } + + @Override + public String getName() { + return "Discovery"; + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
