001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.activemq.transport.amqp; 018 019import java.nio.ByteBuffer; 020import java.util.AbstractMap; 021import java.util.Map; 022 023import org.apache.activemq.command.ActiveMQDestination; 024import org.apache.qpid.proton.amqp.Binary; 025import org.apache.qpid.proton.amqp.DescribedType; 026import org.apache.qpid.proton.amqp.Symbol; 027import org.apache.qpid.proton.amqp.UnsignedLong; 028import org.apache.qpid.proton.amqp.transaction.Coordinator; 029import org.fusesource.hawtbuf.Buffer; 030 031/** 032 * Set of useful methods and definitions used in the AMQP protocol handling 033 */ 034public class AmqpSupport { 035 036 // Identification values used to locating JMS selector types. 037 public static final UnsignedLong JMS_SELECTOR_CODE = UnsignedLong.valueOf(0x0000468C00000004L); 038 public static final Symbol JMS_SELECTOR_NAME = Symbol.valueOf("apache.org:selector-filter:string"); 039 public static final Object[] JMS_SELECTOR_FILTER_IDS = new Object[] { JMS_SELECTOR_CODE, JMS_SELECTOR_NAME }; 040 public static final UnsignedLong NO_LOCAL_CODE = UnsignedLong.valueOf(0x0000468C00000003L); 041 public static final Symbol NO_LOCAL_NAME = Symbol.valueOf("apache.org:no-local-filter:list"); 042 public static final Object[] NO_LOCAL_FILTER_IDS = new Object[] { NO_LOCAL_CODE, NO_LOCAL_NAME }; 043 044 // Capabilities used to identify destination type in some requests. 045 public static final Symbol TEMP_QUEUE_CAPABILITY = Symbol.valueOf("temporary-queue"); 046 public static final Symbol TEMP_TOPIC_CAPABILITY = Symbol.valueOf("temporary-topic"); 047 public static final Symbol QUEUE_CAPABILITY = Symbol.valueOf("queue"); 048 public static final Symbol TOPIC_CAPABILITY = Symbol.valueOf("topic"); 049 050 // Symbols used to announce connection information to remote peer. 051 public static final Symbol INVALID_FIELD = Symbol.valueOf("invalid-field"); 052 public static final Symbol CONTAINER_ID = Symbol.valueOf("container-id"); 053 054 // Symbols used to announce connection information to remote peer. 055 public static final Symbol ANONYMOUS_RELAY = Symbol.valueOf("ANONYMOUS-RELAY"); 056 public static final Symbol DELAYED_DELIVERY = Symbol.valueOf("DELAYED_DELIVERY"); 057 public static final Symbol QUEUE_PREFIX = Symbol.valueOf("queue-prefix"); 058 public static final Symbol TOPIC_PREFIX = Symbol.valueOf("topic-prefix"); 059 public static final Symbol CONNECTION_OPEN_FAILED = Symbol.valueOf("amqp:connection-establishment-failed"); 060 public static final Symbol PRODUCT = Symbol.valueOf("product"); 061 public static final Symbol VERSION = Symbol.valueOf("version"); 062 public static final Symbol PLATFORM = Symbol.valueOf("platform"); 063 064 // Symbols used in configuration of newly opened links. 065 public static final Symbol COPY = Symbol.getSymbol("copy"); 066 067 // Lifetime policy symbols 068 public static final Symbol LIFETIME_POLICY = Symbol.valueOf("lifetime-policy"); 069 070 /** 071 * Search for a given Symbol in a given array of Symbol object. 072 * 073 * @param symbols 074 * the set of Symbols to search. 075 * @param key 076 * the value to try and find in the Symbol array. 077 * 078 * @return true if the key is found in the given Symbol array. 079 */ 080 public static boolean contains(Symbol[] symbols, Symbol key) { 081 if (symbols == null || symbols.length == 0) { 082 return false; 083 } 084 085 for (Symbol symbol : symbols) { 086 if (symbol.equals(key)) { 087 return true; 088 } 089 } 090 091 return false; 092 } 093 094 /** 095 * Search for a particular filter using a set of known indentification values 096 * in the Map of filters. 097 * 098 * @param filters 099 * The filters map that should be searched. 100 * @param filterIds 101 * The aliases for the target filter to be located. 102 * 103 * @return the filter if found in the mapping or null if not found. 104 */ 105 public static Map.Entry<Symbol, DescribedType> findFilter(Map<Symbol, Object> filters, Object[] filterIds) { 106 107 if (filterIds == null || filterIds.length == 0) { 108 throw new IllegalArgumentException("Invalid empty Filter Ids array passed: "); 109 } 110 111 if (filters == null || filters.isEmpty()) { 112 return null; 113 } 114 115 for (Map.Entry<Symbol, Object> filter : filters.entrySet()) { 116 if (filter.getValue() instanceof DescribedType) { 117 DescribedType describedType = ((DescribedType) filter.getValue()); 118 Object descriptor = describedType.getDescriptor(); 119 120 for (Object filterId : filterIds) { 121 if (descriptor.equals(filterId)) { 122 return new AbstractMap.SimpleImmutableEntry<Symbol, DescribedType>(filter.getKey(), describedType); 123 } 124 } 125 } 126 } 127 128 return null; 129 } 130 131 /** 132 * Conversion from Java ByteBuffer to a HawtBuf buffer. 133 * 134 * @param data 135 * the ByteBuffer instance to convert. 136 * 137 * @return a new HawtBuf buffer converted from the given ByteBuffer. 138 */ 139 public static Buffer toBuffer(ByteBuffer data) { 140 if (data == null) { 141 return null; 142 } 143 144 Buffer rc; 145 146 if (data.isDirect()) { 147 rc = new Buffer(data.remaining()); 148 data.get(rc.data); 149 } else { 150 rc = new Buffer(data); 151 data.position(data.position() + data.remaining()); 152 } 153 154 return rc; 155 } 156 157 /** 158 * Given a long value, convert it to a byte array for marshalling. 159 * 160 * @param value 161 * the value to convert. 162 * 163 * @return a new byte array that holds the big endian value of the long. 164 */ 165 public static byte[] toBytes(long value) { 166 Buffer buffer = new Buffer(8); 167 buffer.bigEndianEditor().writeLong(value); 168 return buffer.data; 169 } 170 171 /** 172 * Converts a Binary value to a long assuming that the contained value is 173 * stored in Big Endian encoding. 174 * 175 * @param value 176 * the Binary object whose payload is converted to a long. 177 * 178 * @return a long value constructed from the bytes of the Binary instance. 179 */ 180 public static long toLong(Binary value) { 181 Buffer buffer = new Buffer(value.getArray(), value.getArrayOffset(), value.getLength()); 182 return buffer.bigEndianEditor().readLong(); 183 } 184 185 /** 186 * Given an AMQP endpoint, deduce the appropriate ActiveMQDestination type and create 187 * a new instance. By default if the endpoint address does not carry the standard prefix 188 * value then we default to a Queue type destination. If the endpoint is null or is an 189 * AMQP Coordinator type endpoint this method returns null to indicate no destination 190 * can be mapped. 191 * 192 * @param endpoint 193 * the AMQP endpoint to construct an ActiveMQDestination from. 194 * 195 * @return a new ActiveMQDestination that best matches the address of the given endpoint 196 * 197 * @throws AmqpProtocolException if an error occurs while deducing the destination type. 198 */ 199 public static ActiveMQDestination createDestination(Object endpoint) throws AmqpProtocolException { 200 if (endpoint == null) { 201 return null; 202 } else if (endpoint instanceof Coordinator) { 203 return null; 204 } else if (endpoint instanceof org.apache.qpid.proton.amqp.messaging.Terminus) { 205 org.apache.qpid.proton.amqp.messaging.Terminus terminus = (org.apache.qpid.proton.amqp.messaging.Terminus) endpoint; 206 if (terminus.getAddress() == null || terminus.getAddress().length() == 0) { 207 if (terminus instanceof org.apache.qpid.proton.amqp.messaging.Source) { 208 throw new AmqpProtocolException("amqp:invalid-field", "source address not set"); 209 } else { 210 throw new AmqpProtocolException("amqp:invalid-field", "target address not set"); 211 } 212 } 213 214 return ActiveMQDestination.createDestination(terminus.getAddress(), ActiveMQDestination.QUEUE_TYPE); 215 } else { 216 throw new RuntimeException("Unexpected terminus type: " + endpoint); 217 } 218 } 219 220 /** 221 * Given an ActiveMQDestination return the proper Capability value for the concrete destination type. 222 * 223 * @param destination 224 * The ActiveMQDestination whose capability is being requested. 225 * 226 * @return a Symbol that matches the defined Capability value for the ActiveMQDestiantion. 227 */ 228 public static Symbol getDestinationTypeSymbol(ActiveMQDestination destination) { 229 if (destination.isQueue()) { 230 if (destination.isTemporary()) { 231 return TEMP_QUEUE_CAPABILITY; 232 } else { 233 return QUEUE_CAPABILITY; 234 } 235 } else { 236 if (destination.isTemporary()) { 237 return TEMP_TOPIC_CAPABILITY; 238 } else { 239 return TOPIC_CAPABILITY; 240 } 241 } 242 } 243}