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.List;
026 import java.util.Map;
027 import java.util.Properties;
028 import java.util.StringTokenizer;
029
030 import javax.jms.Destination;
031 import javax.jms.JMSException;
032 import javax.jms.Queue;
033 import javax.jms.TemporaryQueue;
034 import javax.jms.TemporaryTopic;
035 import javax.jms.Topic;
036
037 import org.apache.activemq.jndi.JNDIBaseStorable;
038 import org.apache.activemq.util.IntrospectionSupport;
039 import org.apache.activemq.util.URISupport;
040
041 /**
042 * @openwire:marshaller
043 * @version $Revision: 1.10 $
044 */
045 public abstract class ActiveMQDestination extends JNDIBaseStorable implements DataStructure, Destination, Externalizable, Comparable {
046
047 public static final String PATH_SEPERATOR = ".";
048 public static final char COMPOSITE_SEPERATOR = ',';
049
050 public static final byte QUEUE_TYPE = 0x01;
051 public static final byte TOPIC_TYPE = 0x02;
052 public static final byte TEMP_MASK = 0x04;
053 public static final byte TEMP_TOPIC_TYPE = TOPIC_TYPE | TEMP_MASK;
054 public static final byte TEMP_QUEUE_TYPE = QUEUE_TYPE | TEMP_MASK;
055
056 public static final String QUEUE_QUALIFIED_PREFIX = "queue://";
057 public static final String TOPIC_QUALIFIED_PREFIX = "topic://";
058 public static final String TEMP_QUEUE_QUALIFED_PREFIX = "temp-queue://";
059 public static final String TEMP_TOPIC_QUALIFED_PREFIX = "temp-topic://";
060
061 public static final String TEMP_DESTINATION_NAME_PREFIX = "ID:";
062
063 private static final long serialVersionUID = -3885260014960795889L;
064
065 protected String physicalName;
066
067 protected transient ActiveMQDestination[] compositeDestinations;
068 protected transient String[] destinationPaths;
069 protected transient boolean isPattern;
070 protected transient int hashValue;
071 protected Map<String, String> options;
072
073 public ActiveMQDestination() {
074 }
075
076 protected ActiveMQDestination(String name) {
077 setPhysicalName(name);
078 }
079
080 public ActiveMQDestination(ActiveMQDestination composites[]) {
081 setCompositeDestinations(composites);
082 }
083
084
085 // static helper methods for working with destinations
086 // -------------------------------------------------------------------------
087 public static ActiveMQDestination createDestination(String name, byte defaultType) {
088
089 if (name.startsWith(QUEUE_QUALIFIED_PREFIX)) {
090 return new ActiveMQQueue(name.substring(QUEUE_QUALIFIED_PREFIX.length()));
091 } else if (name.startsWith(TOPIC_QUALIFIED_PREFIX)) {
092 return new ActiveMQTopic(name.substring(TOPIC_QUALIFIED_PREFIX.length()));
093 } else if (name.startsWith(TEMP_QUEUE_QUALIFED_PREFIX)) {
094 return new ActiveMQTempQueue(name.substring(TEMP_QUEUE_QUALIFED_PREFIX.length()));
095 } else if (name.startsWith(TEMP_TOPIC_QUALIFED_PREFIX)) {
096 return new ActiveMQTempTopic(name.substring(TEMP_TOPIC_QUALIFED_PREFIX.length()));
097 }
098
099 switch (defaultType) {
100 case QUEUE_TYPE:
101 return new ActiveMQQueue(name);
102 case TOPIC_TYPE:
103 return new ActiveMQTopic(name);
104 case TEMP_QUEUE_TYPE:
105 return new ActiveMQTempQueue(name);
106 case TEMP_TOPIC_TYPE:
107 return new ActiveMQTempTopic(name);
108 default:
109 throw new IllegalArgumentException("Invalid default destination type: " + defaultType);
110 }
111 }
112
113 public static ActiveMQDestination transform(Destination dest) throws JMSException {
114 if (dest == null) {
115 return null;
116 }
117 if (dest instanceof ActiveMQDestination) {
118 return (ActiveMQDestination)dest;
119 }
120 if (dest instanceof TemporaryQueue) {
121 return new ActiveMQTempQueue(((TemporaryQueue)dest).getQueueName());
122 }
123 if (dest instanceof TemporaryTopic) {
124 return new ActiveMQTempTopic(((TemporaryTopic)dest).getTopicName());
125 }
126 if (dest instanceof Queue) {
127 return new ActiveMQQueue(((Queue)dest).getQueueName());
128 }
129 if (dest instanceof Topic) {
130 return new ActiveMQTopic(((Topic)dest).getTopicName());
131 }
132 throw new JMSException("Could not transform the destination into a ActiveMQ destination: " + dest);
133 }
134
135 public static int compare(ActiveMQDestination destination, ActiveMQDestination destination2) {
136 if (destination == destination2) {
137 return 0;
138 }
139 if (destination == null) {
140 return -1;
141 } else if (destination2 == null) {
142 return 1;
143 } else {
144 if (destination.isQueue() == destination2.isQueue()) {
145 return destination.getPhysicalName().compareTo(destination2.getPhysicalName());
146 } else {
147 return destination.isQueue() ? -1 : 1;
148 }
149 }
150 }
151
152 public int compareTo(Object that) {
153 if (that instanceof ActiveMQDestination) {
154 return compare(this, (ActiveMQDestination)that);
155 }
156 if (that == null) {
157 return 1;
158 } else {
159 return getClass().getName().compareTo(that.getClass().getName());
160 }
161 }
162
163 public boolean isComposite() {
164 return compositeDestinations != null;
165 }
166
167 public ActiveMQDestination[] getCompositeDestinations() {
168 return compositeDestinations;
169 }
170
171 public void setCompositeDestinations(ActiveMQDestination[] destinations) {
172 this.compositeDestinations = destinations;
173 this.destinationPaths = null;
174 this.hashValue = 0;
175 this.isPattern = false;
176
177 StringBuffer sb = new StringBuffer();
178 for (int i = 0; i < destinations.length; i++) {
179 if (i != 0) {
180 sb.append(COMPOSITE_SEPERATOR);
181 }
182 if (getDestinationType() == destinations[i].getDestinationType()) {
183 sb.append(destinations[i].getPhysicalName());
184 } else {
185 sb.append(destinations[i].getQualifiedName());
186 }
187 }
188 physicalName = sb.toString();
189 }
190
191 public String getQualifiedName() {
192 if (isComposite()) {
193 return physicalName;
194 }
195 return getQualifiedPrefix() + physicalName;
196 }
197
198 protected abstract String getQualifiedPrefix();
199
200 /**
201 * @openwire:property version=1
202 */
203 public String getPhysicalName() {
204 return physicalName;
205 }
206
207 public void setPhysicalName(String physicalName) {
208 final int len = physicalName.length();
209 // options offset
210 int p = -1;
211 boolean composite = false;
212 for (int i = 0; i < len; i++) {
213 char c = physicalName.charAt(i);
214 if (c == '?') {
215 p = i;
216 break;
217 }
218 if (c == COMPOSITE_SEPERATOR) {
219 // won't be wild card
220 isPattern = false;
221 composite = true;
222 } else if (!composite && (c == '*' || c == '>')) {
223 isPattern = true;
224 }
225 }
226 // Strip off any options
227 if (p >= 0) {
228 String optstring = physicalName.substring(p + 1);
229 physicalName = physicalName.substring(0, p);
230 try {
231 options = URISupport.parseQuery(optstring);
232 } catch (URISyntaxException e) {
233 throw new IllegalArgumentException("Invalid destination name: " + physicalName + ", it's options are not encoded properly: " + e);
234 }
235 }
236 this.physicalName = physicalName;
237 this.destinationPaths = null;
238 this.hashValue = 0;
239 if (composite) {
240 // Check to see if it is a composite.
241 List<String> l = new ArrayList<String>();
242 StringTokenizer iter = new StringTokenizer(physicalName, "" + COMPOSITE_SEPERATOR);
243 while (iter.hasMoreTokens()) {
244 String name = iter.nextToken().trim();
245 if (name.length() == 0) {
246 continue;
247 }
248 l.add(name);
249 }
250 if (l.size() > 1) {
251 compositeDestinations = new ActiveMQDestination[l.size()];
252 int counter = 0;
253 for (String dest : l) {
254 compositeDestinations[counter++] = createDestination(dest);
255 }
256 }
257 }
258 }
259
260 public ActiveMQDestination createDestination(String name) {
261 return createDestination(name, getDestinationType());
262 }
263
264 public String[] getDestinationPaths() {
265
266 if (destinationPaths != null) {
267 return destinationPaths;
268 }
269
270 List<String> l = new ArrayList<String>();
271 StringTokenizer iter = new StringTokenizer(physicalName, PATH_SEPERATOR);
272 while (iter.hasMoreTokens()) {
273 String name = iter.nextToken().trim();
274 if (name.length() == 0) {
275 continue;
276 }
277 l.add(name);
278 }
279
280 destinationPaths = new String[l.size()];
281 l.toArray(destinationPaths);
282 return destinationPaths;
283 }
284
285 public abstract byte getDestinationType();
286
287 public boolean isQueue() {
288 return false;
289 }
290
291 public boolean isTopic() {
292 return false;
293 }
294
295 public boolean isTemporary() {
296 return false;
297 }
298
299 public boolean equals(Object o) {
300 if (this == o) {
301 return true;
302 }
303 if (o == null || getClass() != o.getClass()) {
304 return false;
305 }
306
307 ActiveMQDestination d = (ActiveMQDestination)o;
308 return physicalName.equals(d.physicalName);
309 }
310
311 public int hashCode() {
312 if (hashValue == 0) {
313 hashValue = physicalName.hashCode();
314 }
315 return hashValue;
316 }
317
318 public String toString() {
319 return getQualifiedName();
320 }
321
322 public void writeExternal(ObjectOutput out) throws IOException {
323 out.writeUTF(this.getPhysicalName());
324 out.writeObject(options);
325 }
326
327 @SuppressWarnings("unchecked")
328 public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
329 this.setPhysicalName(in.readUTF());
330 this.options = (Map<String, String>)in.readObject();
331 }
332
333 public String getDestinationTypeAsString() {
334 switch (getDestinationType()) {
335 case QUEUE_TYPE:
336 return "Queue";
337 case TOPIC_TYPE:
338 return "Topic";
339 case TEMP_QUEUE_TYPE:
340 return "TempQueue";
341 case TEMP_TOPIC_TYPE:
342 return "TempTopic";
343 default:
344 throw new IllegalArgumentException("Invalid destination type: " + getDestinationType());
345 }
346 }
347
348 public Map<String, String> getOptions() {
349 return options;
350 }
351
352 public boolean isMarshallAware() {
353 return false;
354 }
355
356 public void buildFromProperties(Properties properties) {
357 if (properties == null) {
358 properties = new Properties();
359 }
360
361 IntrospectionSupport.setProperties(this, properties);
362 }
363
364 public void populateProperties(Properties props) {
365 props.setProperty("physicalName", getPhysicalName());
366 }
367
368 public boolean isPattern() {
369 return isPattern;
370 }
371 }