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