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 }