View Javadoc

    1   /***
    2    *
    3    * Copyright 2004 Protique Ltd
    4    *
    5    * Licensed under the Apache License, Version 2.0 (the "License");
    6    * you may not use this file except in compliance with the License.
    7    * You may obtain a copy of the License at
    8    *
    9    * http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   **/
    18
    19  package org.codehaus.activemq.message;
    20
    21  import org.codehaus.activemq.filter.DestinationFilter;
    22  import org.codehaus.activemq.jndi.JNDIBaseStorable;
    23
    24  import javax.jms.Destination;
    25  import javax.jms.JMSException;
    26  import javax.jms.Queue;
    27  import javax.jms.TemporaryQueue;
    28  import javax.jms.TemporaryTopic;
    29  import javax.jms.Topic;
    30  import java.io.DataInput;
    31  import java.io.DataOutput;
    32  import java.io.IOException;
    33  import java.io.Serializable;
    34  import java.util.Properties;
    35
    36  /***
    37   * A <CODE>Destination</CODE> object encapsulates a provider-specific
    38   * address.
    39   * The JMS API does not define a standard address syntax. Although a standard
    40   * address syntax was considered, it was decided that the differences in
    41   * address semantics between existing message-oriented middleware (MOM)
    42   * products were too wide to bridge with a single syntax.
    43   * <p/>
    44   * <P>Since <CODE>Destination</CODE> is an administered object, it may
    45   * contain
    46   * provider-specific configuration information in addition to its address.
    47   * <p/>
    48   * <P>The JMS API also supports a client's use of provider-specific address
    49   * names.
    50   * <p/>
    51   * <P><CODE>Destination</CODE> objects support concurrent use.
    52   * <p/>
    53   * <P>A <CODE>Destination</CODE> object is a JMS administered object.
    54   * <p/>
    55   * <P>JMS administered objects are objects containing configuration
    56   * information that are created by an administrator and later used by
    57   * JMS clients. They make it practical to administer the JMS API in the
    58   * enterprise.
    59   * <p/>
    60   * <P>Although the interfaces for administered objects do not explicitly
    61   * depend on the Java Naming and Directory Interface (JNDI) API, the JMS API
    62   * establishes the convention that JMS clients find administered objects by
    63   * looking them up in a JNDI namespace.
    64   * <p/>
    65   * <P>An administrator can place an administered object anywhere in a
    66   * namespace. The JMS API does not define a naming policy.
    67   * <p/>
    68   * <P>It is expected that JMS providers will provide the tools an
    69   * administrator needs to create and configure administered objects in a
    70   * JNDI namespace. JMS provider implementations of administered objects
    71   * should implement the <CODE>javax.naming.Referenceable</CODE> and
    72   * <CODE>java.io.Serializable</CODE> interfaces so that they can be stored in
    73   * all JNDI naming contexts. In addition, it is recommended that these
    74   * implementations follow the JavaBeans<SUP><FONT SIZE="-2">TM</FONT></SUP>
    75   * design patterns.
    76   * <p/>
    77   * <P>This strategy provides several benefits:
    78   * <p/>
    79   * <UL>
    80   * <LI>It hides provider-specific details from JMS clients.
    81   * <LI>It abstracts JMS administrative information into objects in the Java
    82   * programming language ("Java objects")
    83   * that are easily organized and administered from a common
    84   * management console.
    85   * <LI>Since there will be JNDI providers for all popular naming
    86   * services, JMS providers can deliver one implementation
    87   * of administered objects that will run everywhere.
    88   * </UL>
    89   * <p/>
    90   * <P>An administered object should not hold on to any remote resources.
    91   * Its lookup should not use remote resources other than those used by the
    92   * JNDI API itself.
    93   * <p/>
    94   * <P>Clients should think of administered objects as local Java objects.
    95   * Looking them up should not have any hidden side effects or use surprising
    96   * amounts of local resources.
    97   */
    98
    99  public abstract class ActiveMQDestination extends JNDIBaseStorable implements Destination, Comparable, Serializable {
    100
    101     private static final int NULL_DESTINATION = 10;
    102     private static final String TEMP_PREFIX = "{TD{";
    103     private static final String TEMP_POSTFIX = "}TD}";
    104
    105     /***
    106      * A helper method to return a descriptive string for the topic or queue
    107      *
    108      * @return a descriptive string for this queue or topic
    109      */
    110     public static String inspect(Destination destination) {
    111         if (destination instanceof Topic) {
    112             return "Topic(" + destination.toString() + ")";
    113         }
    114         else {
    115             return "Queue(" + destination.toString() + ")";
    116         }
    117     }
    118
    119     /***
    120      * Topic Destination object
    121      */
    122     public static final int ACTIVEMQ_TOPIC = 1;
    123     /***
    124      * Temporary Topic Destination object
    125      */
    126     public static final int ACTIVEMQ_TEMPORARY_TOPIC = 2;
    127
    128     /***
    129      * Queue Destination object
    130      */
    131     public static final int ACTIVEMQ_QUEUE = 3;
    132     /***
    133      * Temporary Queue Destination object
    134      */
    135     public static final int ACTIVEMQ_TEMPORARY_QUEUE = 4;
    136
    137
    138     private String physicalName;
    139
    140     /***
    141      * The Default Constructor
    142      */
    143     protected ActiveMQDestination() {
    144     }
    145
    146     /***
    147      * Construct the ActiveMQDestination with a defined physical name;
    148      *
    149      * @param name
    150      */
    151
    152     protected ActiveMQDestination(String name) {
    153         this.physicalName = name;
    154     }
    155
    156     /***
    157      * @param o object to compare
    158      * @return 1 if this > o else 0 if they are equal or -1 if this < o
    159      */
    160     public int compareTo(Object o) {
    161         if (o instanceof ActiveMQDestination) {
    162             return compareTo((ActiveMQDestination) o);
    163         }
    164         return -1;
    165     }
    166
    167     /***
    168      * Lets sort by name first then lets sort topics greater than queues
    169      *
    170      * @param that another destination to compare against
    171      * @return 1 if this > that else 0 if they are equal or -1 if this < that
    172      */
    173     public int compareTo(ActiveMQDestination that) {
    174         int answer = 0;
    175         if (physicalName != that.physicalName) {
    176             if (physicalName == null) {
    177                 return -1;
    178             }
    179             else if (that.physicalName == null) {
    180                 return 1;
    181             }
    182             answer = physicalName.compareTo(that.physicalName);
    183         }
    184         if (answer == 0) {
    185             if (isTopic()) {
    186                 if (that.isQueue()) {
    187                     return 1;
    188                 }
    189             }
    190             else {
    191                 if (that.isTopic()) {
    192                     return -1;
    193                 }
    194             }
    195         }
    196         return answer;
    197     }
    198
    199
    200     /***
    201      * @return Returns the Destination type
    202      */
    203
    204     public abstract int getDestinationType();
    205
    206
    207     /***
    208      * @return Returns the physicalName.
    209      */
    210     public String getPhysicalName() {
    211         return this.physicalName;
    212     }
    213
    214     /***
    215      * @param newPhysicalName The physicalName to set.
    216      */
    217     public void setPhysicalName(String newPhysicalName) {
    218         this.physicalName = newPhysicalName;
    219     }
    220
    221     /***
    222      * Set the properties that will represent the instance in JNDI
    223      *
    224      * @param props
    225      */
    226     protected void buildFromProperties(Properties props) {
    227         this.physicalName = props.getProperty("physicalName", this.physicalName);
    228
    229     }
    230
    231     /***
    232      * Initialize the instance from properties stored in JNDI
    233      *
    234      * @param props
    235      */
    236
    237     protected void populateProperties(Properties props) {
    238         props.put("physicalName", this.physicalName);
    239     }
    240
    241     /***
    242      * Returns true if a temporary Destination
    243      *
    244      * @return true/false
    245      */
    246
    247     public boolean isTemporary() {
    248         return getDestinationType() == ACTIVEMQ_TEMPORARY_TOPIC ||
    249                 getDestinationType() == ACTIVEMQ_TEMPORARY_QUEUE;
    250     }
    251
    252     /***
    253      * Returns true if a Topic Destination
    254      *
    255      * @return true/false
    256      */
    257
    258     public boolean isTopic() {
    259         return getDestinationType() == ACTIVEMQ_TOPIC ||
    260                 getDestinationType() == ACTIVEMQ_TEMPORARY_TOPIC;
    261     }
    262
    263     /***
    264      * Returns true if a Queue Destination
    265      *
    266      * @return true/false
    267      */
    268     public boolean isQueue() {
    269         return !isTopic();
    270     }
    271
    272     /***
    273      * @return string representation of this instance
    274      */
    275
    276     public String toString() {
    277         return this.physicalName;
    278     }
    279
    280     /***
    281      * @return hashCode for this instance
    282      */
    283
    284     public int hashCode() {
    285         int answer = 0xcafebabe;
    286
    287         if (this.physicalName != null) {
    288             answer = physicalName.hashCode();
    289         }
    290         if (isTopic()) {
    291             answer ^= 0xfabfab;
    292         }
    293         return answer;
    294     }
    295
    296     /***
    297      * if the object passed in is equivalent, return true
    298      *
    299      * @param obj the object to compare
    300      * @return true if this instance and obj are equivalent
    301      */
    302
    303     public boolean equals(Object obj) {
    304         boolean result = this == obj;
    305         if (!result && obj != null && obj instanceof ActiveMQDestination) {
    306             ActiveMQDestination other = (ActiveMQDestination) obj;
    307             result = this.getDestinationType() == other.getDestinationType() &&
    308                     this.physicalName.equals(other.physicalName);
    309         }
    310         return result;
    311     }
    312
    313     /***
    314      * Write an ActiveMQDestination to a Stream
    315      *
    316      * @param destination
    317      * @param dataOut
    318      * @throws IOException
    319      */
    320
    321     public static void writeToStream(ActiveMQDestination destination, DataOutput dataOut) throws IOException {
    322         if (destination != null) {
    323             dataOut.write(destination.getDestinationType());
    324             dataOut.writeUTF(destination == null ? "" : destination.getPhysicalName());
    325         }
    326         else {
    327             dataOut.write(NULL_DESTINATION);
    328         }
    329     }
    330
    331     /***
    332      * Read an ActiveMQDestination  from a Stream
    333      *
    334      * @param dataIn
    335      * @return the ActiveMQDestination
    336      * @throws IOException
    337      */
    338
    339     public static ActiveMQDestination readFromStream(DataInput dataIn) throws IOException {
    340
    341         int type = dataIn.readByte();
    342         if (type == NULL_DESTINATION) {
    343             return null;
    344         }
    345         ActiveMQDestination result = null;
    346         if (type == ACTIVEMQ_TOPIC) {
    347             result = new ActiveMQTopic();
    348         }
    349         else if (type == ACTIVEMQ_TEMPORARY_TOPIC) {
    350             result = new ActiveMQTemporaryTopic();
    351         }
    352         else if (type == ACTIVEMQ_QUEUE) {
    353             result = new ActiveMQQueue();
    354         }
    355         else {
    356             result = new ActiveMQTemporaryQueue();
    357         }
    358         result.setPhysicalName(dataIn.readUTF());
    359         return result;
    360
    361     }
    362
    363     /***
    364      * Create a temporary name from the clientId
    365      *
    366      * @param clientId
    367      * @return
    368      */
    369     public static String createTemporaryName(String clientId) {
    370         return TEMP_PREFIX + clientId + TEMP_POSTFIX;
    371     }
    372
    373     /***
    374      * From a temporary destination find the clientId of the Connection that created it
    375      *
    376      * @param destination
    377      * @return the clientId or null if not a temporary destination
    378      */
    379     public static String getClientId(ActiveMQDestination destination) {
    380         String answer = null;
    381         if (destination != null && destination.isTemporary()) {
    382             String name = destination.getPhysicalName();
    383             int start = name.indexOf(TEMP_PREFIX);
    384             if (start >= 0) {
    385                 start += TEMP_PREFIX.length();
    386                 int stop = name.lastIndexOf(TEMP_POSTFIX);
    387                 if (stop > start && stop < name.length()) {
    388                     answer = name.substring(start, stop);
    389                 }
    390             }
    391         }
    392         return answer;
    393     }
    394
    395     /***
    396      * @return true if the destination matches multiple possible destinations
    397      */
    398     public boolean isWildcard() {
    399         if (physicalName != null) {
    400             return physicalName.indexOf(DestinationFilter.ANY_CHILD) >= 0
    401                     || physicalName.indexOf(DestinationFilter.ANY_DESCENDENT) >= 0;
    402         }
    403         return false;
    404     }
    405
    406     /***
    407      * @param destination
    408      * @return @throws JMSException
    409      * @throws javax.jms.JMSException
    410      */
    411     public static ActiveMQDestination transformDestination(Destination destination) throws JMSException {
    412         ActiveMQDestination result = null;
    413         if (destination != null) {
    414             if (destination instanceof ActiveMQDestination) {
    415                 result = (ActiveMQDestination) destination;
    416             }
    417             else {
    418                 if (destination instanceof TemporaryQueue) {
    419                     result = new ActiveMQTemporaryQueue(((Queue) destination).getQueueName());
    420                 }
    421                 else if (destination instanceof TemporaryTopic) {
    422                     result = new ActiveMQTemporaryTopic(((Topic) destination).getTopicName());
    423                 }
    424                 else if (destination instanceof Queue) {
    425                     result = new ActiveMQTemporaryQueue(((Queue) destination).getQueueName());
    426                 }
    427                 else if (destination instanceof Topic) {
    428                     result = new ActiveMQTemporaryTopic(((Topic) destination).getTopicName());
    429                 }
    430             }
    431         }
    432         return result;
    433     }
    434 }