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