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 */ 017 package org.apache.activemq.command; 018 019 import java.io.Externalizable; 020 import java.io.IOException; 021 import java.io.ObjectInput; 022 import java.io.ObjectOutput; 023 import java.net.URISyntaxException; 024 import java.util.ArrayList; 025 import java.util.List; 026 import java.util.Map; 027 import java.util.Properties; 028 import java.util.StringTokenizer; 029 030 import javax.jms.Destination; 031 import javax.jms.JMSException; 032 import javax.jms.Queue; 033 import javax.jms.TemporaryQueue; 034 import javax.jms.TemporaryTopic; 035 import javax.jms.Topic; 036 037 import org.apache.activemq.jndi.JNDIBaseStorable; 038 import org.apache.activemq.util.IntrospectionSupport; 039 import org.apache.activemq.util.URISupport; 040 041 /** 042 * @openwire:marshaller 043 * @version $Revision: 1.10 $ 044 */ 045 public abstract class ActiveMQDestination extends JNDIBaseStorable implements DataStructure, Destination, Externalizable, Comparable { 046 047 public static final String PATH_SEPERATOR = "."; 048 public static final char COMPOSITE_SEPERATOR = ','; 049 050 public static final byte QUEUE_TYPE = 0x01; 051 public static final byte TOPIC_TYPE = 0x02; 052 public static final byte TEMP_MASK = 0x04; 053 public static final byte TEMP_TOPIC_TYPE = TOPIC_TYPE | TEMP_MASK; 054 public static final byte TEMP_QUEUE_TYPE = QUEUE_TYPE | TEMP_MASK; 055 056 public static final String QUEUE_QUALIFIED_PREFIX = "queue://"; 057 public static final String TOPIC_QUALIFIED_PREFIX = "topic://"; 058 public static final String TEMP_QUEUE_QUALIFED_PREFIX = "temp-queue://"; 059 public static final String TEMP_TOPIC_QUALIFED_PREFIX = "temp-topic://"; 060 061 public static final String TEMP_DESTINATION_NAME_PREFIX = "ID:"; 062 063 private static final long serialVersionUID = -3885260014960795889L; 064 065 protected String physicalName; 066 067 protected transient ActiveMQDestination[] compositeDestinations; 068 protected transient String[] destinationPaths; 069 protected transient boolean isPattern; 070 protected transient int hashValue; 071 protected Map<String, String> options; 072 073 public ActiveMQDestination() { 074 } 075 076 protected ActiveMQDestination(String name) { 077 setPhysicalName(name); 078 } 079 080 public ActiveMQDestination(ActiveMQDestination composites[]) { 081 setCompositeDestinations(composites); 082 } 083 084 085 // static helper methods for working with destinations 086 // ------------------------------------------------------------------------- 087 public static ActiveMQDestination createDestination(String name, byte defaultType) { 088 089 if (name.startsWith(QUEUE_QUALIFIED_PREFIX)) { 090 return new ActiveMQQueue(name.substring(QUEUE_QUALIFIED_PREFIX.length())); 091 } else if (name.startsWith(TOPIC_QUALIFIED_PREFIX)) { 092 return new ActiveMQTopic(name.substring(TOPIC_QUALIFIED_PREFIX.length())); 093 } else if (name.startsWith(TEMP_QUEUE_QUALIFED_PREFIX)) { 094 return new ActiveMQTempQueue(name.substring(TEMP_QUEUE_QUALIFED_PREFIX.length())); 095 } else if (name.startsWith(TEMP_TOPIC_QUALIFED_PREFIX)) { 096 return new ActiveMQTempTopic(name.substring(TEMP_TOPIC_QUALIFED_PREFIX.length())); 097 } 098 099 switch (defaultType) { 100 case QUEUE_TYPE: 101 return new ActiveMQQueue(name); 102 case TOPIC_TYPE: 103 return new ActiveMQTopic(name); 104 case TEMP_QUEUE_TYPE: 105 return new ActiveMQTempQueue(name); 106 case TEMP_TOPIC_TYPE: 107 return new ActiveMQTempTopic(name); 108 default: 109 throw new IllegalArgumentException("Invalid default destination type: " + defaultType); 110 } 111 } 112 113 public static ActiveMQDestination transform(Destination dest) throws JMSException { 114 if (dest == null) { 115 return null; 116 } 117 if (dest instanceof ActiveMQDestination) { 118 return (ActiveMQDestination)dest; 119 } 120 121 if (dest instanceof Queue && dest instanceof Topic) { 122 String queueName = ((Queue) dest).getQueueName(); 123 String topicName = ((Topic) dest).getTopicName(); 124 if (queueName != null && topicName == null) { 125 return new ActiveMQQueue(queueName); 126 } else if (queueName == null && topicName != null) { 127 return new ActiveMQTopic(topicName); 128 } 129 throw new JMSException("Could no disambiguate on queue|Topic-name totransform pollymorphic destination into a ActiveMQ destination: " + dest); 130 } 131 if (dest instanceof TemporaryQueue) { 132 return new ActiveMQTempQueue(((TemporaryQueue)dest).getQueueName()); 133 } 134 if (dest instanceof TemporaryTopic) { 135 return new ActiveMQTempTopic(((TemporaryTopic)dest).getTopicName()); 136 } 137 if (dest instanceof Queue) { 138 return new ActiveMQQueue(((Queue)dest).getQueueName()); 139 } 140 if (dest instanceof Topic) { 141 return new ActiveMQTopic(((Topic)dest).getTopicName()); 142 } 143 throw new JMSException("Could not transform the destination into a ActiveMQ destination: " + dest); 144 } 145 146 public static int compare(ActiveMQDestination destination, ActiveMQDestination destination2) { 147 if (destination == destination2) { 148 return 0; 149 } 150 if (destination == null) { 151 return -1; 152 } else if (destination2 == null) { 153 return 1; 154 } else { 155 if (destination.isQueue() == destination2.isQueue()) { 156 return destination.getPhysicalName().compareTo(destination2.getPhysicalName()); 157 } else { 158 return destination.isQueue() ? -1 : 1; 159 } 160 } 161 } 162 163 public int compareTo(Object that) { 164 if (that instanceof ActiveMQDestination) { 165 return compare(this, (ActiveMQDestination)that); 166 } 167 if (that == null) { 168 return 1; 169 } else { 170 return getClass().getName().compareTo(that.getClass().getName()); 171 } 172 } 173 174 public boolean isComposite() { 175 return compositeDestinations != null; 176 } 177 178 public ActiveMQDestination[] getCompositeDestinations() { 179 return compositeDestinations; 180 } 181 182 public void setCompositeDestinations(ActiveMQDestination[] destinations) { 183 this.compositeDestinations = destinations; 184 this.destinationPaths = null; 185 this.hashValue = 0; 186 this.isPattern = false; 187 188 StringBuffer sb = new StringBuffer(); 189 for (int i = 0; i < destinations.length; i++) { 190 if (i != 0) { 191 sb.append(COMPOSITE_SEPERATOR); 192 } 193 if (getDestinationType() == destinations[i].getDestinationType()) { 194 sb.append(destinations[i].getPhysicalName()); 195 } else { 196 sb.append(destinations[i].getQualifiedName()); 197 } 198 } 199 physicalName = sb.toString(); 200 } 201 202 public String getQualifiedName() { 203 if (isComposite()) { 204 return physicalName; 205 } 206 return getQualifiedPrefix() + physicalName; 207 } 208 209 protected abstract String getQualifiedPrefix(); 210 211 /** 212 * @openwire:property version=1 213 */ 214 public String getPhysicalName() { 215 return physicalName; 216 } 217 218 public void setPhysicalName(String physicalName) { 219 final int len = physicalName.length(); 220 // options offset 221 int p = -1; 222 boolean composite = false; 223 for (int i = 0; i < len; i++) { 224 char c = physicalName.charAt(i); 225 if (c == '?') { 226 p = i; 227 break; 228 } 229 if (c == COMPOSITE_SEPERATOR) { 230 // won't be wild card 231 isPattern = false; 232 composite = true; 233 } else if (!composite && (c == '*' || c == '>')) { 234 isPattern = true; 235 } 236 } 237 // Strip off any options 238 if (p >= 0) { 239 String optstring = physicalName.substring(p + 1); 240 physicalName = physicalName.substring(0, p); 241 try { 242 options = URISupport.parseQuery(optstring); 243 } catch (URISyntaxException e) { 244 throw new IllegalArgumentException("Invalid destination name: " + physicalName + ", it's options are not encoded properly: " + e); 245 } 246 } 247 this.physicalName = physicalName; 248 this.destinationPaths = null; 249 this.hashValue = 0; 250 if (composite) { 251 // Check to see if it is a composite. 252 List<String> l = new ArrayList<String>(); 253 StringTokenizer iter = new StringTokenizer(physicalName, "" + COMPOSITE_SEPERATOR); 254 while (iter.hasMoreTokens()) { 255 String name = iter.nextToken().trim(); 256 if (name.length() == 0) { 257 continue; 258 } 259 l.add(name); 260 } 261 if (l.size() > 1) { 262 compositeDestinations = new ActiveMQDestination[l.size()]; 263 int counter = 0; 264 for (String dest : l) { 265 compositeDestinations[counter++] = createDestination(dest); 266 } 267 } 268 } 269 } 270 271 public ActiveMQDestination createDestination(String name) { 272 return createDestination(name, getDestinationType()); 273 } 274 275 public String[] getDestinationPaths() { 276 277 if (destinationPaths != null) { 278 return destinationPaths; 279 } 280 281 List<String> l = new ArrayList<String>(); 282 StringTokenizer iter = new StringTokenizer(physicalName, PATH_SEPERATOR); 283 while (iter.hasMoreTokens()) { 284 String name = iter.nextToken().trim(); 285 if (name.length() == 0) { 286 continue; 287 } 288 l.add(name); 289 } 290 291 destinationPaths = new String[l.size()]; 292 l.toArray(destinationPaths); 293 return destinationPaths; 294 } 295 296 public abstract byte getDestinationType(); 297 298 public boolean isQueue() { 299 return false; 300 } 301 302 public boolean isTopic() { 303 return false; 304 } 305 306 public boolean isTemporary() { 307 return false; 308 } 309 310 public boolean equals(Object o) { 311 if (this == o) { 312 return true; 313 } 314 if (o == null || getClass() != o.getClass()) { 315 return false; 316 } 317 318 ActiveMQDestination d = (ActiveMQDestination)o; 319 return physicalName.equals(d.physicalName); 320 } 321 322 public int hashCode() { 323 if (hashValue == 0) { 324 hashValue = physicalName.hashCode(); 325 } 326 return hashValue; 327 } 328 329 public String toString() { 330 return getQualifiedName(); 331 } 332 333 public void writeExternal(ObjectOutput out) throws IOException { 334 out.writeUTF(this.getPhysicalName()); 335 out.writeObject(options); 336 } 337 338 @SuppressWarnings("unchecked") 339 public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { 340 this.setPhysicalName(in.readUTF()); 341 this.options = (Map<String, String>)in.readObject(); 342 } 343 344 public String getDestinationTypeAsString() { 345 switch (getDestinationType()) { 346 case QUEUE_TYPE: 347 return "Queue"; 348 case TOPIC_TYPE: 349 return "Topic"; 350 case TEMP_QUEUE_TYPE: 351 return "TempQueue"; 352 case TEMP_TOPIC_TYPE: 353 return "TempTopic"; 354 default: 355 throw new IllegalArgumentException("Invalid destination type: " + getDestinationType()); 356 } 357 } 358 359 public Map<String, String> getOptions() { 360 return options; 361 } 362 363 public boolean isMarshallAware() { 364 return false; 365 } 366 367 public void buildFromProperties(Properties properties) { 368 if (properties == null) { 369 properties = new Properties(); 370 } 371 372 IntrospectionSupport.setProperties(this, properties); 373 } 374 375 public void populateProperties(Properties props) { 376 props.setProperty("physicalName", getPhysicalName()); 377 } 378 379 public boolean isPattern() { 380 return isPattern; 381 } 382 }