1 /*** 2 * 3 * Copyright 2004 Protique Ltd 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 **/ 18 19 package org.codehaus.activemq.message; 20 21 import org.codehaus.activemq.filter.DestinationFilter; 22 import org.codehaus.activemq.jndi.JNDIBaseStorable; 23 24 import javax.jms.Destination; 25 import javax.jms.JMSException; 26 import javax.jms.Queue; 27 import javax.jms.TemporaryQueue; 28 import javax.jms.TemporaryTopic; 29 import javax.jms.Topic; 30 import java.io.DataInput; 31 import java.io.DataOutput; 32 import java.io.IOException; 33 import java.io.Serializable; 34 import java.util.Properties; 35 36 /*** 37 * A <CODE>Destination</CODE> object encapsulates a provider-specific 38 * address. 39 * The JMS API does not define a standard address syntax. Although a standard 40 * address syntax was considered, it was decided that the differences in 41 * address semantics between existing message-oriented middleware (MOM) 42 * products were too wide to bridge with a single syntax. 43 * <p/> 44 * <P>Since <CODE>Destination</CODE> is an administered object, it may 45 * contain 46 * provider-specific configuration information in addition to its address. 47 * <p/> 48 * <P>The JMS API also supports a client's use of provider-specific address 49 * names. 50 * <p/> 51 * <P><CODE>Destination</CODE> objects support concurrent use. 52 * <p/> 53 * <P>A <CODE>Destination</CODE> object is a JMS administered object. 54 * <p/> 55 * <P>JMS administered objects are objects containing configuration 56 * information that are created by an administrator and later used by 57 * JMS clients. They make it practical to administer the JMS API in the 58 * enterprise. 59 * <p/> 60 * <P>Although the interfaces for administered objects do not explicitly 61 * depend on the Java Naming and Directory Interface (JNDI) API, the JMS API 62 * establishes the convention that JMS clients find administered objects by 63 * looking them up in a JNDI namespace. 64 * <p/> 65 * <P>An administrator can place an administered object anywhere in a 66 * namespace. The JMS API does not define a naming policy. 67 * <p/> 68 * <P>It is expected that JMS providers will provide the tools an 69 * administrator needs to create and configure administered objects in a 70 * JNDI namespace. JMS provider implementations of administered objects 71 * should implement the <CODE>javax.naming.Referenceable</CODE> and 72 * <CODE>java.io.Serializable</CODE> interfaces so that they can be stored in 73 * all JNDI naming contexts. In addition, it is recommended that these 74 * implementations follow the JavaBeans<SUP><FONT SIZE="-2">TM</FONT></SUP> 75 * design patterns. 76 * <p/> 77 * <P>This strategy provides several benefits: 78 * <p/> 79 * <UL> 80 * <LI>It hides provider-specific details from JMS clients. 81 * <LI>It abstracts JMS administrative information into objects in the Java 82 * programming language ("Java objects") 83 * that are easily organized and administered from a common 84 * management console. 85 * <LI>Since there will be JNDI providers for all popular naming 86 * services, JMS providers can deliver one implementation 87 * of administered objects that will run everywhere. 88 * </UL> 89 * <p/> 90 * <P>An administered object should not hold on to any remote resources. 91 * Its lookup should not use remote resources other than those used by the 92 * JNDI API itself. 93 * <p/> 94 * <P>Clients should think of administered objects as local Java objects. 95 * Looking them up should not have any hidden side effects or use surprising 96 * amounts of local resources. 97 */ 98 99 public abstract class ActiveMQDestination extends JNDIBaseStorable implements Destination, Comparable, Serializable { 100 101 private static final int NULL_DESTINATION = 10; 102 private static final String TEMP_PREFIX = "{TD{"; 103 private static final String TEMP_POSTFIX = "}TD}"; 104 105 /*** 106 * A helper method to return a descriptive string for the topic or queue 107 * 108 * @return a descriptive string for this queue or topic 109 */ 110 public static String inspect(Destination destination) { 111 if (destination instanceof Topic) { 112 return "Topic(" + destination.toString() + ")"; 113 } 114 else { 115 return "Queue(" + destination.toString() + ")"; 116 } 117 } 118 119 /*** 120 * Topic Destination object 121 */ 122 public static final int ACTIVEMQ_TOPIC = 1; 123 /*** 124 * Temporary Topic Destination object 125 */ 126 public static final int ACTIVEMQ_TEMPORARY_TOPIC = 2; 127 128 /*** 129 * Queue Destination object 130 */ 131 public static final int ACTIVEMQ_QUEUE = 3; 132 /*** 133 * Temporary Queue Destination object 134 */ 135 public static final int ACTIVEMQ_TEMPORARY_QUEUE = 4; 136 137 138 private String physicalName; 139 140 /*** 141 * The Default Constructor 142 */ 143 protected ActiveMQDestination() { 144 } 145 146 /*** 147 * Construct the ActiveMQDestination with a defined physical name; 148 * 149 * @param name 150 */ 151 152 protected ActiveMQDestination(String name) { 153 this.physicalName = name; 154 } 155 156 /*** 157 * @param o object to compare 158 * @return 1 if this > o else 0 if they are equal or -1 if this < o 159 */ 160 public int compareTo(Object o) { 161 if (o instanceof ActiveMQDestination) { 162 return compareTo((ActiveMQDestination) o); 163 } 164 return -1; 165 } 166 167 /*** 168 * Lets sort by name first then lets sort topics greater than queues 169 * 170 * @param that another destination to compare against 171 * @return 1 if this > that else 0 if they are equal or -1 if this < that 172 */ 173 public int compareTo(ActiveMQDestination that) { 174 int answer = 0; 175 if (physicalName != that.physicalName) { 176 if (physicalName == null) { 177 return -1; 178 } 179 else if (that.physicalName == null) { 180 return 1; 181 } 182 answer = physicalName.compareTo(that.physicalName); 183 } 184 if (answer == 0) { 185 if (isTopic()) { 186 if (that.isQueue()) { 187 return 1; 188 } 189 } 190 else { 191 if (that.isTopic()) { 192 return -1; 193 } 194 } 195 } 196 return answer; 197 } 198 199 200 /*** 201 * @return Returns the Destination type 202 */ 203 204 public abstract int getDestinationType(); 205 206 207 /*** 208 * @return Returns the physicalName. 209 */ 210 public String getPhysicalName() { 211 return this.physicalName; 212 } 213 214 /*** 215 * @param newPhysicalName The physicalName to set. 216 */ 217 public void setPhysicalName(String newPhysicalName) { 218 this.physicalName = newPhysicalName; 219 } 220 221 /*** 222 * Set the properties that will represent the instance in JNDI 223 * 224 * @param props 225 */ 226 protected void buildFromProperties(Properties props) { 227 this.physicalName = props.getProperty("physicalName", this.physicalName); 228 229 } 230 231 /*** 232 * Initialize the instance from properties stored in JNDI 233 * 234 * @param props 235 */ 236 237 protected void populateProperties(Properties props) { 238 props.put("physicalName", this.physicalName); 239 } 240 241 /*** 242 * Returns true if a temporary Destination 243 * 244 * @return true/false 245 */ 246 247 public boolean isTemporary() { 248 return getDestinationType() == ACTIVEMQ_TEMPORARY_TOPIC || 249 getDestinationType() == ACTIVEMQ_TEMPORARY_QUEUE; 250 } 251 252 /*** 253 * Returns true if a Topic Destination 254 * 255 * @return true/false 256 */ 257 258 public boolean isTopic() { 259 return getDestinationType() == ACTIVEMQ_TOPIC || 260 getDestinationType() == ACTIVEMQ_TEMPORARY_TOPIC; 261 } 262 263 /*** 264 * Returns true if a Queue Destination 265 * 266 * @return true/false 267 */ 268 public boolean isQueue() { 269 return !isTopic(); 270 } 271 272 /*** 273 * @return string representation of this instance 274 */ 275 276 public String toString() { 277 return this.physicalName; 278 } 279 280 /*** 281 * @return hashCode for this instance 282 */ 283 284 public int hashCode() { 285 int answer = 0xcafebabe; 286 287 if (this.physicalName != null) { 288 answer = physicalName.hashCode(); 289 } 290 if (isTopic()) { 291 answer ^= 0xfabfab; 292 } 293 return answer; 294 } 295 296 /*** 297 * if the object passed in is equivalent, return true 298 * 299 * @param obj the object to compare 300 * @return true if this instance and obj are equivalent 301 */ 302 303 public boolean equals(Object obj) { 304 boolean result = this == obj; 305 if (!result && obj != null && obj instanceof ActiveMQDestination) { 306 ActiveMQDestination other = (ActiveMQDestination) obj; 307 result = this.getDestinationType() == other.getDestinationType() && 308 this.physicalName.equals(other.physicalName); 309 } 310 return result; 311 } 312 313 /*** 314 * Write an ActiveMQDestination to a Stream 315 * 316 * @param destination 317 * @param dataOut 318 * @throws IOException 319 */ 320 321 public static void writeToStream(ActiveMQDestination destination, DataOutput dataOut) throws IOException { 322 if (destination != null) { 323 dataOut.write(destination.getDestinationType()); 324 dataOut.writeUTF(destination == null ? "" : destination.getPhysicalName()); 325 } 326 else { 327 dataOut.write(NULL_DESTINATION); 328 } 329 } 330 331 /*** 332 * Read an ActiveMQDestination from a Stream 333 * 334 * @param dataIn 335 * @return the ActiveMQDestination 336 * @throws IOException 337 */ 338 339 public static ActiveMQDestination readFromStream(DataInput dataIn) throws IOException { 340 341 int type = dataIn.readByte(); 342 if (type == NULL_DESTINATION) { 343 return null; 344 } 345 ActiveMQDestination result = null; 346 if (type == ACTIVEMQ_TOPIC) { 347 result = new ActiveMQTopic(); 348 } 349 else if (type == ACTIVEMQ_TEMPORARY_TOPIC) { 350 result = new ActiveMQTemporaryTopic(); 351 } 352 else if (type == ACTIVEMQ_QUEUE) { 353 result = new ActiveMQQueue(); 354 } 355 else { 356 result = new ActiveMQTemporaryQueue(); 357 } 358 result.setPhysicalName(dataIn.readUTF()); 359 return result; 360 361 } 362 363 /*** 364 * Create a temporary name from the clientId 365 * 366 * @param clientId 367 * @return 368 */ 369 public static String createTemporaryName(String clientId) { 370 return TEMP_PREFIX + clientId + TEMP_POSTFIX; 371 } 372 373 /*** 374 * From a temporary destination find the clientId of the Connection that created it 375 * 376 * @param destination 377 * @return the clientId or null if not a temporary destination 378 */ 379 public static String getClientId(ActiveMQDestination destination) { 380 String answer = null; 381 if (destination != null && destination.isTemporary()) { 382 String name = destination.getPhysicalName(); 383 int start = name.indexOf(TEMP_PREFIX); 384 if (start >= 0) { 385 start += TEMP_PREFIX.length(); 386 int stop = name.lastIndexOf(TEMP_POSTFIX); 387 if (stop > start && stop < name.length()) { 388 answer = name.substring(start, stop); 389 } 390 } 391 } 392 return answer; 393 } 394 395 /*** 396 * @return true if the destination matches multiple possible destinations 397 */ 398 public boolean isWildcard() { 399 if (physicalName != null) { 400 return physicalName.indexOf(DestinationFilter.ANY_CHILD) >= 0 401 || physicalName.indexOf(DestinationFilter.ANY_DESCENDENT) >= 0; 402 } 403 return false; 404 } 405 406 /*** 407 * @param destination 408 * @return @throws JMSException 409 * @throws javax.jms.JMSException 410 */ 411 public static ActiveMQDestination transformDestination(Destination destination) throws JMSException { 412 ActiveMQDestination result = null; 413 if (destination != null) { 414 if (destination instanceof ActiveMQDestination) { 415 result = (ActiveMQDestination) destination; 416 } 417 else { 418 if (destination instanceof TemporaryQueue) { 419 result = new ActiveMQTemporaryQueue(((Queue) destination).getQueueName()); 420 } 421 else if (destination instanceof TemporaryTopic) { 422 result = new ActiveMQTemporaryTopic(((Topic) destination).getTopicName()); 423 } 424 else if (destination instanceof Queue) { 425 result = new ActiveMQTemporaryQueue(((Queue) destination).getQueueName()); 426 } 427 else if (destination instanceof Topic) { 428 result = new ActiveMQTemporaryTopic(((Topic) destination).getTopicName()); 429 } 430 } 431 } 432 return result; 433 } 434 }