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