001/* 002 * Copyright 2008-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2019 Ping Identity Corporation 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 com.unboundid.util.Debug; 026import com.unboundid.util.Extensible; 027import com.unboundid.util.ThreadSafety; 028import com.unboundid.util.ThreadSafetyLevel; 029 030 031 032/** 033 * This class defines an API that can be used to select between multiple 034 * directory servers when establishing a connection. Implementations are free 035 * to use any kind of logic that they desire when selecting the server to which 036 * the connection is to be established. They may also support the use of 037 * health checks to determine whether the created connections are suitable for 038 * use. 039 * <BR><BR> 040 * Implementations MUST be threadsafe to allow for multiple concurrent attempts 041 * to establish new connections. 042 */ 043@Extensible() 044@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE) 045public abstract class ServerSet 046{ 047 /** 048 * Creates a new instance of this server set. 049 */ 050 protected ServerSet() 051 { 052 // No implementation is required. 053 } 054 055 056 057 /** 058 * Indicates whether connections created by this server set will be 059 * authenticated. 060 * 061 * @return {@code true} if connections created by this server set will be 062 * authenticated, or {@code false} if not. 063 */ 064 public boolean includesAuthentication() 065 { 066 return false; 067 } 068 069 070 071 /** 072 * Indicates whether connections created by this server set will have 073 * post-connect processing performed. 074 * 075 * @return {@code true} if connections created by this server set will have 076 * post-connect processing performed, or {@code false} if not. 077 */ 078 public boolean includesPostConnectProcessing() 079 { 080 return false; 081 } 082 083 084 085 /** 086 * Attempts to establish a connection to one of the directory servers in this 087 * server set. The connection that is returned must be established. The 088 * {@link #includesAuthentication()} must return true if and only if the 089 * connection will also be authenticated, and the 090 * {@link #includesPostConnectProcessing()} method must return true if and 091 * only if pre-authentication and post-authentication post-connect processing 092 * will have been performed. The caller may determine the server to which the 093 * connection is established using the 094 * {@link LDAPConnection#getConnectedAddress} and 095 * {@link LDAPConnection#getConnectedPort} methods. 096 * 097 * @return An {@code LDAPConnection} object that is established to one of the 098 * servers in this server set. 099 * 100 * @throws LDAPException If it is not possible to establish a connection to 101 * any of the servers in this server set. 102 */ 103 public abstract LDAPConnection getConnection() 104 throws LDAPException; 105 106 107 108 /** 109 * Attempts to establish a connection to one of the directory servers in this 110 * server set, using the provided health check to further validate the 111 * connection. The connection that is returned must be established. The 112 * {@link #includesAuthentication()} must return true if and only if the 113 * connection will also be authenticated, and the 114 * {@link #includesPostConnectProcessing()} method must return true if and 115 * only if pre-authentication and post-authentication post-connect processing 116 * will have been performed. The caller may determine the server to which the 117 * connection is established using the 118 * {@link LDAPConnection#getConnectedAddress} and 119 * {@link LDAPConnection#getConnectedPort} methods. 120 * 121 * @param healthCheck The health check to use to verify the health of the 122 * newly-created connection. It may be {@code null} if 123 * no additional health check should be performed. If it 124 * is non-{@code null} and this server set performs 125 * authentication, then the health check's 126 * {@code ensureConnectionValidAfterAuthentication} 127 * method will be invoked immediately after the bind 128 * operation is processed (regardless of whether the bind 129 * was successful or not). And regardless of whether 130 * this server set performs authentication, the 131 * health check's {@code ensureNewConnectionValid} 132 * method must be invoked on the connection to ensure 133 * that it is valid immediately before it is returned. 134 * 135 * @return An {@code LDAPConnection} object that is established to one of the 136 * servers in this server set. 137 * 138 * @throws LDAPException If it is not possible to establish a connection to 139 * any of the servers in this server set. 140 */ 141 public LDAPConnection getConnection( 142 final LDAPConnectionPoolHealthCheck healthCheck) 143 throws LDAPException 144 { 145 final LDAPConnection c = getConnection(); 146 147 if (healthCheck != null) 148 { 149 try 150 { 151 healthCheck.ensureNewConnectionValid(c); 152 } 153 catch (final LDAPException le) 154 { 155 Debug.debugException(le); 156 c.close(); 157 throw le; 158 } 159 } 160 161 return c; 162 } 163 164 165 166 /** 167 * Performs the appropriate bind, post-connect, and health check processing 168 * for the provided connection, in the provided order. The processing 169 * performed will include: 170 * <OL> 171 * <LI> 172 * If the provided {@code postConnectProcessor} is not {@code null}, then 173 * invoke its {@code processPreAuthenticatedConnection} method on the 174 * provided connection. If this method throws an {@code LDAPException}, 175 * then it will propagated up to the caller of this method. 176 * </LI> 177 * <LI> 178 * If the provided {@code bindRequest} is not {@code null}, then 179 * authenticate the connection using that request. If the provided 180 * {@code healthCheck} is also not {@code null}, then invoke its 181 * {@code ensureConnectionValidAfterAuthentication} method on the 182 * connection, even if the bind was not successful. If the health check 183 * throws an {@code LDAPException}, then it will be propagated up to the 184 * caller of this method. If there is no health check or if it did not 185 * throw an exception but the bind attempt did throw an exception, then 186 * propagate that exception instead. 187 * </LI> 188 * <LI> 189 * If the provided {@code postConnectProcessor} is not {@code null}, then 190 * invoke its {@code processPostAuthenticatedConnection} method on the 191 * provided connection. If this method throws an {@code LDAPException}, 192 * then it will propagated up to the caller of this method. 193 * </LI> 194 * <LI> 195 * If the provided {@code healthCheck} is not {@code null}, then invoke 196 * its {@code ensureNewConnectionValid} method on the provided connection. 197 * If this method throws an {@code LDAPException}, then it will be 198 * propagated up to the caller of this method. 199 * </LI> 200 * </OL> 201 * 202 * @param connection The connection to be processed. It must not 203 * be {@code null}, and it must be established. 204 * Note that if an {@code LDAPException} is 205 * thrown by this method or anything that it 206 * calls, then the connection will have been 207 * closed before that exception has been 208 * propagated up to the caller of this method. 209 * @param bindRequest The bind request to use to authenticate the 210 * connection. It may be {@code null} if no 211 * authentication should be performed. 212 * @param postConnectProcessor The post-connect processor to invoke on the 213 * provided connection. It may be {@code null} 214 * if no post-connect processing should be 215 * performed. 216 * @param healthCheck The health check to use to verify the health 217 * of connection. It may be {@code null} if no 218 * health check processing should be performed. 219 * 220 * @throws LDAPException If a problem is encountered during any of the 221 * processing performed by this method. If an 222 * exception is thrown, then the provided connection 223 * will have been closed. 224 */ 225 protected static void doBindPostConnectAndHealthCheckProcessing( 226 final LDAPConnection connection, 227 final BindRequest bindRequest, 228 final PostConnectProcessor postConnectProcessor, 229 final LDAPConnectionPoolHealthCheck healthCheck) 230 throws LDAPException 231 { 232 try 233 { 234 if (postConnectProcessor != null) 235 { 236 postConnectProcessor.processPreAuthenticatedConnection(connection); 237 } 238 239 if (bindRequest != null) 240 { 241 BindResult bindResult; 242 LDAPException bindException = null; 243 try 244 { 245 bindResult = connection.bind(bindRequest.duplicate()); 246 } 247 catch (final LDAPException le) 248 { 249 Debug.debugException(le); 250 bindException = le; 251 bindResult = new BindResult(le); 252 } 253 254 if (healthCheck != null) 255 { 256 healthCheck.ensureConnectionValidAfterAuthentication(connection, 257 bindResult); 258 } 259 260 if (bindException != null) 261 { 262 throw bindException; 263 } 264 } 265 266 if (postConnectProcessor != null) 267 { 268 postConnectProcessor.processPostAuthenticatedConnection(connection); 269 } 270 271 if (healthCheck != null) 272 { 273 healthCheck.ensureNewConnectionValid(connection); 274 } 275 } 276 catch (final LDAPException le) 277 { 278 Debug.debugException(le); 279 connection.closeWithoutUnbind(); 280 throw le; 281 } 282 } 283 284 285 286 /** 287 * Retrieves a string representation of this server set. 288 * 289 * @return A string representation of this server set. 290 */ 291 @Override() 292 public String toString() 293 { 294 final StringBuilder buffer = new StringBuilder(); 295 toString(buffer); 296 return buffer.toString(); 297 } 298 299 300 301 /** 302 * Appends a string representation of this server set to the provided buffer. 303 * 304 * @param buffer The buffer to which the string representation should be 305 * appended. 306 */ 307 public void toString(final StringBuilder buffer) 308 { 309 buffer.append("ServerSet(className="); 310 buffer.append(getClass().getName()); 311 buffer.append(')'); 312 } 313}