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       @Override
208       public String toString()
209       {
210          StringBuilder str = new StringBuilder(replaceWildcardChars(factoryClassName));
211    
212          if (params != null)
213          {
214             if (!params.isEmpty())
215             {
216                str.append("?");
217             }
218    
219             boolean first = true;
220             for (Map.Entry<String, Object> entry : params.entrySet())
221             {
222                if (!first)
223                {
224                   str.append("&");
225                }
226                String encodedKey = replaceWildcardChars(entry.getKey());
227    
228                String val = entry.getValue().toString();
229                String encodedVal = replaceWildcardChars(val);
230    
231                str.append(encodedKey).append('=').append(encodedVal);
232    
233                first = false;
234             }
235          }
236    
237          return str.toString();
238       }
239    
240       /**
241        * Encodes this TransportConfiguration into a buffer.
242        * <p/>
243        * Note that this is only used internally HornetQ.
244        *
245        * @param buffer the buffer to encode into
246        */
247       public void encode(final HornetQBuffer buffer)
248       {
249          buffer.writeString(name);
250          buffer.writeString(factoryClassName);
251    
252          buffer.writeInt(params == null ? 0 : params.size());
253    
254          if (params != null)
255          {
256             for (Map.Entry<String, Object> entry : params.entrySet())
257             {
258                buffer.writeString(entry.getKey());
259    
260                Object val = entry.getValue();
261    
262                if (val instanceof Boolean)
263                {
264                   buffer.writeByte(TransportConfiguration.TYPE_BOOLEAN);
265                   buffer.writeBoolean((Boolean) val);
266                }
267                else if (val instanceof Integer)
268                {
269                   buffer.writeByte(TransportConfiguration.TYPE_INT);
270                   buffer.writeInt((Integer) val);
271                }
272                else if (val instanceof Long)
273                {
274                   buffer.writeByte(TransportConfiguration.TYPE_LONG);
275                   buffer.writeLong((Long) val);
276                }
277                else if (val instanceof String)
278                {
279                   buffer.writeByte(TransportConfiguration.TYPE_STRING);
280                   buffer.writeString((String) val);
281                }
282                else
283                {
284                   throw new IllegalArgumentException("Invalid type " + val);
285                }
286             }
287          }
288       }
289    
290       /**
291        * Decodes this TransportConfiguration from a buffer.
292        * <p/>
293        * Note this is only used internally by HornetQ
294        *
295        * @param buffer the buffer to decode from
296        */
297       public void decode(final HornetQBuffer buffer)
298       {
299          name = buffer.readString();
300          factoryClassName = buffer.readString();
301    
302          int num = buffer.readInt();
303    
304          if (params == null)
305          {
306             if (num > 0)
307             {
308                params = new HashMap<String, Object>();
309             }
310          }
311          else
312          {
313             params.clear();
314          }
315    
316          for (int i = 0; i < num; i++)
317          {
318             String key = buffer.readString();
319    
320             byte type = buffer.readByte();
321    
322             Object val;
323    
324             switch (type)
325             {
326                case TYPE_BOOLEAN:
327                {
328                   val = buffer.readBoolean();
329    
330                   break;
331                }
332                case TYPE_INT:
333                {
334                   val = buffer.readInt();
335    
336                   break;
337                }
338                case TYPE_LONG:
339                {
340                   val = buffer.readLong();
341    
342                   break;
343                }
344                case TYPE_STRING:
345                {
346                   val = buffer.readString();
347    
348                   break;
349                }
350                default:
351                {
352                   throw new IllegalArgumentException("Invalid type " + type);
353                }
354             }
355    
356             params.put(key, val);
357          }
358       }
359    
360       private String replaceWildcardChars(final String str)
361       {
362          return str.replace('.', '-');
363       }
364    }