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 }