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