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 }