001/* 002 * Copyright 2012-2017 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2012-2017 UnboundID Corp. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.sdk; 022 023 024 025import java.util.concurrent.ArrayBlockingQueue; 026import java.util.concurrent.TimeUnit; 027import java.util.concurrent.atomic.AtomicBoolean; 028import javax.net.SocketFactory; 029 030import com.unboundid.util.Debug; 031import com.unboundid.util.NotMutable; 032import com.unboundid.util.StaticUtils; 033import com.unboundid.util.ThreadSafety; 034import com.unboundid.util.ThreadSafetyLevel; 035import com.unboundid.util.Validator; 036 037import static com.unboundid.ldap.sdk.LDAPMessages.*; 038 039 040 041/** 042 * This class provides a server set implementation that will attempt to 043 * establish connections to all associated servers in parallel, keeping the one 044 * that was first to be successfully established and closing all others. 045 * <BR><BR> 046 * Note that this server set implementation may only be used in conjunction with 047 * connection options that allow the associated socket factory to create 048 * multiple connections in parallel. If the 049 * {@link LDAPConnectionOptions#allowConcurrentSocketFactoryUse} method returns 050 * false for the associated connection options, then the {@code getConnection} 051 * methods will throw an exception. 052 * <BR><BR> 053 * <H2>Example</H2> 054 * The following example demonstrates the process for creating a fastest connect 055 * server set that may be used to establish connections to either of two 056 * servers. When using the server set to attempt to create a connection, it 057 * will try both in parallel and will return the first connection that it is 058 * able to establish: 059 * <PRE> 060 * // Create arrays with the addresses and ports of the directory server 061 * // instances. 062 * String[] addresses = 063 * { 064 * server1Address, 065 * server2Address 066 * }; 067 * int[] ports = 068 * { 069 * server1Port, 070 * server2Port 071 * }; 072 * 073 * // Create the server set using the address and port arrays. 074 * FastestConnectServerSet fastestConnectSet = 075 * new FastestConnectServerSet(addresses, ports); 076 * 077 * // Verify that we can establish a single connection using the server set. 078 * LDAPConnection connection = fastestConnectSet.getConnection(); 079 * RootDSE rootDSEFromConnection = connection.getRootDSE(); 080 * connection.close(); 081 * 082 * // Verify that we can establish a connection pool using the server set. 083 * SimpleBindRequest bindRequest = 084 * new SimpleBindRequest("uid=pool.user,dc=example,dc=com", "password"); 085 * LDAPConnectionPool pool = 086 * new LDAPConnectionPool(fastestConnectSet, bindRequest, 10); 087 * RootDSE rootDSEFromPool = pool.getRootDSE(); 088 * pool.close(); 089 * </PRE> 090 */ 091@NotMutable() 092@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 093public final class FastestConnectServerSet 094 extends ServerSet 095{ 096 // The port numbers of the target servers. 097 private final int[] ports; 098 099 // The set of connection options to use for new connections. 100 private final LDAPConnectionOptions connectionOptions; 101 102 // The socket factory to use to establish connections. 103 private final SocketFactory socketFactory; 104 105 // The addresses of the target servers. 106 private final String[] addresses; 107 108 109 110 /** 111 * Creates a new fastest connect server set with the specified set of 112 * directory server addresses and port numbers. It will use the default 113 * socket factory provided by the JVM to create the underlying sockets. 114 * 115 * @param addresses The addresses of the directory servers to which the 116 * connections should be established. It must not be 117 * {@code null} or empty. 118 * @param ports The ports of the directory servers to which the 119 * connections should be established. It must not be 120 * {@code null}, and it must have the same number of 121 * elements as the {@code addresses} array. The order of 122 * elements in the {@code addresses} array must correspond 123 * to the order of elements in the {@code ports} array. 124 */ 125 public FastestConnectServerSet(final String[] addresses, final int[] ports) 126 { 127 this(addresses, ports, null, null); 128 } 129 130 131 132 /** 133 * Creates a new fastest connect server set with the specified set of 134 * directory server addresses and port numbers. It will use the default 135 * socket factory provided by the JVM to create the underlying sockets. 136 * 137 * @param addresses The addresses of the directory servers to which 138 * the connections should be established. It must 139 * not be {@code null} or empty. 140 * @param ports The ports of the directory servers to which the 141 * connections should be established. It must not 142 * be {@code null}, and it must have the same 143 * number of elements as the {@code addresses} 144 * array. The order of elements in the 145 * {@code addresses} array must correspond to the 146 * order of elements in the {@code ports} array. 147 * @param connectionOptions The set of connection options to use for the 148 * underlying connections. 149 */ 150 public FastestConnectServerSet(final String[] addresses, final int[] ports, 151 final LDAPConnectionOptions connectionOptions) 152 { 153 this(addresses, ports, null, connectionOptions); 154 } 155 156 157 158 /** 159 * Creates a new fastest connect server set with the specified set of 160 * directory server addresses and port numbers. It will use the provided 161 * socket factory to create the underlying sockets. 162 * 163 * @param addresses The addresses of the directory servers to which the 164 * connections should be established. It must not be 165 * {@code null} or empty. 166 * @param ports The ports of the directory servers to which the 167 * connections should be established. It must not be 168 * {@code null}, and it must have the same number of 169 * elements as the {@code addresses} array. The order 170 * of elements in the {@code addresses} array must 171 * correspond to the order of elements in the 172 * {@code ports} array. 173 * @param socketFactory The socket factory to use to create the underlying 174 * connections. 175 */ 176 public FastestConnectServerSet(final String[] addresses, final int[] ports, 177 final SocketFactory socketFactory) 178 { 179 this(addresses, ports, socketFactory, null); 180 } 181 182 183 184 /** 185 * Creates a new fastest connect server set with the specified set of 186 * directory server addresses and port numbers. It will use the provided 187 * socket factory to create the underlying sockets. 188 * 189 * @param addresses The addresses of the directory servers to which 190 * the connections should be established. It must 191 * not be {@code null} or empty. 192 * @param ports The ports of the directory servers to which the 193 * connections should be established. It must not 194 * be {@code null}, and it must have the same 195 * number of elements as the {@code addresses} 196 * array. The order of elements in the 197 * {@code addresses} array must correspond to the 198 * order of elements in the {@code ports} array. 199 * @param socketFactory The socket factory to use to create the 200 * underlying connections. 201 * @param connectionOptions The set of connection options to use for the 202 * underlying connections. 203 */ 204 public FastestConnectServerSet(final String[] addresses, final int[] ports, 205 final SocketFactory socketFactory, 206 final LDAPConnectionOptions connectionOptions) 207 { 208 Validator.ensureNotNull(addresses, ports); 209 Validator.ensureTrue(addresses.length > 0, 210 "RoundRobinServerSet.addresses must not be empty."); 211 Validator.ensureTrue(addresses.length == ports.length, 212 "RoundRobinServerSet addresses and ports arrays must be the same " + 213 "size."); 214 215 this.addresses = addresses; 216 this.ports = ports; 217 218 if (socketFactory == null) 219 { 220 this.socketFactory = SocketFactory.getDefault(); 221 } 222 else 223 { 224 this.socketFactory = socketFactory; 225 } 226 227 if (connectionOptions == null) 228 { 229 this.connectionOptions = new LDAPConnectionOptions(); 230 } 231 else 232 { 233 this.connectionOptions = connectionOptions; 234 } 235 } 236 237 238 239 /** 240 * Retrieves the addresses of the directory servers to which the connections 241 * should be established. 242 * 243 * @return The addresses of the directory servers to which the connections 244 * should be established. 245 */ 246 public String[] getAddresses() 247 { 248 return addresses; 249 } 250 251 252 253 /** 254 * Retrieves the ports of the directory servers to which the connections 255 * should be established. 256 * 257 * @return The ports of the directory servers to which the connections should 258 * be established. 259 */ 260 public int[] getPorts() 261 { 262 return ports; 263 } 264 265 266 267 /** 268 * Retrieves the socket factory that will be used to establish connections. 269 * 270 * @return The socket factory that will be used to establish connections. 271 */ 272 public SocketFactory getSocketFactory() 273 { 274 return socketFactory; 275 } 276 277 278 279 /** 280 * Retrieves the set of connection options that will be used for underlying 281 * connections. 282 * 283 * @return The set of connection options that will be used for underlying 284 * connections. 285 */ 286 public LDAPConnectionOptions getConnectionOptions() 287 { 288 return connectionOptions; 289 } 290 291 292 293 /** 294 * {@inheritDoc} 295 */ 296 @Override() 297 public LDAPConnection getConnection() 298 throws LDAPException 299 { 300 return getConnection(null); 301 } 302 303 304 305 /** 306 * {@inheritDoc} 307 */ 308 @Override() 309 public LDAPConnection getConnection( 310 final LDAPConnectionPoolHealthCheck healthCheck) 311 throws LDAPException 312 { 313 if (! connectionOptions.allowConcurrentSocketFactoryUse()) 314 { 315 throw new LDAPException(ResultCode.CONNECT_ERROR, 316 ERR_FASTEST_CONNECT_SET_OPTIONS_NOT_PARALLEL.get()); 317 } 318 319 final ArrayBlockingQueue<Object> resultQueue = 320 new ArrayBlockingQueue<Object>(addresses.length, false); 321 final AtomicBoolean connectionSelected = new AtomicBoolean(false); 322 323 final FastestConnectThread[] connectThreads = 324 new FastestConnectThread[addresses.length]; 325 for (int i=0; i < connectThreads.length; i++) 326 { 327 connectThreads[i] = new FastestConnectThread(addresses[i], ports[i], 328 socketFactory, connectionOptions, healthCheck, resultQueue, 329 connectionSelected); 330 } 331 332 for (final FastestConnectThread t : connectThreads) 333 { 334 t.start(); 335 } 336 337 try 338 { 339 final long effectiveConnectTimeout; 340 final long connectTimeout = 341 connectionOptions.getConnectTimeoutMillis(); 342 if ((connectTimeout > 0L) && (connectTimeout < Integer.MAX_VALUE)) 343 { 344 effectiveConnectTimeout = connectTimeout; 345 } 346 else 347 { 348 effectiveConnectTimeout = Integer.MAX_VALUE; 349 } 350 351 int connectFailures = 0; 352 final long stopWaitingTime = 353 System.currentTimeMillis() + effectiveConnectTimeout; 354 while (true) 355 { 356 final Object o; 357 final long waitTime = stopWaitingTime - System.currentTimeMillis(); 358 if (waitTime > 0L) 359 { 360 o = resultQueue.poll(waitTime, TimeUnit.MILLISECONDS); 361 } 362 else 363 { 364 o = resultQueue.poll(); 365 } 366 367 if (o == null) 368 { 369 throw new LDAPException(ResultCode.CONNECT_ERROR, 370 ERR_FASTEST_CONNECT_SET_CONNECT_TIMEOUT.get( 371 effectiveConnectTimeout)); 372 } 373 else if (o instanceof LDAPConnection) 374 { 375 return (LDAPConnection) o; 376 } 377 else 378 { 379 connectFailures++; 380 if (connectFailures >= addresses.length) 381 { 382 throw new LDAPException(ResultCode.CONNECT_ERROR, 383 ERR_FASTEST_CONNECT_SET_ALL_FAILED.get()); 384 } 385 } 386 } 387 } 388 catch (final LDAPException le) 389 { 390 Debug.debugException(le); 391 throw le; 392 } 393 catch (final Exception e) 394 { 395 Debug.debugException(e); 396 397 if (e instanceof InterruptedException) 398 { 399 Thread.currentThread().interrupt(); 400 } 401 402 throw new LDAPException(ResultCode.CONNECT_ERROR, 403 ERR_FASTEST_CONNECT_SET_CONNECT_EXCEPTION.get( 404 StaticUtils.getExceptionMessage(e)), 405 e); 406 } 407 } 408 409 410 411 /** 412 * {@inheritDoc} 413 */ 414 @Override() 415 public void toString(final StringBuilder buffer) 416 { 417 buffer.append("FastestConnectServerSet(servers={"); 418 419 for (int i=0; i < addresses.length; i++) 420 { 421 if (i > 0) 422 { 423 buffer.append(", "); 424 } 425 426 buffer.append(addresses[i]); 427 buffer.append(':'); 428 buffer.append(ports[i]); 429 } 430 431 buffer.append("})"); 432 } 433}