AMQDestination.java
001 /*
002  *
003  * Licensed to the Apache Software Foundation (ASF) under one
004  * or more contributor license agreements.  See the NOTICE file
005  * distributed with this work for additional information
006  * regarding copyright ownership.  The ASF licenses this file
007  * to you under the Apache License, Version 2.0 (the
008  * "License"); you may not use this file except in compliance
009  * with the License.  You may obtain a copy of the License at
010  *
011  *   http://www.apache.org/licenses/LICENSE-2.0
012  *
013  * Unless required by applicable law or agreed to in writing,
014  * software distributed under the License is distributed on an
015  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016  * KIND, either express or implied.  See the License for the
017  * specific language governing permissions and limitations
018  * under the License.
019  *
020  */
021 package org.apache.qpid.client;
022 
023 import java.net.URISyntaxException;
024 
025 import javax.jms.Destination;
026 import javax.naming.NamingException;
027 import javax.naming.Reference;
028 import javax.naming.Referenceable;
029 import javax.naming.StringRefAddr;
030 
031 import org.apache.qpid.exchange.ExchangeDefaults;
032 import org.apache.qpid.framing.AMQShortString;
033 import org.apache.qpid.url.AMQBindingURL;
034 import org.apache.qpid.url.BindingURL;
035 import org.apache.qpid.url.URLHelper;
036 
037 
038 public abstract class AMQDestination implements Destination, Referenceable
039 {
040     protected final AMQShortString _exchangeName;
041 
042     protected final AMQShortString _exchangeClass;
043 
044     protected final boolean _isDurable;
045 
046     protected final boolean _isExclusive;
047 
048     protected final boolean _isAutoDelete;
049 
050     private AMQShortString _queueName;
051 
052     private AMQShortString _routingKey;
053 
054     private AMQShortString[] _bindingKeys;
055 
056     private String _url;
057     private AMQShortString _urlAsShortString;
058 
059     private boolean _checkedForQueueBinding;
060 
061     private boolean _exchangeExistsChecked;
062 
063     private byte[] _byteEncoding;
064     private static final int IS_DURABLE_MASK = 0x1;
065     private static final int IS_EXCLUSIVE_MASK = 0x2;
066     private static final int IS_AUTODELETE_MASK = 0x4;
067 
068     public static final int QUEUE_TYPE = 1;
069     public static final int TOPIC_TYPE = 2;
070     public static final int UNKNOWN_TYPE = 3;
071 
072     protected AMQDestination(String urlthrows URISyntaxException
073     {
074         this(new AMQBindingURL(url));
075     }
076 
077     protected AMQDestination(BindingURL binding)
078     {
079         _exchangeName = binding.getExchangeName();
080         _exchangeClass = binding.getExchangeClass();
081 
082         _isExclusive = Boolean.parseBoolean(binding.getOption(BindingURL.OPTION_EXCLUSIVE));
083         _isAutoDelete = Boolean.parseBoolean(binding.getOption(BindingURL.OPTION_AUTODELETE));
084         _isDurable = Boolean.parseBoolean(binding.getOption(BindingURL.OPTION_DURABLE));
085         _queueName = binding.getQueueName() == null null : binding.getQueueName();
086         _routingKey = binding.getRoutingKey() == null null : binding.getRoutingKey();
087         _bindingKeys = binding.getBindingKeys() == null || binding.getBindingKeys().length == new AMQShortString[0: binding.getBindingKeys();
088     }
089 
090     protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString routingKey, AMQShortString queueName)
091     {
092         this(exchangeName, exchangeClass, routingKey, false, false, queueName, null);
093     }
094 
095     protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString routingKey, AMQShortString queueName, AMQShortString[] bindingKeys)
096     {
097         this(exchangeName, exchangeClass, routingKey, false, false, queueName,bindingKeys);
098     }
099 
100     protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString destinationName)
101     {
102         this(exchangeName, exchangeClass, destinationName, false, false, null,null);
103     }
104 
105     protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString routingKey, boolean isExclusive,
106             boolean isAutoDelete, AMQShortString queueName)
107     {
108         this(exchangeName, exchangeClass, routingKey, isExclusive, isAutoDelete, queueName, false,null);
109     }
110 
111     protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString routingKey, boolean isExclusive,
112                              boolean isAutoDelete, AMQShortString queueName,AMQShortString[] bindingKeys)
113     {
114         this(exchangeName, exchangeClass, routingKey, isExclusive, isAutoDelete, queueName, false,bindingKeys);
115     }
116 
117     protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString routingKey, boolean isExclusive,
118             boolean isAutoDelete, AMQShortString queueName, boolean isDurable){
119         this (exchangeName, exchangeClass, routingKey, isExclusive,isAutoDelete,queueName,isDurable,null);
120     }
121 
122     protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString routingKey, boolean isExclusive,
123                              boolean isAutoDelete, AMQShortString queueName, boolean isDurable,AMQShortString[] bindingKeys)
124     {
125         if ( (ExchangeDefaults.DIRECT_EXCHANGE_CLASS.equals(exchangeClass|| 
126               ExchangeDefaults.TOPIC_EXCHANGE_CLASS.equals(exchangeClass))  
127               && routingKey == null)
128         {
129             throw new IllegalArgumentException("routing/binding key  must not be null");
130         }
131         if (exchangeName == null)
132         {
133             throw new IllegalArgumentException("Exchange name must not be null");
134         }
135         if (exchangeClass == null)
136         {
137             throw new IllegalArgumentException("Exchange class must not be null");
138         }
139         _exchangeName = exchangeName;
140         _exchangeClass = exchangeClass;
141         _routingKey = routingKey;
142         _isExclusive = isExclusive;
143         _isAutoDelete = isAutoDelete;
144         _queueName = queueName;
145         _isDurable = isDurable;
146         _bindingKeys = bindingKeys == null || bindingKeys.length == new AMQShortString[0: bindingKeys;
147     }
148 
149     public AMQShortString getEncodedName()
150     {
151         if(_urlAsShortString == null)
152         {
153             toURL();
154         }
155         return _urlAsShortString;
156     }
157 
158     public boolean isDurable()
159     {
160         return _isDurable;
161     }
162 
163     public AMQShortString getExchangeName()
164     {
165         return _exchangeName;
166     }
167 
168     public AMQShortString getExchangeClass()
169     {
170         return _exchangeClass;
171     }
172 
173     public boolean isTopic()
174     {
175         return ExchangeDefaults.TOPIC_EXCHANGE_CLASS.equals(_exchangeClass);
176     }
177 
178     public boolean isQueue()
179     {
180         return ExchangeDefaults.DIRECT_EXCHANGE_CLASS.equals(_exchangeClass);
181     }
182 
183     public String getQueueName()
184     {
185         return _queueName == null null : _queueName.toString();
186     }
187 
188     public AMQShortString getAMQQueueName()
189     {
190         return _queueName;
191     }
192 
193     public void setQueueName(AMQShortString queueName)
194     {
195 
196         _queueName = queueName;
197         // calculated URL now out of date
198         _url = null;
199         _urlAsShortString = null;
200         _byteEncoding = null;
201     }
202 
203     public AMQShortString getRoutingKey()
204     {
205         return _routingKey;
206     }
207 
208     public AMQShortString[] getBindingKeys()
209     {
210         if (_bindingKeys != null && _bindingKeys.length > 0)
211         {
212             return _bindingKeys;
213         }
214         else
215         {
216             // catering to the common use case where the
217             //routingKey is the same as the bindingKey.
218             return new AMQShortString[]{_routingKey};
219         }
220     }
221 
222     public boolean isExclusive()
223     {
224         return _isExclusive;
225     }
226 
227     public boolean isAutoDelete()
228     {
229         return _isAutoDelete;
230     }
231 
232     public abstract boolean isNameRequired();
233 
234     public String toString()
235     {
236         return toURL();
237 
238     }
239 
240     public boolean isCheckedForQueueBinding()
241     {
242         return _checkedForQueueBinding;
243     }
244 
245     public void setCheckedForQueueBinding(boolean checkedForQueueBinding)
246     {
247         _checkedForQueueBinding = checkedForQueueBinding;
248     }
249 
250 
251     public boolean isExchangeExistsChecked()
252     {
253         return _exchangeExistsChecked;
254     }
255 
256     public void setExchangeExistsChecked(final boolean exchangeExistsChecked)
257     {
258         _exchangeExistsChecked = exchangeExistsChecked;
259     }
260 
261     public String toURL()
262     {
263         String url = _url;
264         if(url == null)
265         {
266 
267 
268             StringBuffer sb = new StringBuffer();
269 
270             sb.append(_exchangeClass);
271             sb.append("://");
272             sb.append(_exchangeName);
273 
274             sb.append("/"+_routingKey+"/");
275 
276             if (_queueName != null)
277             {
278                 sb.append(_queueName);
279             }
280 
281             sb.append('?');
282 
283             if (_routingKey != null)
284             {
285                 sb.append(BindingURL.OPTION_ROUTING_KEY);
286                 sb.append("='");
287                 sb.append(_routingKey).append("'");
288                 sb.append(URLHelper.DEFAULT_OPTION_SEPERATOR);
289             }
290 
291             // We can't allow both routingKey and bindingKey
292             if (_routingKey == null && _bindingKeys != null && _bindingKeys.length>0)
293             {
294 
295                 for (AMQShortString bindingKey:_bindingKeys)
296                 {
297                     sb.append(BindingURL.OPTION_BINDING_KEY);
298                     sb.append("='");
299                     sb.append(bindingKey);
300                     sb.append("'");
301                     sb.append(URLHelper.DEFAULT_OPTION_SEPERATOR);
302 
303                 }
304             }
305 
306             if (_isDurable)
307             {
308                 sb.append(BindingURL.OPTION_DURABLE);
309                 sb.append("='true'");
310                 sb.append(URLHelper.DEFAULT_OPTION_SEPERATOR);
311             }
312 
313             if (_isExclusive)
314             {
315                 sb.append(BindingURL.OPTION_EXCLUSIVE);
316                 sb.append("='true'");
317                 sb.append(URLHelper.DEFAULT_OPTION_SEPERATOR);
318             }
319 
320             if (_isAutoDelete)
321             {
322                 sb.append(BindingURL.OPTION_AUTODELETE);
323                 sb.append("='true'");
324                 sb.append(URLHelper.DEFAULT_OPTION_SEPERATOR);
325             }
326 
327             //removeKey the last char '?' if there is no options , ',' if there are.
328             sb.deleteCharAt(sb.length() 1);
329             url = sb.toString();
330             _url = url;
331             _urlAsShortString = new AMQShortString(url);
332         }
333         return url;
334     }
335 
336     public byte[] toByteEncoding()
337     {
338         byte[] encoding = _byteEncoding;
339         if(encoding == null)
340         {
341             int size = _exchangeClass.length() +
342                        _exchangeName.length() +
343                        +  // in place of the destination name
344                        (_queueName == null : _queueName.length()) +
345                        1;
346             encoding = new byte[size];
347             int pos = 0;
348 
349             pos = _exchangeClass.writeToByteArray(encoding, pos);
350             pos = _exchangeName.writeToByteArray(encoding, pos);
351 
352             encoding[pos++(byte)0;
353 
354             if(_queueName == null)
355             {
356                 encoding[pos++(byte)0;
357             }
358             else
359             {
360                 pos = _queueName.writeToByteArray(encoding,pos);
361             }
362             byte options = 0;
363             if(_isDurable)
364             {
365                 options |= IS_DURABLE_MASK;
366             }
367             if(_isExclusive)
368             {
369                 options |= IS_EXCLUSIVE_MASK;
370             }
371             if(_isAutoDelete)
372             {
373                 options |= IS_AUTODELETE_MASK;
374             }
375             encoding[pos= options;
376 
377 
378             _byteEncoding = encoding;
379 
380         }
381         return encoding;
382     }
383 
384     public boolean equals(Object o)
385     {
386         if (this == o)
387         {
388             return true;
389         }
390         if (o == null || getClass() != o.getClass())
391         {
392             return false;
393         }
394 
395         final AMQDestination that = (AMQDestinationo;
396 
397         if (!_exchangeClass.equals(that._exchangeClass))
398         {
399             return false;
400         }
401         if (!_exchangeName.equals(that._exchangeName))
402         {
403             return false;
404         }
405         if ((_queueName == null && that._queueName != null||
406             (_queueName != null && !_queueName.equals(that._queueName)))
407         {
408             return false;
409         }
410 
411         return true;
412     }
413 
414     public int hashCode()
415     {
416         int result;
417         result = _exchangeName.hashCode();
418         result = 29 * result + _exchangeClass.hashCode();
419         //result = 29 * result + _destinationName.hashCode();
420         if (_queueName != null)
421         {
422             result = 29 * result + _queueName.hashCode();
423         }
424 
425         return result;
426     }
427 
428     public Reference getReference() throws NamingException
429     {
430         return new Reference(
431                 this.getClass().getName(),
432                 new StringRefAddr(this.getClass().getName(), toURL()),
433                 AMQConnectionFactory.class.getName(),
434                 null);          // factory location
435     }
436 
437 
438     public static Destination createDestination(byte[] byteEncodedDestination)
439     {
440         AMQShortString exchangeClass;
441         AMQShortString exchangeName;
442         AMQShortString routingKey;
443         AMQShortString queueName;
444         boolean isDurable;
445         boolean isExclusive;
446         boolean isAutoDelete;
447 
448         int pos = 0;
449         exchangeClass = AMQShortString.readFromByteArray(byteEncodedDestination, pos);
450         pos+= exchangeClass.length() 1;
451         exchangeName =  AMQShortString.readFromByteArray(byteEncodedDestination, pos);
452         pos+= exchangeName.length() 1;
453         routingKey =  AMQShortString.readFromByteArray(byteEncodedDestination, pos);
454         pos+= (routingKey == null : routingKey.length()) 1;
455         queueName =  AMQShortString.readFromByteArray(byteEncodedDestination, pos);
456         pos+= (queueName == null : queueName.length()) 1;
457         int options = byteEncodedDestination[pos];
458         isDurable = (options & IS_DURABLE_MASK!= 0;
459         isExclusive = (options & IS_EXCLUSIVE_MASK!= 0;
460         isAutoDelete = (options & IS_AUTODELETE_MASK!= 0;
461 
462         if (exchangeClass.equals(ExchangeDefaults.DIRECT_EXCHANGE_CLASS))
463         {
464             return new AMQQueue(exchangeName,routingKey,queueName,isExclusive,isAutoDelete,isDurable);
465         }
466         else if (exchangeClass.equals(ExchangeDefaults.TOPIC_EXCHANGE_CLASS))
467         {
468             return new AMQTopic(exchangeName,routingKey,isAutoDelete,queueName,isDurable);
469         }
470         else if (exchangeClass.equals(ExchangeDefaults.HEADERS_EXCHANGE_CLASS))
471         {
472             return new AMQHeadersExchange(routingKey);
473         }
474         else
475         {
476             return new AMQAnyDestination(exchangeName,exchangeClass,
477                                          routingKey,isExclusive, 
478                                          isAutoDelete,queueName, 
479                                          isDurable, new AMQShortString[0]);
480         }
481 
482     }
483 
484     public static Destination createDestination(BindingURL binding)
485     {
486         AMQShortString type = binding.getExchangeClass();
487 
488         if (type.equals(ExchangeDefaults.DIRECT_EXCHANGE_CLASS))
489         {
490             return new AMQQueue(binding);
491         }
492         else if (type.equals(ExchangeDefaults.TOPIC_EXCHANGE_CLASS))
493         {
494             return new AMQTopic(binding);
495         }
496         else if (type.equals(ExchangeDefaults.HEADERS_EXCHANGE_CLASS))
497         {
498             return new AMQHeadersExchange(binding);
499         }
500         else
501         {
502             return new AMQAnyDestination(binding);
503         }
504     }
505 }