    001    /*
    002     * Copyright 2009 Red Hat, Inc.
    003     * Red Hat licenses this file to you under the Apache License, version
    004     * 2.0 (the "License"); you may not use this file except in compliance
    005     * with the License.  You may obtain a copy of the License at
    006     *    http://www.apache.org/licenses/LICENSE-2.0
    007     * Unless required by applicable law or agreed to in writing, software
    008     * distributed under the License is distributed on an "AS IS" BASIS,
    009     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    010     * implied.  See the License for the specific language governing
    011     * permissions and limitations under the License.
    012     */
    013    package org.hornetq.api.core;
    014
    015    import java.io.Serializable;
    016    import java.util.HashMap;
    017    import java.util.Map;
    018
    019    import org.hornetq.utils.UUIDGenerator;
    020
    021    /**
    022     * A TransportConfiguration is used by a client to specify a connections to a server and its backup if one exists.<br><br>
    023     * <p/>
    024     * Typically the constructors take the class name and parameters for needed to create the connection. These will be
    025     * different dependent on which connector is being used, i.e. Netty or InVM etc. For example:<br><br>
    026     * <p/>
    027     * <code>
    028     * HashMap<String, Object> map = new HashMap<String, Object>();<br>
    029     * map.put("host", "localhost");<br>
    030     * map.put("port", 5445);<br>
    031     * TransportConfiguration config = new TransportConfiguration(InVMConnectorFactory.class.getName(), map); <br>
    032     * ClientSessionFactory sf = new ClientSessionFactoryImpl(config);  <br>
    033     * </code><br><br>
    034     *
    035     * @author <a href="mailto:tim.fox@jboss.com">Tim Fox</a>
    036     */
    037    public class TransportConfiguration implements Serializable
    038    {
    039       private static final long serialVersionUID = -3994528421527392679L;
    040
    041       private String name;
    042
    043       private String factoryClassName;
    044
    045       private Map<String, Object> params;
    046
    047       private static final byte TYPE_BOOLEAN = 0;
    048
    049       private static final byte TYPE_INT = 1;
    050
    051       private static final byte TYPE_LONG = 2;
    052
    053       private static final byte TYPE_STRING = 3;
    054
    055       /**
    056        * Utility method for splitting a comma separated list of hosts
    057        *
    058        * @param commaSeparatedHosts the comma separated host string
    059        * @return the hosts
    060        */
    061       public static String[] splitHosts(final String commaSeparatedHosts)
    062       {
    063          if (commaSeparatedHosts == null)
    064          {
    065             return new String[0];
    066          }
    067          String[] hosts = commaSeparatedHosts.split(",");
    068
    069          for (int i = 0; i < hosts.length; i++)
    070          {
    071             hosts[i] = hosts[i].trim();
    072          }
    073          return hosts;
    074       }
    075
    076       /**
    077        * Creates a default TransportConfiguration with no configured transport.
    078        */
    079       public TransportConfiguration()
    080       {
    081       }
    082
    083       /**
    084        * Creates a TransportConfiguration with a specific name providing the class name of the {@link org.hornetq.spi.core.remoting.ConnectorFactory}
    085        * and any parameters needed.
    086        *
    087        * @param className The class name of the ConnectorFactory
    088        * @param params    The parameters needed by the ConnectorFactory
    089        * @param name      The name of this TransportConfiguration
    090        */
    091       public TransportConfiguration(final String className, final Map<String, Object> params, final String name)
    092       {
    093          factoryClassName = className;
    094
    095          this.params = params;
    096
    097          this.name = name;
    098       }
    099
    100       /**
    101        * Creates a TransportConfiguration providing the class name of the {@link org.hornetq.spi.core.remoting.ConnectorFactory}
    102        * and any parameters needed.
    103        *
    104        * @param className The class name of the ConnectorFactory
    105        * @param params    The parameters needed by the ConnectorFactory
    106        */
    107       public TransportConfiguration(final String className, final Map<String, Object> params)
    108       {
    109          this(className, params, UUIDGenerator.getInstance().generateStringUUID());
    110       }
    111
    112       /**
    113        * Creates a TransportConfiguration providing the class name of the {@link org.hornetq.spi.core.remoting.ConnectorFactory}
    114        *
    115        * @param className The class name of the ConnectorFactory
    116        */
    117       public TransportConfiguration(final String className)
    118       {
    119          this(className, new HashMap<String, Object>(), UUIDGenerator.getInstance().generateStringUUID());
    120       }
    121
    122       /**
    123        * Returns the name of this TransportConfiguration.
    124        *
    125        * @return the name
    126        */
    127       public String getName()
    128       {
    129          return name;
    130       }
    131
    132       /**
    133        * Returns the class name of ConnectorFactory being used by this TransportConfiguration
    134        *
    135        * @return The factory's class name
    136        */
    137       public String getFactoryClassName()
    138       {
    139          return factoryClassName;
    140       }
    141
    142       /**
    143        * Returns any parameters set for this TransportConfiguration
    144        *
    145        * @return the parameters
    146        */
    147       public Map<String, Object> getParams()
    148       {
    149          return params;
    150       }
    151
    152       @Override
    153       public int hashCode()
    154       {
    155          return factoryClassName.hashCode();
    156       }
    157
    158       @Override
    159       public boolean equals(final Object other)
    160       {
    161          if (other instanceof TransportConfiguration == false)
    162          {
    163             return false;
    164          }
    165
    166          TransportConfiguration kother = (TransportConfiguration) other;
    167
    168          if (factoryClassName.equals(kother.factoryClassName))
    169          {
    170             if (params == null || params.isEmpty())
    171             {
    172                return kother.params == null || kother.params.isEmpty();
    173             }
    174             else
    175             {
    176                if (kother.params == null || kother.params.isEmpty())
    177                {
    178                   return false;
    179                }
    180                else if (params.size() == kother.params.size())
    181                {
    182                   for (Map.Entry<String, Object> entry : params.entrySet())
    183                   {
    184                      Object thisVal = entry.getValue();
    185
    186                      Object otherVal = kother.params.get(entry.getKey());
    187
    188                      if (otherVal == null || !otherVal.equals(thisVal))
    189                      {
    190                         return false;
    191                      }
    192                   }
    193                   return true;
    194                }
    195                else
    196                {
    197                   return false;
    198                }
    199             }
    200          }
    201          else
    202          {
    203             return false;
    204          }
    205       }
    206
    207       /**
    208        * There's a case on ClusterConnections that we need to find an equivalent Connector
    209        * and we can't use a Netty Cluster Connection on an InVM ClusterConnection (inVM used on tests)
    210        * for that reason I need to test if the two instances of the TransportConfiguration are equivalent
    211        * while a test a connector against an acceptor
    212        * @param otherConfig
    213        * @return
    214        */
    215       public boolean isEquivalent(TransportConfiguration otherConfig)
    216       {
    217          if (this.getFactoryClassName().equals(otherConfig.getFactoryClassName()))
    218          {
    219             return true;
    220          }
    221          else if (this.getFactoryClassName().contains("Netty") && otherConfig.getFactoryClassName().contains("Netty"))
    222          {
    223             return true;
    224          }
    225          else if (this.getFactoryClassName().contains("InVM") && otherConfig.getFactoryClassName().contains("InVM"))
    226          {
    227             return true;
    228          }
    229          else
    230          {
    231             return false;
    232          }
    233       }
    234
    235       @Override
    236       public String toString()
    237       {
    238          StringBuilder str = new StringBuilder(replaceWildcardChars(factoryClassName));
    239
    240          if (params != null)
    241          {
    242             if (!params.isEmpty())
    243             {
    244                str.append("?");
    245             }
    246
    247             boolean first = true;
    248             for (Map.Entry<String, Object> entry : params.entrySet())
    249             {
    250                if (!first)
    251                {
    252                   str.append("&");
    253                }
    254                String encodedKey = replaceWildcardChars(entry.getKey());
    255
    256                String val = entry.getValue().toString();
    257                String encodedVal = replaceWildcardChars(val);
    258
    259                str.append(encodedKey).append('=').append(encodedVal);
    260
    261                first = false;
    262             }
    263          }
    264
    265          return str.toString();
    266       }
    267
    268       /**
    269        * Encodes this TransportConfiguration into a buffer.
    270        * <p/>
    271        * Note that this is only used internally HornetQ.
    272        *
    273        * @param buffer the buffer to encode into
    274        */
    275       public void encode(final HornetQBuffer buffer)
    276       {
    277          buffer.writeString(name);
    278          buffer.writeString(factoryClassName);
    279
    280          buffer.writeInt(params == null ? 0 : params.size());
    281
    282          if (params != null)
    283          {
    284             for (Map.Entry<String, Object> entry : params.entrySet())
    285             {
    286                buffer.writeString(entry.getKey());
    287
    288                Object val = entry.getValue();
    289
    290                if (val instanceof Boolean)
    291                {
    292                   buffer.writeByte(TransportConfiguration.TYPE_BOOLEAN);
    293                   buffer.writeBoolean((Boolean) val);
    294                }
    295                else if (val instanceof Integer)
    296                {
    297                   buffer.writeByte(TransportConfiguration.TYPE_INT);
    298                   buffer.writeInt((Integer) val);
    299                }
    300                else if (val instanceof Long)
    301                {
    302                   buffer.writeByte(TransportConfiguration.TYPE_LONG);
    303                   buffer.writeLong((Long) val);
    304                }
    305                else if (val instanceof String)
    306                {
    307                   buffer.writeByte(TransportConfiguration.TYPE_STRING);
    308                   buffer.writeString((String) val);
    309                }
    310                else
    311                {
    312                   throw new IllegalArgumentException("Invalid type " + val);
    313                }
    314             }
    315          }
    316       }
    317
    318       /**
    319        * Decodes this TransportConfiguration from a buffer.
    320        * <p/>
    321        * Note this is only used internally by HornetQ
    322        *
    323        * @param buffer the buffer to decode from
    324        */
    325       public void decode(final HornetQBuffer buffer)
    326       {
    327          name = buffer.readString();
    328          factoryClassName = buffer.readString();
    329
    330          int num = buffer.readInt();
    331
    332          if (params == null)
    333          {
    334             if (num > 0)
    335             {
    336                params = new HashMap<String, Object>();
    337             }
    338          }
    339          else
    340          {
    341             params.clear();
    342          }
    343
    344          for (int i = 0; i < num; i++)
    345          {
    346             String key = buffer.readString();
    347
    348             byte type = buffer.readByte();
    349
    350             Object val;
    351
    352             switch (type)
    353             {
    354                case TYPE_BOOLEAN:
    355                {
    356                   val = buffer.readBoolean();
    357
    358                   break;
    359                }
    360                case TYPE_INT:
    361                {
    362                   val = buffer.readInt();
    363
    364                   break;
    365                }
    366                case TYPE_LONG:
    367                {
    368                   val = buffer.readLong();
    369
    370                   break;
    371                }
    372                case TYPE_STRING:
    373                {
    374                   val = buffer.readString();
    375
    376                   break;
    377                }
    378                default:
    379                {
    380                   throw new IllegalArgumentException("Invalid type " + type);
    381                }
    382             }
    383
    384             params.put(key, val);
    385          }
    386       }
    387
    388       private String replaceWildcardChars(final String str)
    389       {
    390          return str.replace('.', '-');
    391       }
    392    }