001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.directory.ldap.client.api; 021 022 023import static org.apache.directory.api.ldap.model.message.ResultCodeEnum.processResponse; 024 025import java.io.File; 026import java.io.IOException; 027import java.io.OutputStreamWriter; 028import java.io.Writer; 029import java.net.ConnectException; 030import java.net.InetSocketAddress; 031import java.net.SocketAddress; 032import java.nio.channels.UnresolvedAddressException; 033import java.nio.charset.Charset; 034import java.nio.file.Files; 035import java.nio.file.Paths; 036import java.security.PrivilegedExceptionAction; 037import java.util.ArrayList; 038import java.util.HashMap; 039import java.util.Iterator; 040import java.util.List; 041import java.util.Map; 042import java.util.concurrent.ConcurrentHashMap; 043import java.util.concurrent.TimeUnit; 044import java.util.concurrent.atomic.AtomicBoolean; 045import java.util.concurrent.locks.ReentrantLock; 046 047import javax.net.ssl.SSLContext; 048import javax.net.ssl.TrustManager; 049import javax.security.auth.Subject; 050import javax.security.auth.login.Configuration; 051import javax.security.auth.login.LoginContext; 052import javax.security.sasl.Sasl; 053import javax.security.sasl.SaslClient; 054 055import org.apache.directory.api.asn1.DecoderException; 056import org.apache.directory.api.asn1.util.Oid; 057import org.apache.directory.api.i18n.I18n; 058import org.apache.directory.api.ldap.codec.api.BinaryAttributeDetector; 059import org.apache.directory.api.ldap.codec.api.DefaultConfigurableBinaryAttributeDetector; 060import org.apache.directory.api.ldap.codec.api.ExtendedOperationFactory; 061import org.apache.directory.api.ldap.codec.api.LdapApiService; 062import org.apache.directory.api.ldap.codec.api.LdapApiServiceFactory; 063import org.apache.directory.api.ldap.codec.api.LdapDecoder; 064import org.apache.directory.api.ldap.codec.api.LdapMessageContainer; 065import org.apache.directory.api.ldap.codec.api.MessageEncoderException; 066import org.apache.directory.api.ldap.codec.api.SchemaBinaryAttributeDetector; 067import org.apache.directory.api.ldap.extras.extended.startTls.StartTlsRequestImpl; 068import org.apache.directory.api.ldap.model.constants.LdapConstants; 069import org.apache.directory.api.ldap.model.constants.SchemaConstants; 070import org.apache.directory.api.ldap.model.cursor.Cursor; 071import org.apache.directory.api.ldap.model.cursor.CursorException; 072import org.apache.directory.api.ldap.model.cursor.EntryCursor; 073import org.apache.directory.api.ldap.model.cursor.SearchCursor; 074import org.apache.directory.api.ldap.model.entry.Attribute; 075import org.apache.directory.api.ldap.model.entry.DefaultEntry; 076import org.apache.directory.api.ldap.model.entry.Entry; 077import org.apache.directory.api.ldap.model.entry.Modification; 078import org.apache.directory.api.ldap.model.entry.ModificationOperation; 079import org.apache.directory.api.ldap.model.entry.Value; 080import org.apache.directory.api.ldap.model.exception.LdapAuthenticationException; 081import org.apache.directory.api.ldap.model.exception.LdapException; 082import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException; 083import org.apache.directory.api.ldap.model.exception.LdapNoPermissionException; 084import org.apache.directory.api.ldap.model.exception.LdapNoSuchObjectException; 085import org.apache.directory.api.ldap.model.exception.LdapOperationException; 086import org.apache.directory.api.ldap.model.exception.LdapOtherException; 087import org.apache.directory.api.ldap.model.exception.LdapTlsHandshakeException; 088import org.apache.directory.api.ldap.model.message.AbandonRequest; 089import org.apache.directory.api.ldap.model.message.AbandonRequestImpl; 090import org.apache.directory.api.ldap.model.message.AddRequest; 091import org.apache.directory.api.ldap.model.message.AddRequestImpl; 092import org.apache.directory.api.ldap.model.message.AddResponse; 093import org.apache.directory.api.ldap.model.message.AliasDerefMode; 094import org.apache.directory.api.ldap.model.message.BindRequest; 095import org.apache.directory.api.ldap.model.message.BindRequestImpl; 096import org.apache.directory.api.ldap.model.message.BindResponse; 097import org.apache.directory.api.ldap.model.message.CompareRequest; 098import org.apache.directory.api.ldap.model.message.CompareRequestImpl; 099import org.apache.directory.api.ldap.model.message.CompareResponse; 100import org.apache.directory.api.ldap.model.message.Control; 101import org.apache.directory.api.ldap.model.message.DeleteRequest; 102import org.apache.directory.api.ldap.model.message.DeleteRequestImpl; 103import org.apache.directory.api.ldap.model.message.DeleteResponse; 104import org.apache.directory.api.ldap.model.message.ExtendedRequest; 105import org.apache.directory.api.ldap.model.message.ExtendedResponse; 106import org.apache.directory.api.ldap.model.message.IntermediateResponse; 107import org.apache.directory.api.ldap.model.message.LdapResult; 108import org.apache.directory.api.ldap.model.message.Message; 109import org.apache.directory.api.ldap.model.message.ModifyDnRequest; 110import org.apache.directory.api.ldap.model.message.ModifyDnRequestImpl; 111import org.apache.directory.api.ldap.model.message.ModifyDnResponse; 112import org.apache.directory.api.ldap.model.message.ModifyRequest; 113import org.apache.directory.api.ldap.model.message.ModifyRequestImpl; 114import org.apache.directory.api.ldap.model.message.ModifyResponse; 115import org.apache.directory.api.ldap.model.message.OpaqueExtendedRequest; 116import org.apache.directory.api.ldap.model.message.OpaqueExtendedResponse; 117import org.apache.directory.api.ldap.model.message.Request; 118import org.apache.directory.api.ldap.model.message.Response; 119import org.apache.directory.api.ldap.model.message.ResultCodeEnum; 120import org.apache.directory.api.ldap.model.message.SearchRequest; 121import org.apache.directory.api.ldap.model.message.SearchRequestImpl; 122import org.apache.directory.api.ldap.model.message.SearchResultDone; 123import org.apache.directory.api.ldap.model.message.SearchResultEntry; 124import org.apache.directory.api.ldap.model.message.SearchResultReference; 125import org.apache.directory.api.ldap.model.message.SearchScope; 126import org.apache.directory.api.ldap.model.message.UnbindRequest; 127import org.apache.directory.api.ldap.model.message.UnbindRequestImpl; 128import org.apache.directory.api.ldap.model.message.controls.ManageDsaITImpl; 129import org.apache.directory.api.ldap.model.message.controls.OpaqueControl; 130import org.apache.directory.api.ldap.model.message.extended.AddNoDResponse; 131import org.apache.directory.api.ldap.model.message.extended.BindNoDResponse; 132import org.apache.directory.api.ldap.model.message.extended.CompareNoDResponse; 133import org.apache.directory.api.ldap.model.message.extended.DeleteNoDResponse; 134import org.apache.directory.api.ldap.model.message.extended.ExtendedNoDResponse; 135import org.apache.directory.api.ldap.model.message.extended.ModifyDnNoDResponse; 136import org.apache.directory.api.ldap.model.message.extended.ModifyNoDResponse; 137import org.apache.directory.api.ldap.model.message.extended.NoticeOfDisconnect; 138import org.apache.directory.api.ldap.model.message.extended.SearchNoDResponse; 139import org.apache.directory.api.ldap.model.name.Dn; 140import org.apache.directory.api.ldap.model.name.Rdn; 141import org.apache.directory.api.ldap.model.schema.AttributeType; 142import org.apache.directory.api.ldap.model.schema.ObjectClass; 143import org.apache.directory.api.ldap.model.schema.SchemaManager; 144import org.apache.directory.api.ldap.model.schema.parsers.OpenLdapSchemaParser; 145import org.apache.directory.api.ldap.model.schema.registries.Registries; 146import org.apache.directory.api.ldap.model.schema.registries.SchemaLoader; 147import org.apache.directory.api.ldap.schema.manager.impl.DefaultSchemaManager; 148import org.apache.directory.api.util.Network; 149import org.apache.directory.api.util.StringConstants; 150import org.apache.directory.api.util.Strings; 151import org.apache.directory.ldap.client.api.callback.SaslCallbackHandler; 152import org.apache.directory.ldap.client.api.exception.InvalidConnectionException; 153import org.apache.directory.ldap.client.api.future.AddFuture; 154import org.apache.directory.ldap.client.api.future.BindFuture; 155import org.apache.directory.ldap.client.api.future.CompareFuture; 156import org.apache.directory.ldap.client.api.future.DeleteFuture; 157import org.apache.directory.ldap.client.api.future.ExtendedFuture; 158import org.apache.directory.ldap.client.api.future.HandshakeFuture; 159import org.apache.directory.ldap.client.api.future.ModifyDnFuture; 160import org.apache.directory.ldap.client.api.future.ModifyFuture; 161import org.apache.directory.ldap.client.api.future.ResponseFuture; 162import org.apache.directory.ldap.client.api.future.SearchFuture; 163import org.apache.mina.core.filterchain.IoFilter; 164import org.apache.mina.core.future.CloseFuture; 165import org.apache.mina.core.future.ConnectFuture; 166import org.apache.mina.core.future.WriteFuture; 167import org.apache.mina.core.service.IoConnector; 168import org.apache.mina.core.session.IoSession; 169import org.apache.mina.filter.FilterEvent; 170import org.apache.mina.filter.codec.ProtocolCodecFilter; 171import org.apache.mina.filter.codec.ProtocolEncoderException; 172import org.apache.mina.filter.ssl.SslEvent; 173import org.apache.mina.filter.ssl.SslFilter; 174import org.apache.mina.transport.socket.SocketSessionConfig; 175import org.apache.mina.transport.socket.nio.NioSocketConnector; 176import org.slf4j.Logger; 177import org.slf4j.LoggerFactory; 178 179 180/** 181 * This class is the base for every operations sent or received to and 182 * from a LDAP server. 183 * 184 * A connection instance is necessary to send requests to the server. The connection 185 * is valid until either the client closes it, the server closes it or the 186 * client does an unbind. 187 * 188 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 189 */ 190public class LdapNetworkConnection extends AbstractLdapConnection implements LdapAsyncConnection 191{ 192 /** logger for reporting errors that might not be handled properly upstream */ 193 private static final Logger LOG = LoggerFactory.getLogger( LdapNetworkConnection.class ); 194 195 /** The timeout used for response we are waiting for */ 196 private long timeout = LdapConnectionConfig.DEFAULT_TIMEOUT; 197 198 /** configuration object for the connection */ 199 private LdapConnectionConfig config; 200 201 /** The Socket configuration */ 202 private SocketSessionConfig socketSessionConfig; 203 204 /** The connector open with the remote server */ 205 private IoConnector connector; 206 207 /** A mutex used to avoid a double close of the connector */ 208 private ReentrantLock connectorMutex = new ReentrantLock(); 209 210 /** 211 * The created session, created when we open a connection with 212 * the Ldap server. 213 */ 214 private IoSession ldapSession; 215 216 /** a map to hold the ResponseFutures for all operations */ 217 private Map<Integer, ResponseFuture<? extends Response>> futureMap = new ConcurrentHashMap<>(); 218 219 /** list of controls supported by the server */ 220 private List<String> supportedControls; 221 222 /** The ROOT DSE entry */ 223 private Entry rootDse; 224 225 /** A flag indicating that the BindRequest has been issued and successfully authenticated the user */ 226 private AtomicBoolean authenticated = new AtomicBoolean( false ); 227 228 /** A flag indicating that the connection is connected or not */ 229 private AtomicBoolean connected = new AtomicBoolean( false ); 230 231 /** a list of listeners interested in getting notified when the 232 * connection's session gets closed cause of network issues 233 */ 234 private List<ConnectionClosedEventListener> conCloseListeners; 235 236 /** The Ldap codec protocol filter */ 237 private IoFilter ldapProtocolFilter = new ProtocolCodecFilter( codec.getProtocolCodecFactory() ); 238 239 /** the SslFilter key */ 240 private static final String SSL_FILTER_KEY = "sslFilter"; 241 242 /** The exception stored in the session if we've got one */ 243 private static final String EXCEPTION_KEY = "sessionException"; 244 245 /** The krb5 configuration property */ 246 private static final String KRB5_CONF = "java.security.krb5.conf"; 247 248 /** A future used to block any action until the handhake is completed */ 249 private HandshakeFuture handshakeFuture; 250 251 // ~~~~~~~~~~~~~~~~~ common error messages ~~~~~~~~~~~~~~~~~~~~~~~~~~ 252 static final String TIME_OUT_ERROR = I18n.err( I18n.ERR_04170_TIMEOUT_OCCURED ); 253 254 static final String NO_RESPONSE_ERROR = I18n.err( I18n.ERR_04169_RESPONSE_QUEUE_EMPTIED ); 255 256 //------------------------- The constructors --------------------------// 257 /** 258 * Create a new instance of a LdapConnection on localhost, 259 * port 389. 260 */ 261 public LdapNetworkConnection() 262 { 263 this( null, -1, false ); 264 } 265 266 267 /** 268 * 269 * Creates a new instance of LdapConnection with the given connection configuration. 270 * 271 * @param config the configuration of the LdapConnection 272 */ 273 public LdapNetworkConnection( LdapConnectionConfig config ) 274 { 275 this( config, LdapApiServiceFactory.getSingleton() ); 276 } 277 278 279 /** 280 * Creates a new LdapNetworkConnection instance 281 * 282 * @param config The configuration to use 283 * @param ldapApiService The LDAP API Service to use 284 */ 285 public LdapNetworkConnection( LdapConnectionConfig config, LdapApiService ldapApiService ) 286 { 287 super( ldapApiService ); 288 this.config = config; 289 290 if ( config.getBinaryAttributeDetector() == null ) 291 { 292 config.setBinaryAttributeDetector( new DefaultConfigurableBinaryAttributeDetector() ); 293 } 294 295 this.timeout = config.getTimeout(); 296 } 297 298 299 /** 300 * Create a new instance of a LdapConnection on localhost, 301 * port 389 if the SSL flag is off, or 636 otherwise. 302 * 303 * @param useSsl A flag to tell if it's a SSL connection or not. 304 */ 305 public LdapNetworkConnection( boolean useSsl ) 306 { 307 this( null, -1, useSsl ); 308 } 309 310 311 /** 312 * Creates a new LdapNetworkConnection instance 313 * 314 * @param useSsl If we are going to create a secure connection or not 315 * @param ldapApiService The LDAP API Service to use 316 */ 317 public LdapNetworkConnection( boolean useSsl, LdapApiService ldapApiService ) 318 { 319 this( null, -1, useSsl, ldapApiService ); 320 } 321 322 323 /** 324 * Create a new instance of a LdapConnection on a given 325 * server, using the default port (389). 326 * 327 * @param server The server we want to be connected to. If null or empty, 328 * we will default to LocalHost. 329 */ 330 public LdapNetworkConnection( String server ) 331 { 332 this( server, -1, false ); 333 } 334 335 336 /** 337 * Creates a new LdapNetworkConnection instance 338 * 339 * @param server The server we want to be connected to. If null or empty, 340 * we will default to LocalHost. 341 * @param ldapApiService The LDAP API Service to use 342 */ 343 public LdapNetworkConnection( String server, LdapApiService ldapApiService ) 344 { 345 this( server, -1, false, ldapApiService ); 346 } 347 348 349 /** 350 * Create a new instance of a LdapConnection on a given 351 * server, using the default port (389) if the SSL flag 352 * is off, or 636 otherwise. 353 * 354 * @param server The server we want to be connected to. If null or empty, 355 * we will default to LocalHost. 356 * @param useSsl A flag to tell if it's a SSL connection or not. 357 */ 358 public LdapNetworkConnection( String server, boolean useSsl ) 359 { 360 this( server, -1, useSsl ); 361 } 362 363 364 /** 365 * Creates a new LdapNetworkConnection instance 366 * 367 * @param server The server we want to be connected to. If null or empty, 368 * we will default to LocalHost. 369 * @param useSsl A flag to tell if it's a SSL connection or not. 370 * @param ldapApiService The LDAP API Service to use 371 */ 372 public LdapNetworkConnection( String server, boolean useSsl, LdapApiService ldapApiService ) 373 { 374 this( server, -1, useSsl, ldapApiService ); 375 } 376 377 378 /** 379 * Create a new instance of a LdapConnection on a 380 * given server and a given port. We don't use ssl. 381 * 382 * @param server The server we want to be connected to 383 * @param port The port the server is listening to 384 */ 385 public LdapNetworkConnection( String server, int port ) 386 { 387 this( server, port, false ); 388 } 389 390 391 /** 392 * Create a new instance of a LdapConnection on a 393 * given server and a given port. We don't use ssl. 394 * 395 * @param server The server we want to be connected to. If null or empty, 396 * we will default to LocalHost. 397 * @param port The port the server is listening on 398 * @param ldapApiService The LDAP API Service to use 399 */ 400 public LdapNetworkConnection( String server, int port, LdapApiService ldapApiService ) 401 { 402 this( server, port, false, ldapApiService ); 403 } 404 405 406 /** 407 * Create a new instance of a LdapConnection on a given 408 * server, and a give port. We set the SSL flag accordingly 409 * to the last parameter. 410 * 411 * @param server The server we want to be connected to. If null or empty, 412 * we will default to LocalHost. 413 * @param port The port the server is listening to 414 * @param useSsl A flag to tell if it's a SSL connection or not. 415 */ 416 public LdapNetworkConnection( String server, int port, boolean useSsl ) 417 { 418 this( buildConfig( server, port, useSsl ) ); 419 } 420 421 422 /** 423 * Create a new instance of a LdapConnection on a given 424 * server, and a give port. This SSL connection will use the provided 425 * TrustManagers 426 * 427 * @param server The server we want to be connected to. If null or empty, 428 * we will default to LocalHost. 429 * @param port The port the server is listening to 430 * @param trustManagers The TrustManager to use 431 */ 432 public LdapNetworkConnection( String server, int port, TrustManager... trustManagers ) 433 { 434 this( buildConfig( server, port, true ) ); 435 436 config.setTrustManagers( trustManagers ); 437 } 438 439 440 /** 441 * Create a new instance of a LdapConnection on a 442 * given server and a given port. We don't use ssl. 443 * 444 * @param server The server we want to be connected to. If null or empty, 445 * we will default to LocalHost. 446 * @param port The port the server is listening on 447 * @param useSsl A flag to tell if it's a SSL connection or not. 448 * @param ldapApiService The LDAP API Service to use 449 */ 450 public LdapNetworkConnection( String server, int port, boolean useSsl, LdapApiService ldapApiService ) 451 { 452 this( buildConfig( server, port, useSsl ), ldapApiService ); 453 } 454 455 456 private static LdapConnectionConfig buildConfig( String server, int port, boolean useSsl ) 457 { 458 LdapConnectionConfig config = new LdapConnectionConfig(); 459 config.setUseSsl( useSsl ); 460 461 if ( port != -1 ) 462 { 463 config.setLdapPort( port ); 464 } 465 else 466 { 467 if ( useSsl ) 468 { 469 config.setLdapPort( config.getDefaultLdapsPort() ); 470 } 471 else 472 { 473 config.setLdapPort( config.getDefaultLdapPort() ); 474 } 475 } 476 477 // Default to localhost if null 478 if ( Strings.isEmpty( server ) ) 479 { 480 config.setLdapHost( Network.LOOPBACK_HOSTNAME ); 481 482 } 483 else 484 { 485 config.setLdapHost( server ); 486 } 487 488 config.setBinaryAttributeDetector( new DefaultConfigurableBinaryAttributeDetector() ); 489 490 return config; 491 } 492 493 494 /** 495 * Create the connector 496 * 497 * @throws LdapException If the connector can't be created 498 */ 499 private void createConnector() throws LdapException 500 { 501 // Use only one thread inside the connector 502 connector = new NioSocketConnector( 1 ); 503 504 if ( socketSessionConfig != null ) 505 { 506 ( ( SocketSessionConfig ) connector.getSessionConfig() ).setAll( socketSessionConfig ); 507 } 508 else 509 { 510 ( ( SocketSessionConfig ) connector.getSessionConfig() ).setReuseAddress( true ); 511 } 512 513 // Add the codec to the chain 514 connector.getFilterChain().addLast( "ldapCodec", ldapProtocolFilter ); 515 516 // If we use SSL, we have to add the SslFilter to the chain 517 if ( config.isUseSsl() ) 518 { 519 addSslFilter(); 520 } 521 522 // Inject the protocolHandler 523 connector.setHandler( this ); 524 } 525 526 527 //--------------------------- Helper methods ---------------------------// 528 /** 529 * {@inheritDoc} 530 */ 531 @Override 532 public boolean isConnected() 533 { 534 return ( ldapSession != null ) && connected.get() && !ldapSession.isClosing(); 535 } 536 537 538 /** 539 * {@inheritDoc} 540 */ 541 @Override 542 public boolean isAuthenticated() 543 { 544 return isConnected() && authenticated.get(); 545 } 546 547 548 /** 549 * Tells if the connection is using a secured channel 550 * 551 * @return <tt>true</tt> if the session is using a secured channel 552 */ 553 public boolean isSecured() 554 { 555 return isConnected() && ldapSession.isSecured(); 556 } 557 558 559 /** 560 * Check that a session is valid, ie we can send requests to the 561 * server 562 * 563 * @throws InvalidConnectionException If the session is not valid 564 */ 565 private void checkSession() throws InvalidConnectionException 566 { 567 if ( ldapSession == null ) 568 { 569 throw new InvalidConnectionException( I18n.err( I18n.ERR_04104_NULL_CONNECTION_CANNOT_CONNECT ) ); 570 } 571 572 if ( !connected.get() ) 573 { 574 throw new InvalidConnectionException( I18n.err( I18n.ERR_04108_INVALID_CONNECTION ) ); 575 } 576 } 577 578 579 private void addToFutureMap( int messageId, ResponseFuture<? extends Response> future ) 580 { 581 if ( LOG.isDebugEnabled() ) 582 { 583 LOG.debug( I18n.msg( I18n.MSG_04106_ADDING, messageId, future.getClass().getName() ) ); 584 } 585 586 futureMap.put( messageId, future ); 587 } 588 589 590 private ResponseFuture<? extends Response> getFromFutureMap( int messageId ) 591 { 592 ResponseFuture<? extends Response> future = futureMap.remove( messageId ); 593 594 if ( LOG.isDebugEnabled() && ( future != null ) ) 595 { 596 LOG.debug( I18n.msg( I18n.MSG_04126_REMOVING, messageId, future.getClass().getName() ) ); 597 } 598 599 return future; 600 } 601 602 603 private ResponseFuture<? extends Response> peekFromFutureMap( int messageId ) 604 { 605 ResponseFuture<? extends Response> future = futureMap.get( messageId ); 606 607 // future can be null if there was a abandon operation on that messageId 608 if ( LOG.isDebugEnabled() && ( future != null ) ) 609 { 610 LOG.debug( I18n.msg( I18n.MSG_04119_GETTING, messageId, future.getClass().getName() ) ); 611 } 612 613 return future; 614 } 615 616 617 /** 618 * Get the largest timeout from the search time limit and the connection 619 * timeout. 620 * 621 * @param connectionTimoutInMS Connection timeout 622 * @param searchTimeLimitInSeconds Search timeout 623 * @return The largest timeout 624 */ 625 public long getTimeout( long connectionTimoutInMS, int searchTimeLimitInSeconds ) 626 { 627 if ( searchTimeLimitInSeconds < 0 ) 628 { 629 return connectionTimoutInMS; 630 } 631 else if ( searchTimeLimitInSeconds == 0 ) 632 { 633 if ( config.getTimeout() == 0 ) 634 { 635 return Long.MAX_VALUE; 636 } 637 else 638 { 639 return config.getTimeout(); 640 } 641 } 642 else 643 { 644 long searchTimeLimitInMS = searchTimeLimitInSeconds * 1000L; 645 return Math.max( searchTimeLimitInMS, connectionTimoutInMS ); 646 } 647 } 648 649 650 /** 651 * Process the connect. 652 */ 653 private ConnectFuture tryConnect() throws LdapException 654 { 655 // Build the connection address 656 SocketAddress address = new InetSocketAddress( config.getLdapHost(), config.getLdapPort() ); 657 long maxRetry = System.currentTimeMillis() + timeout; 658 ConnectFuture connectionFuture = connector.connect( address ); 659 boolean result = false; 660 661 while ( maxRetry > System.currentTimeMillis() ) 662 { 663 // Wait until it's established 664 try 665 { 666 result = connectionFuture.await( timeout ); 667 } 668 catch ( InterruptedException e ) 669 { 670 connector.dispose(); 671 connector = null; 672 673 if ( LOG.isDebugEnabled() ) 674 { 675 LOG.debug( I18n.msg( I18n.MSG_04120_INTERRUPTED_WAITING_FOR_CONNECTION, 676 config.getLdapHost(), 677 config.getLdapPort() ), e ); 678 } 679 680 throw new LdapOtherException( e.getMessage(), e ); 681 } 682 finally 683 { 684 if ( result ) 685 { 686 boolean isConnected = connectionFuture.isConnected(); 687 688 if ( !isConnected ) 689 { 690 Throwable connectionException = connectionFuture.getException(); 691 692 if ( LOG.isDebugEnabled() ) 693 { 694 if ( ( connectionException instanceof ConnectException ) 695 || ( connectionException instanceof UnresolvedAddressException ) ) 696 { 697 // No need to wait 698 // We know that there was a permanent error such as "connection refused". 699 LOG.debug( I18n.msg( I18n.MSG_04144_CONNECTION_ERROR, connectionFuture.getException().getMessage() ) ); 700 } 701 702 LOG.debug( I18n.msg( I18n.MSG_04143_CONNECTION_RETRYING ) ); 703 } 704 705 // Wait 500 ms and retry 706 try 707 { 708 Thread.sleep( 500 ); 709 } 710 catch ( InterruptedException e ) 711 { 712 connector = null; 713 714 if ( LOG.isDebugEnabled() ) 715 { 716 LOG.debug( I18n.msg( I18n.MSG_04120_INTERRUPTED_WAITING_FOR_CONNECTION, 717 config.getLdapHost(), 718 config.getLdapPort() ), e ); 719 } 720 721 throw new LdapOtherException( e.getMessage(), e ); 722 } 723 } 724 else 725 { 726 break; 727 } 728 } 729 } 730 } 731 732 if ( connectionFuture == null ) 733 { 734 connector.dispose(); 735 throw new InvalidConnectionException( I18n.err( I18n.ERR_04109_CANNOT_CONNECT ) ); 736 } 737 738 return connectionFuture; 739 } 740 741 742 /** 743 * Close the connection and generate the appropriate exception 744 */ 745 private void close( ConnectFuture connectionFuture ) throws LdapException 746 { 747 // disposing connector if not connected 748 close(); 749 750 Throwable e = connectionFuture.getException(); 751 752 if ( e != null ) 753 { 754 // Special case for UnresolvedAddressException 755 // (most of the time no message is associated with this exception) 756 if ( ( e instanceof UnresolvedAddressException ) && ( e.getMessage() == null ) ) 757 { 758 throw new InvalidConnectionException( I18n.err( I18n.ERR_04121_CANNOT_RESOLVE_HOSTNAME, config.getLdapHost() ), e ); 759 } 760 761 // Default case 762 throw new InvalidConnectionException( I18n.err( I18n.ERR_04110_CANNOT_CONNECT_TO_SERVER, e.getMessage() ), e ); 763 } 764 765 // We didn't received anything : this is an error 766 if ( LOG.isErrorEnabled() ) 767 { 768 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Connect" ) ); 769 } 770 771 throw new LdapException( TIME_OUT_ERROR ); 772 } 773 774 775 /** 776 * Verify that the connection has been secured, otherwise throw a meaningful exception 777 */ 778 private void checkSecured( ConnectFuture connectionFuture ) throws LdapException 779 { 780 try 781 { 782 boolean isSecured = handshakeFuture.get( timeout, TimeUnit.MILLISECONDS ); 783 784 if ( !isSecured ) 785 { 786 // check for a specific cause 787 Throwable cause = connectionFuture.getException(); 788 789 if ( cause == null && connectionFuture.getSession() != null ) 790 { 791 cause = ( Throwable ) connectionFuture.getSession().getAttribute( EXCEPTION_KEY ); 792 } 793 794 // if there is no cause assume timeout 795 if ( cause == null ) 796 { 797 throw new LdapException( TIME_OUT_ERROR ); 798 } 799 800 throw new LdapTlsHandshakeException( I18n.err( I18n.ERR_04120_TLS_HANDSHAKE_ERROR ), cause ); 801 } 802 } 803 catch ( Exception e ) 804 { 805 if ( e instanceof LdapException ) 806 { 807 throw ( LdapException ) e; 808 } 809 810 String msg = I18n.err( I18n.ERR_04122_SSL_CONTEXT_INIT_FAILURE ); 811 LOG.error( msg, e ); 812 throw new LdapException( msg, e ); 813 } 814 } 815 816 817 /** 818 * Set a listener associated to the closeFuture 819 */ 820 private void setCloseListener( ConnectFuture connectionFuture ) 821 { 822 // Get the close future for this session 823 CloseFuture closeFuture = connectionFuture.getSession().getCloseFuture(); 824 825 closeFuture.addListener( future -> 826 { 827 // Process all the waiting operations and cancel them 828 if ( LOG.isDebugEnabled() ) 829 { 830 LOG.debug( I18n.msg( I18n.MSG_04137_NOD_RECEIVED ) ); 831 } 832 833 for ( ResponseFuture<?> responseFuture : futureMap.values() ) 834 { 835 if ( LOG.isDebugEnabled() ) 836 { 837 LOG.debug( I18n.msg( I18n.MSG_04134_CLOSING, responseFuture ) ); 838 } 839 840 responseFuture.cancel(); 841 842 try 843 { 844 if ( responseFuture instanceof AddFuture ) 845 { 846 ( ( AddFuture ) responseFuture ).set( AddNoDResponse.PROTOCOLERROR ); 847 } 848 else if ( responseFuture instanceof BindFuture ) 849 { 850 ( ( BindFuture ) responseFuture ).set( BindNoDResponse.PROTOCOLERROR ); 851 } 852 else if ( responseFuture instanceof CompareFuture ) 853 { 854 ( ( CompareFuture ) responseFuture ).set( CompareNoDResponse.PROTOCOLERROR ); 855 } 856 else if ( responseFuture instanceof DeleteFuture ) 857 { 858 ( ( DeleteFuture ) responseFuture ).set( DeleteNoDResponse.PROTOCOLERROR ); 859 } 860 else if ( responseFuture instanceof ExtendedFuture ) 861 { 862 ( ( ExtendedFuture ) responseFuture ).set( ExtendedNoDResponse.PROTOCOLERROR ); 863 } 864 else if ( responseFuture instanceof ModifyFuture ) 865 { 866 ( ( ModifyFuture ) responseFuture ).set( ModifyNoDResponse.PROTOCOLERROR ); 867 } 868 else if ( responseFuture instanceof ModifyDnFuture ) 869 { 870 ( ( ModifyDnFuture ) responseFuture ).set( ModifyDnNoDResponse.PROTOCOLERROR ); 871 } 872 else if ( responseFuture instanceof SearchFuture ) 873 { 874 ( ( SearchFuture ) responseFuture ).set( SearchNoDResponse.PROTOCOLERROR ); 875 } 876 } 877 catch ( InterruptedException e ) 878 { 879 LOG.error( I18n.err( I18n.ERR_04113_ERROR_PROCESSING_NOD, responseFuture ), e ); 880 } 881 882 futureMap.remove( messageId.get() ); 883 } 884 885 futureMap.clear(); 886 } ); 887 } 888 889 890 /** 891 * Set the BinaryDetector instance in the session 892 */ 893 private void setBinaryDetector() 894 { 895 @SuppressWarnings("unchecked") 896 LdapMessageContainer<? extends Message> container = 897 ( LdapMessageContainer<? extends Message> ) ldapSession 898 .getAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR ); 899 900 if ( container != null ) 901 { 902 if ( ( schemaManager != null ) && !( container.getBinaryAttributeDetector() instanceof SchemaBinaryAttributeDetector ) ) 903 { 904 container.setBinaryAttributeDetector( new SchemaBinaryAttributeDetector( schemaManager ) ); 905 } 906 } 907 else 908 { 909 BinaryAttributeDetector atDetector = new DefaultConfigurableBinaryAttributeDetector(); 910 911 if ( schemaManager != null ) 912 { 913 atDetector = new SchemaBinaryAttributeDetector( schemaManager ); 914 } 915 916 ldapSession.setAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR, 917 new LdapMessageContainer<Message>( codec, atDetector ) ); 918 } 919 } 920 921 922 //-------------------------- The methods ---------------------------// 923 /** 924 * {@inheritDoc} 925 */ 926 @Override 927 public boolean connect() throws LdapException 928 { 929 if ( ( ldapSession != null ) && connected.get() ) 930 { 931 // No need to connect if we already have a connected session 932 return true; 933 } 934 935 // Create the connector if needed 936 if ( connector == null ) 937 { 938 createConnector(); 939 } 940 941 // And create the connection future 942 ConnectFuture connectionFuture = tryConnect(); 943 944 // Check if we are good to go 945 if ( !connectionFuture.isConnected() ) 946 { 947 close( connectionFuture ); 948 } 949 950 // Check if we are secured if requested 951 if ( config.isUseSsl() ) 952 { 953 checkSecured( connectionFuture ); 954 } 955 956 // Add a listener to close the session in the session. 957 setCloseListener( connectionFuture ); 958 959 // Get back the session 960 ldapSession = connectionFuture.getSession(); 961 962 // Store the container into the session if we don't have one 963 setBinaryDetector(); 964 965 // Initialize the MessageId 966 messageId.set( 0 ); 967 968 // And return 969 return true; 970 } 971 972 973 /** 974 * {@inheritDoc} 975 */ 976 @Override 977 public void close() 978 { 979 // Close the session 980 if ( ( ldapSession != null ) && connected.get() ) 981 { 982 ldapSession.closeNow(); 983 connected.set( false ); 984 } 985 986 // And close the connector if it has been created locally 987 // Release the connector 988 connectorMutex.lock(); 989 990 try 991 { 992 if ( connector != null ) 993 { 994 connector.dispose(); 995 connector = null; 996 } 997 } 998 finally 999 { 1000 connectorMutex.unlock(); 1001 } 1002 1003 // Reset the messageId 1004 messageId.set( 0 ); 1005 } 1006 1007 1008 //------------------------ The LDAP operations ------------------------// 1009 // Add operations // 1010 //---------------------------------------------------------------------// 1011 /** 1012 * {@inheritDoc} 1013 */ 1014 @Override 1015 public void add( Entry entry ) throws LdapException 1016 { 1017 if ( entry == null ) 1018 { 1019 String msg = I18n.err( I18n.ERR_04123_CANNOT_ADD_EMPTY_ENTRY ); 1020 1021 if ( LOG.isDebugEnabled() ) 1022 { 1023 LOG.debug( msg ); 1024 } 1025 1026 throw new IllegalArgumentException( msg ); 1027 } 1028 1029 AddRequest addRequest = new AddRequestImpl(); 1030 addRequest.setEntry( entry ); 1031 1032 AddResponse addResponse = add( addRequest ); 1033 1034 processResponse( addResponse ); 1035 } 1036 1037 1038 /** 1039 * {@inheritDoc} 1040 */ 1041 @Override 1042 public AddFuture addAsync( Entry entry ) throws LdapException 1043 { 1044 if ( entry == null ) 1045 { 1046 String msg = I18n.err( I18n.ERR_04125_CANNOT_ADD_NULL_ENTRY ); 1047 1048 if ( LOG.isDebugEnabled() ) 1049 { 1050 LOG.debug( msg ); 1051 } 1052 1053 throw new IllegalArgumentException( msg ); 1054 } 1055 1056 AddRequest addRequest = new AddRequestImpl(); 1057 addRequest.setEntry( entry ); 1058 1059 return addAsync( addRequest ); 1060 } 1061 1062 1063 /** 1064 * {@inheritDoc} 1065 */ 1066 @Override 1067 public AddResponse add( AddRequest addRequest ) throws LdapException 1068 { 1069 if ( addRequest == null ) 1070 { 1071 String msg = I18n.err( I18n.ERR_04124_CANNOT_PROCESS_NULL_ADD_REQUEST ); 1072 1073 if ( LOG.isDebugEnabled() ) 1074 { 1075 LOG.debug( msg ); 1076 } 1077 1078 throw new IllegalArgumentException( msg ); 1079 } 1080 1081 if ( addRequest.getEntry() == null ) 1082 { 1083 String msg = I18n.err( I18n.ERR_04125_CANNOT_ADD_NULL_ENTRY ); 1084 1085 if ( LOG.isDebugEnabled() ) 1086 { 1087 LOG.debug( msg ); 1088 } 1089 1090 throw new IllegalArgumentException( msg ); 1091 } 1092 1093 AddFuture addFuture = addAsync( addRequest ); 1094 1095 // Get the result from the future 1096 try 1097 { 1098 // Read the response, waiting for it if not available immediately 1099 // Get the response, blocking 1100 AddResponse addResponse = addFuture.get( timeout, TimeUnit.MILLISECONDS ); 1101 1102 if ( addResponse == null ) 1103 { 1104 // We didn't received anything : this is an error 1105 if ( LOG.isErrorEnabled() ) 1106 { 1107 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Add" ) ); 1108 } 1109 1110 throw new LdapException( TIME_OUT_ERROR ); 1111 } 1112 1113 if ( addResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 1114 { 1115 // Everything is fine, return the response 1116 if ( LOG.isDebugEnabled() ) 1117 { 1118 LOG.debug( I18n.msg( I18n.MSG_04108_ADD_SUCCESSFUL, addResponse ) ); 1119 } 1120 } 1121 else 1122 { 1123 // We have had an error 1124 if ( LOG.isDebugEnabled() ) 1125 { 1126 LOG.debug( I18n.msg( I18n.MSG_04107_ADD_FAILED, addResponse ) ); 1127 } 1128 } 1129 1130 return addResponse; 1131 } 1132 catch ( Exception ie ) 1133 { 1134 // Catch all other exceptions 1135 LOG.error( NO_RESPONSE_ERROR, ie ); 1136 1137 // Send an abandon request 1138 if ( !addFuture.isCancelled() ) 1139 { 1140 abandon( addRequest.getMessageId() ); 1141 } 1142 1143 throw new LdapException( NO_RESPONSE_ERROR, ie ); 1144 } 1145 } 1146 1147 1148 /** 1149 * {@inheritDoc} 1150 */ 1151 @Override 1152 public AddFuture addAsync( AddRequest addRequest ) throws LdapException 1153 { 1154 if ( addRequest == null ) 1155 { 1156 String msg = I18n.err( I18n.ERR_04124_CANNOT_PROCESS_NULL_ADD_REQUEST ); 1157 1158 if ( LOG.isDebugEnabled() ) 1159 { 1160 LOG.debug( msg ); 1161 } 1162 1163 throw new IllegalArgumentException( msg ); 1164 } 1165 1166 if ( addRequest.getEntry() == null ) 1167 { 1168 String msg = I18n.err( I18n.ERR_04125_CANNOT_ADD_NULL_ENTRY ); 1169 1170 if ( LOG.isDebugEnabled() ) 1171 { 1172 LOG.debug( msg ); 1173 } 1174 1175 throw new IllegalArgumentException( msg ); 1176 } 1177 1178 // try to connect, if we aren't already connected. 1179 connect(); 1180 1181 checkSession(); 1182 1183 int newId = messageId.incrementAndGet(); 1184 1185 addRequest.setMessageId( newId ); 1186 AddFuture addFuture = new AddFuture( this, newId ); 1187 addToFutureMap( newId, addFuture ); 1188 1189 // Send the request to the server 1190 writeRequest( addRequest ); 1191 1192 // Ok, done return the future 1193 return addFuture; 1194 } 1195 1196 1197 //------------------------ The LDAP operations ------------------------// 1198 1199 /** 1200 * {@inheritDoc} 1201 */ 1202 @Override 1203 public void abandon( int messageId ) 1204 { 1205 if ( messageId < 0 ) 1206 { 1207 String msg = I18n.err( I18n.ERR_04126_CANNOT_ABANDON_NEG_MSG_ID ); 1208 1209 if ( LOG.isDebugEnabled() ) 1210 { 1211 LOG.debug( msg ); 1212 } 1213 1214 throw new IllegalArgumentException( msg ); 1215 } 1216 1217 AbandonRequest abandonRequest = new AbandonRequestImpl(); 1218 abandonRequest.setAbandoned( messageId ); 1219 1220 abandonInternal( abandonRequest ); 1221 } 1222 1223 1224 /** 1225 * {@inheritDoc} 1226 */ 1227 @Override 1228 public void abandon( AbandonRequest abandonRequest ) 1229 { 1230 if ( abandonRequest == null ) 1231 { 1232 String msg = I18n.err( I18n.ERR_04127_CANNOT_PROCESS_NULL_ABANDON_REQ ); 1233 1234 if ( LOG.isDebugEnabled() ) 1235 { 1236 LOG.debug( msg ); 1237 } 1238 1239 throw new IllegalArgumentException( msg ); 1240 } 1241 1242 abandonInternal( abandonRequest ); 1243 } 1244 1245 1246 /** 1247 * Internal AbandonRequest handling 1248 * 1249 * @param abandonRequest The request to abandon 1250 */ 1251 private void abandonInternal( AbandonRequest abandonRequest ) 1252 { 1253 if ( LOG.isDebugEnabled() ) 1254 { 1255 LOG.debug( I18n.msg( I18n.MSG_04104_SENDING_REQUEST, abandonRequest ) ); 1256 } 1257 1258 int newId = messageId.incrementAndGet(); 1259 abandonRequest.setMessageId( newId ); 1260 1261 // Send the request to the server 1262 ldapSession.write( abandonRequest ); 1263 1264 // remove the associated listener if any 1265 int abandonId = abandonRequest.getAbandoned(); 1266 1267 ResponseFuture<? extends Response> rf = getFromFutureMap( abandonId ); 1268 1269 // if the listener is not null, this is a async operation and no need to 1270 // send cancel signal on future, sending so will leave a dangling poision object in the corresponding queue 1271 // this is a sync operation send cancel signal to the corresponding ResponseFuture 1272 if ( rf != null ) 1273 { 1274 if ( LOG.isDebugEnabled() ) 1275 { 1276 LOG.debug( I18n.msg( I18n.MSG_04141_SENDING_CANCEL ) ); 1277 } 1278 1279 rf.cancel( true ); 1280 } 1281 else 1282 { 1283 // this shouldn't happen 1284 if ( LOG.isWarnEnabled() ) 1285 { 1286 LOG.warn( I18n.msg( I18n.MSG_04165_NO_FUTURE_ASSOCIATED_TO_MSG_ID_COMPLETED, abandonId ) ); 1287 } 1288 } 1289 } 1290 1291 1292 /** 1293 * {@inheritDoc} 1294 */ 1295 @Override 1296 public void bind() throws LdapException 1297 { 1298 if ( LOG.isDebugEnabled() ) 1299 { 1300 LOG.debug( I18n.msg( I18n.MSG_04112_BIND ) ); 1301 } 1302 1303 // Create the BindRequest 1304 BindRequest bindRequest = createBindRequest( config.getName(), Strings.getBytesUtf8( config.getCredentials() ) ); 1305 1306 BindResponse bindResponse = bind( bindRequest ); 1307 1308 processResponse( bindResponse ); 1309 } 1310 1311 1312 /** 1313 * {@inheritDoc} 1314 */ 1315 @Override 1316 public void anonymousBind() throws LdapException 1317 { 1318 if ( LOG.isDebugEnabled() ) 1319 { 1320 LOG.debug( I18n.msg( I18n.MSG_04109_ANONYMOUS_BIND ) ); 1321 } 1322 1323 // Create the BindRequest 1324 BindRequest bindRequest = createBindRequest( StringConstants.EMPTY, Strings.EMPTY_BYTES ); 1325 1326 BindResponse bindResponse = bind( bindRequest ); 1327 1328 processResponse( bindResponse ); 1329 } 1330 1331 1332 /** 1333 * {@inheritDoc} 1334 */ 1335 @Override 1336 public BindFuture bindAsync() throws LdapException 1337 { 1338 if ( LOG.isDebugEnabled() ) 1339 { 1340 LOG.debug( I18n.msg( I18n.MSG_04111_ASYNC_BIND ) ); 1341 } 1342 1343 // Create the BindRequest 1344 BindRequest bindRequest = createBindRequest( config.getName(), Strings.getBytesUtf8( config.getCredentials() ) ); 1345 1346 return bindAsync( bindRequest ); 1347 } 1348 1349 1350 /** 1351 * {@inheritDoc} 1352 */ 1353 @Override 1354 public BindFuture anonymousBindAsync() throws LdapException 1355 { 1356 if ( LOG.isDebugEnabled() ) 1357 { 1358 LOG.debug( I18n.msg( I18n.MSG_04110_ANONYMOUS_ASYNC_BIND ) ); 1359 } 1360 1361 // Create the BindRequest 1362 BindRequest bindRequest = createBindRequest( StringConstants.EMPTY, Strings.EMPTY_BYTES ); 1363 1364 return bindAsync( bindRequest ); 1365 } 1366 1367 1368 /** 1369 * Asynchronous unauthenticated authentication bind 1370 * 1371 * @param name The name we use to authenticate the user. It must be a 1372 * valid Dn 1373 * @return The BindResponse LdapResponse 1374 * @throws LdapException if some error occurred 1375 */ 1376 public BindFuture bindAsync( String name ) throws LdapException 1377 { 1378 if ( LOG.isDebugEnabled() ) 1379 { 1380 LOG.debug( I18n.msg( I18n.MSG_04102_BIND_REQUEST, name ) ); 1381 } 1382 1383 // Create the BindRequest 1384 BindRequest bindRequest = createBindRequest( name, Strings.EMPTY_BYTES ); 1385 1386 return bindAsync( bindRequest ); 1387 } 1388 1389 1390 /** 1391 * {@inheritDoc} 1392 */ 1393 @Override 1394 public BindFuture bindAsync( String name, String credentials ) throws LdapException 1395 { 1396 if ( LOG.isDebugEnabled() ) 1397 { 1398 LOG.debug( I18n.msg( I18n.MSG_04102_BIND_REQUEST, name ) ); 1399 } 1400 1401 // The password must not be empty or null 1402 if ( Strings.isEmpty( credentials ) && Strings.isNotEmpty( name ) ) 1403 { 1404 if ( LOG.isDebugEnabled() ) 1405 { 1406 LOG.debug( I18n.msg( I18n.MSG_04105_MISSING_PASSWORD ) ); 1407 } 1408 1409 throw new LdapAuthenticationException( I18n.msg( I18n.MSG_04105_MISSING_PASSWORD ) ); 1410 } 1411 1412 // Create the BindRequest 1413 BindRequest bindRequest = createBindRequest( name, Strings.getBytesUtf8( credentials ) ); 1414 1415 return bindAsync( bindRequest ); 1416 } 1417 1418 1419 /** 1420 * Asynchronous unauthenticated authentication Bind on a server. 1421 * 1422 * @param name The name we use to authenticate the user. It must be a 1423 * valid Dn 1424 * @return The BindResponse LdapResponse 1425 * @throws LdapException if some error occurred 1426 */ 1427 public BindFuture bindAsync( Dn name ) throws LdapException 1428 { 1429 if ( LOG.isDebugEnabled() ) 1430 { 1431 LOG.debug( I18n.msg( I18n.MSG_04102_BIND_REQUEST, name ) ); 1432 } 1433 1434 // Create the BindRequest 1435 BindRequest bindRequest = createBindRequest( name, Strings.EMPTY_BYTES ); 1436 1437 return bindAsync( bindRequest ); 1438 } 1439 1440 1441 /** 1442 * {@inheritDoc} 1443 */ 1444 @Override 1445 public BindFuture bindAsync( Dn name, String credentials ) throws LdapException 1446 { 1447 if ( LOG.isDebugEnabled() ) 1448 { 1449 LOG.debug( I18n.msg( I18n.MSG_04102_BIND_REQUEST, name ) ); 1450 } 1451 1452 // The password must not be empty or null 1453 if ( Strings.isEmpty( credentials ) && ( !Dn.EMPTY_DN.equals( name ) ) ) 1454 { 1455 if ( LOG.isDebugEnabled() ) 1456 { 1457 LOG.debug( I18n.msg( I18n.MSG_04105_MISSING_PASSWORD ) ); 1458 } 1459 1460 throw new LdapAuthenticationException( I18n.msg( I18n.MSG_04105_MISSING_PASSWORD ) ); 1461 } 1462 1463 // Create the BindRequest 1464 BindRequest bindRequest = createBindRequest( name, Strings.getBytesUtf8( credentials ) ); 1465 1466 return bindAsync( bindRequest ); 1467 } 1468 1469 1470 /** 1471 * {@inheritDoc} 1472 */ 1473 @Override 1474 public BindResponse bind( BindRequest bindRequest ) throws LdapException 1475 { 1476 if ( bindRequest == null ) 1477 { 1478 String msg = I18n.err( I18n.ERR_04128_CANNOT_PROCESS_NULL_BIND_REQ ); 1479 1480 if ( LOG.isDebugEnabled() ) 1481 { 1482 LOG.debug( msg ); 1483 } 1484 1485 throw new IllegalArgumentException( msg ); 1486 } 1487 1488 BindFuture bindFuture = bindAsync( bindRequest ); 1489 1490 // Get the result from the future 1491 try 1492 { 1493 // Read the response, waiting for it if not available immediately 1494 // Get the response, blocking 1495 BindResponse bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS ); 1496 1497 if ( bindResponse == null ) 1498 { 1499 // We didn't received anything : this is an error 1500 if ( LOG.isErrorEnabled() ) 1501 { 1502 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Bind" ) ); 1503 } 1504 1505 throw new LdapException( TIME_OUT_ERROR ); 1506 } 1507 1508 if ( bindResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 1509 { 1510 authenticated.set( true ); 1511 1512 // Everything is fine, return the response 1513 if ( LOG.isDebugEnabled() ) 1514 { 1515 LOG.debug( I18n.msg( I18n.MSG_04101_BIND_SUCCESSFUL, bindResponse ) ); 1516 } 1517 } 1518 else 1519 { 1520 // We have had an error 1521 if ( LOG.isDebugEnabled() ) 1522 { 1523 LOG.debug( I18n.msg( I18n.MSG_04100_BIND_FAIL, bindResponse ) ); 1524 } 1525 } 1526 1527 return bindResponse; 1528 } 1529 catch ( Exception ie ) 1530 { 1531 // Catch all other exceptions 1532 LOG.error( NO_RESPONSE_ERROR, ie ); 1533 1534 throw new LdapException( NO_RESPONSE_ERROR, ie ); 1535 } 1536 } 1537 1538 1539 /** 1540 * Create a Simple BindRequest ready to be sent. 1541 * 1542 * @param name The Bind name 1543 * @param credentials The Bind credentials 1544 * @return The created BindRequest instance 1545 */ 1546 private BindRequest createBindRequest( String name, byte[] credentials ) 1547 { 1548 return createBindRequest( name, credentials, null, ( Control[] ) null ); 1549 } 1550 1551 1552 /** 1553 * Create a Simple BindRequest ready to be sent. 1554 * 1555 * @param name The Bind name 1556 * @param credentials The Bind credentials 1557 * @return The created BindRequest instance 1558 */ 1559 private BindRequest createBindRequest( Dn name, byte[] credentials ) 1560 { 1561 return createBindRequest( name.getName(), credentials, null, ( Control[] ) null ); 1562 } 1563 1564 1565 /** 1566 * {@inheritDoc} 1567 */ 1568 @Override 1569 public BindFuture bindAsync( BindRequest bindRequest ) throws LdapException 1570 { 1571 if ( bindRequest == null ) 1572 { 1573 String msg = I18n.err( I18n.ERR_04128_CANNOT_PROCESS_NULL_BIND_REQ ); 1574 1575 if ( LOG.isDebugEnabled() ) 1576 { 1577 LOG.debug( msg ); 1578 } 1579 1580 throw new IllegalArgumentException( msg ); 1581 } 1582 1583 // First switch to anonymous state 1584 authenticated.set( false ); 1585 1586 // try to connect, if we aren't already connected. 1587 connect(); 1588 1589 // establish TLS layer if TLS is enabled and SSL is NOT 1590 if ( config.isUseTls() && !config.isUseSsl() ) 1591 { 1592 startTls(); 1593 } 1594 1595 // If the session has not been establish, or is closed, we get out immediately 1596 checkSession(); 1597 1598 // Update the messageId 1599 int newId = messageId.incrementAndGet(); 1600 bindRequest.setMessageId( newId ); 1601 1602 if ( LOG.isDebugEnabled() ) 1603 { 1604 LOG.debug( I18n.msg( I18n.MSG_04104_SENDING_REQUEST, bindRequest ) ); 1605 } 1606 1607 // Create a future for this Bind operation 1608 BindFuture bindFuture = new BindFuture( this, newId ); 1609 1610 addToFutureMap( newId, bindFuture ); 1611 1612 writeRequest( bindRequest ); 1613 1614 // Ok, done return the future 1615 return bindFuture; 1616 } 1617 1618 1619 /** 1620 * SASL PLAIN Bind on a server. 1621 * 1622 * @param authcid The Authentication identity 1623 * @param credentials The password. It can't be null 1624 * @return The BindResponse LdapResponse 1625 * @throws LdapException if some error occurred 1626 */ 1627 public BindResponse bindSaslPlain( String authcid, String credentials ) throws LdapException 1628 { 1629 return bindSaslPlain( null, authcid, credentials ); 1630 } 1631 1632 1633 /** 1634 * SASL PLAIN Bind on a server. 1635 * 1636 * @param authzid The Authorization identity 1637 * @param authcid The Authentication identity 1638 * @param credentials The password. It can't be null 1639 * @return The BindResponse LdapResponse 1640 * @throws LdapException if some error occurred 1641 */ 1642 public BindResponse bindSaslPlain( String authzid, String authcid, String credentials ) throws LdapException 1643 { 1644 if ( LOG.isDebugEnabled() ) 1645 { 1646 LOG.debug( I18n.msg( I18n.MSG_04127_SASL_PLAIN_BIND ) ); 1647 } 1648 1649 // Create the BindRequest 1650 SaslPlainRequest saslRequest = new SaslPlainRequest(); 1651 saslRequest.setAuthorizationId( authzid ); 1652 saslRequest.setUsername( authcid ); 1653 saslRequest.setCredentials( credentials ); 1654 1655 BindFuture bindFuture = bindAsync( saslRequest ); 1656 1657 // Get the result from the future 1658 try 1659 { 1660 // Read the response, waiting for it if not available immediately 1661 // Get the response, blocking 1662 BindResponse bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS ); 1663 1664 if ( bindResponse == null ) 1665 { 1666 // We didn't received anything : this is an error 1667 if ( LOG.isErrorEnabled() ) 1668 { 1669 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Bind" ) ); 1670 } 1671 1672 throw new LdapException( TIME_OUT_ERROR ); 1673 } 1674 1675 if ( bindResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 1676 { 1677 authenticated.set( true ); 1678 1679 // Everything is fine, return the response 1680 if ( LOG.isDebugEnabled() ) 1681 { 1682 LOG.debug( I18n.msg( I18n.MSG_04101_BIND_SUCCESSFUL, bindResponse ) ); 1683 } 1684 } 1685 else 1686 { 1687 // We have had an error 1688 if ( LOG.isDebugEnabled() ) 1689 { 1690 LOG.debug( I18n.msg( I18n.MSG_04100_BIND_FAIL, bindResponse ) ); 1691 } 1692 } 1693 1694 return bindResponse; 1695 } 1696 catch ( Exception ie ) 1697 { 1698 // Catch all other exceptions 1699 LOG.error( NO_RESPONSE_ERROR, ie ); 1700 1701 throw new LdapException( NO_RESPONSE_ERROR, ie ); 1702 } 1703 } 1704 1705 1706 /** 1707 * Bind to the server using a SaslRequest object. 1708 * 1709 * @param request The SaslRequest POJO containing all the needed parameters 1710 * @return A LdapResponse containing the result 1711 * @throws LdapException if some error occurred 1712 */ 1713 public BindResponse bind( SaslRequest request ) throws LdapException 1714 { 1715 if ( request == null ) 1716 { 1717 String msg = I18n.msg( I18n.MSG_04103_NULL_REQUEST ); 1718 1719 if ( LOG.isDebugEnabled() ) 1720 { 1721 LOG.debug( msg ); 1722 } 1723 1724 throw new IllegalArgumentException( msg ); 1725 } 1726 1727 BindFuture bindFuture = bindAsync( request ); 1728 1729 1730 // Get the result from the future 1731 try 1732 { 1733 // Read the response, waiting for it if not available immediately 1734 // Get the response, blocking 1735 BindResponse bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS ); 1736 1737 if ( bindResponse == null ) 1738 { 1739 // We didn't received anything : this is an error 1740 if ( LOG.isErrorEnabled() ) 1741 { 1742 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Bind" ) ); 1743 } 1744 1745 throw new LdapException( TIME_OUT_ERROR ); 1746 } 1747 1748 if ( bindResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 1749 { 1750 authenticated.set( true ); 1751 1752 // Everything is fine, return the response 1753 if ( LOG.isDebugEnabled() ) 1754 { 1755 LOG.debug( I18n.msg( I18n.MSG_04101_BIND_SUCCESSFUL, bindResponse ) ); 1756 } 1757 } 1758 else 1759 { 1760 // We have had an error 1761 if ( LOG.isDebugEnabled() ) 1762 { 1763 LOG.debug( I18n.msg( I18n.MSG_04100_BIND_FAIL, bindResponse ) ); 1764 } 1765 } 1766 1767 return bindResponse; 1768 } 1769 catch ( Exception ie ) 1770 { 1771 // Catch all other exceptions 1772 LOG.error( NO_RESPONSE_ERROR, ie ); 1773 1774 throw new LdapException( NO_RESPONSE_ERROR, ie ); 1775 } 1776 } 1777 1778 1779 /** 1780 * Bind to the server using the SASL CRAM-MD5 mechanism. 1781 * 1782 * @param userName The user name 1783 * @param credentials The user credentials 1784 * @return A LdapResponse containing the result 1785 * @throws LdapException if some error occurred 1786 */ 1787 public BindResponse bindSaslCramMd5( String userName, String credentials ) throws LdapException 1788 { 1789 SaslCramMd5Request request = new SaslCramMd5Request(); 1790 request.setUsername( userName ); 1791 request.setCredentials( "secret" ); 1792 1793 return bind( request ); 1794 } 1795 1796 1797 /** 1798 * Bind to the server using the SASL DIGEST-MD5 mechanism. 1799 * 1800 * @param userName The user name 1801 * @param credentials The user credentials 1802 * @return A LdapResponse containing the result 1803 * @throws LdapException if some error occurred 1804 */ 1805 public BindResponse bindSaslDigestMd5( String userName, String credentials ) throws LdapException 1806 { 1807 SaslDigestMd5Request request = new SaslDigestMd5Request(); 1808 request.setUsername( userName ); 1809 request.setCredentials( "secret" ); 1810 1811 return bind( request ); 1812 } 1813 1814 1815 /** 1816 * Bind to the server using a CramMd5Request object. 1817 * 1818 * @param request The CramMd5Request POJO containing all the needed parameters 1819 * @return A LdapResponse containing the result 1820 * @throws LdapException if some error occurred 1821 */ 1822 public BindResponse bind( SaslCramMd5Request request ) throws LdapException 1823 { 1824 if ( request == null ) 1825 { 1826 String msg = I18n.msg( I18n.MSG_04103_NULL_REQUEST ); 1827 1828 if ( LOG.isDebugEnabled() ) 1829 { 1830 LOG.debug( msg ); 1831 } 1832 1833 throw new IllegalArgumentException( msg ); 1834 } 1835 1836 BindFuture bindFuture = bindAsync( request ); 1837 1838 // Get the result from the future 1839 try 1840 { 1841 // Read the response, waiting for it if not available immediately 1842 // Get the response, blocking 1843 BindResponse bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS ); 1844 1845 if ( bindResponse == null ) 1846 { 1847 // We didn't received anything : this is an error 1848 if ( LOG.isErrorEnabled() ) 1849 { 1850 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Bind" ) ); 1851 } 1852 1853 throw new LdapException( TIME_OUT_ERROR ); 1854 } 1855 1856 if ( bindResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 1857 { 1858 authenticated.set( true ); 1859 1860 // Everything is fine, return the response 1861 if ( LOG.isDebugEnabled() ) 1862 { 1863 LOG.debug( I18n.msg( I18n.MSG_04101_BIND_SUCCESSFUL, bindResponse ) ); 1864 } 1865 } 1866 else 1867 { 1868 // We have had an error 1869 if ( LOG.isDebugEnabled() ) 1870 { 1871 LOG.debug( I18n.msg( I18n.MSG_04100_BIND_FAIL, bindResponse ) ); 1872 } 1873 } 1874 1875 return bindResponse; 1876 } 1877 catch ( Exception ie ) 1878 { 1879 // Catch all other exceptions 1880 LOG.error( NO_RESPONSE_ERROR, ie ); 1881 1882 throw new LdapException( NO_RESPONSE_ERROR, ie ); 1883 } 1884 } 1885 1886 1887 /** 1888 * Do an asynchronous bind, based on a SaslPlainRequest. 1889 * 1890 * @param request The SaslPlainRequest POJO containing all the needed parameters 1891 * @return The bind operation's future 1892 * @throws LdapException if some error occurred 1893 */ 1894 public BindFuture bindAsync( SaslRequest request ) 1895 throws LdapException 1896 { 1897 return bindSasl( request ); 1898 } 1899 1900 1901 /** 1902 * Bind to the server using a DigestMd5Request object. 1903 * 1904 * @param request The DigestMd5Request POJO containing all the needed parameters 1905 * @return A LdapResponse containing the result 1906 * @throws LdapException if some error occurred 1907 */ 1908 public BindResponse bind( SaslDigestMd5Request request ) throws LdapException 1909 { 1910 if ( request == null ) 1911 { 1912 String msg = I18n.msg( I18n.MSG_04103_NULL_REQUEST ); 1913 1914 if ( LOG.isDebugEnabled() ) 1915 { 1916 LOG.debug( msg ); 1917 } 1918 1919 throw new IllegalArgumentException( msg ); 1920 } 1921 1922 BindFuture bindFuture = bindAsync( request ); 1923 1924 // Get the result from the future 1925 try 1926 { 1927 // Read the response, waiting for it if not available immediately 1928 // Get the response, blocking 1929 BindResponse bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS ); 1930 1931 if ( bindResponse == null ) 1932 { 1933 // We didn't received anything : this is an error 1934 if ( LOG.isErrorEnabled() ) 1935 { 1936 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Bind" ) ); 1937 } 1938 1939 throw new LdapException( TIME_OUT_ERROR ); 1940 } 1941 1942 if ( bindResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 1943 { 1944 authenticated.set( true ); 1945 1946 // Everything is fine, return the response 1947 if ( LOG.isDebugEnabled() ) 1948 { 1949 LOG.debug( I18n.msg( I18n.MSG_04101_BIND_SUCCESSFUL, bindResponse ) ); 1950 } 1951 } 1952 else 1953 { 1954 // We have had an error 1955 if ( LOG.isDebugEnabled() ) 1956 { 1957 LOG.debug( I18n.msg( I18n.MSG_04100_BIND_FAIL, bindResponse ) ); 1958 } 1959 } 1960 1961 return bindResponse; 1962 } 1963 catch ( Exception ie ) 1964 { 1965 // Catch all other exceptions 1966 LOG.error( NO_RESPONSE_ERROR, ie ); 1967 1968 throw new LdapException( NO_RESPONSE_ERROR, ie ); 1969 } 1970 } 1971 1972 1973 /** 1974 * Bind to the server using a GssApiRequest object. 1975 * 1976 * @param request The GssApiRequest POJO containing all the needed parameters 1977 * @return A LdapResponse containing the result 1978 * @throws LdapException if some error occurred 1979 */ 1980 public BindResponse bind( SaslGssApiRequest request ) throws LdapException 1981 { 1982 if ( request == null ) 1983 { 1984 String msg = I18n.msg( I18n.MSG_04103_NULL_REQUEST ); 1985 1986 if ( LOG.isDebugEnabled() ) 1987 { 1988 LOG.debug( msg ); 1989 } 1990 1991 throw new IllegalArgumentException( msg ); 1992 } 1993 1994 BindFuture bindFuture = bindAsync( request ); 1995 1996 // Get the result from the future 1997 try 1998 { 1999 // Read the response, waiting for it if not available immediately 2000 // Get the response, blocking 2001 BindResponse bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS ); 2002 2003 if ( bindResponse == null ) 2004 { 2005 // We didn't received anything : this is an error 2006 if ( LOG.isErrorEnabled() ) 2007 { 2008 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Bind" ) ); 2009 } 2010 2011 throw new LdapException( TIME_OUT_ERROR ); 2012 } 2013 2014 if ( bindResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 2015 { 2016 authenticated.set( true ); 2017 2018 // Everything is fine, return the response 2019 if ( LOG.isDebugEnabled() ) 2020 { 2021 LOG.debug( I18n.msg( I18n.MSG_04101_BIND_SUCCESSFUL, bindResponse ) ); 2022 } 2023 } 2024 else 2025 { 2026 // We have had an error 2027 if ( LOG.isDebugEnabled() ) 2028 { 2029 LOG.debug( I18n.msg( I18n.MSG_04100_BIND_FAIL, bindResponse ) ); 2030 } 2031 } 2032 2033 return bindResponse; 2034 } 2035 catch ( Exception ie ) 2036 { 2037 // Catch all other exceptions 2038 LOG.error( NO_RESPONSE_ERROR, ie ); 2039 2040 throw new LdapException( NO_RESPONSE_ERROR, ie ); 2041 } 2042 } 2043 2044 2045 /** 2046 * Bind to the server using a SaslExternalRequest object. 2047 * 2048 * @param request The SaslExternalRequest POJO containing all the needed parameters 2049 * @return A LdapResponse containing the result 2050 * @throws LdapException if some error occurred 2051 */ 2052 public BindResponse bind( SaslExternalRequest request ) throws LdapException 2053 { 2054 if ( request == null ) 2055 { 2056 String msg = I18n.msg( I18n.MSG_04103_NULL_REQUEST ); 2057 2058 if ( LOG.isDebugEnabled() ) 2059 { 2060 LOG.debug( msg ); 2061 } 2062 2063 throw new IllegalArgumentException( msg ); 2064 } 2065 2066 BindFuture bindFuture = bindAsync( request ); 2067 2068 // Get the result from the future 2069 try 2070 { 2071 // Read the response, waiting for it if not available immediately 2072 // Get the response, blocking 2073 BindResponse bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS ); 2074 2075 if ( bindResponse == null ) 2076 { 2077 // We didn't received anything : this is an error 2078 if ( LOG.isErrorEnabled() ) 2079 { 2080 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Bind" ) ); 2081 } 2082 2083 throw new LdapException( TIME_OUT_ERROR ); 2084 } 2085 2086 if ( bindResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 2087 { 2088 authenticated.set( true ); 2089 2090 // Everything is fine, return the response 2091 if ( LOG.isDebugEnabled() ) 2092 { 2093 LOG.debug( I18n.msg( I18n.MSG_04101_BIND_SUCCESSFUL, bindResponse ) ); 2094 } 2095 } 2096 else 2097 { 2098 // We have had an error 2099 if ( LOG.isDebugEnabled() ) 2100 { 2101 LOG.debug( I18n.msg( I18n.MSG_04100_BIND_FAIL, bindResponse ) ); 2102 } 2103 } 2104 2105 return bindResponse; 2106 } 2107 catch ( Exception ie ) 2108 { 2109 // Catch all other exceptions 2110 LOG.error( NO_RESPONSE_ERROR, ie ); 2111 2112 throw new LdapException( NO_RESPONSE_ERROR, ie ); 2113 } 2114 } 2115 2116 2117 /** 2118 * Do an asynchronous bind, based on a GssApiRequest. 2119 * 2120 * @param request The GssApiRequest POJO containing all the needed parameters 2121 * @return The bind operation's future 2122 * @throws LdapException if some error occurred 2123 */ 2124 public BindFuture bindAsync( SaslGssApiRequest request ) 2125 throws LdapException 2126 { 2127 // Krb5.conf file 2128 if ( request.getKrb5ConfFilePath() != null ) 2129 { 2130 // Using the krb5.conf file provided by the user 2131 System.setProperty( KRB5_CONF, request.getKrb5ConfFilePath() ); 2132 } 2133 else if ( ( request.getRealmName() != null ) && ( request.getKdcHost() != null ) 2134 && ( request.getKdcPort() != 0 ) ) 2135 { 2136 try 2137 { 2138 // Using a custom krb5.conf we create from the settings provided by the user 2139 String krb5ConfPath = createKrb5ConfFile( request.getRealmName(), request.getKdcHost(), 2140 request.getKdcPort() ); 2141 System.setProperty( KRB5_CONF, krb5ConfPath ); 2142 } 2143 catch ( IOException ioe ) 2144 { 2145 throw new LdapException( ioe ); 2146 } 2147 } 2148 else 2149 { 2150 // Using the system Kerberos configuration 2151 System.clearProperty( KRB5_CONF ); 2152 } 2153 2154 // Login Module configuration 2155 if ( request.getLoginModuleConfiguration() != null ) 2156 { 2157 // Using the configuration provided by the user 2158 Configuration.setConfiguration( request.getLoginModuleConfiguration() ); 2159 } 2160 else 2161 { 2162 // Using the default configuration 2163 Configuration.setConfiguration( new Krb5LoginConfiguration() ); 2164 } 2165 2166 try 2167 { 2168 System.setProperty( "javax.security.auth.useSubjectCredsOnly", "true" ); 2169 LoginContext loginContext = new LoginContext( request.getLoginContextName(), 2170 new SaslCallbackHandler( request ) ); 2171 loginContext.login(); 2172 2173 final SaslGssApiRequest requetFinal = request; 2174 return ( BindFuture ) Subject.doAs( loginContext.getSubject(), new PrivilegedExceptionAction<Object>() 2175 { 2176 @Override 2177 public Object run() throws Exception 2178 { 2179 return bindSasl( requetFinal ); 2180 } 2181 } ); 2182 } 2183 catch ( Exception e ) 2184 { 2185 throw new LdapException( e ); 2186 } 2187 } 2188 2189 2190 /** 2191 * {@inheritDoc} 2192 */ 2193 @Override 2194 public EntryCursor search( Dn baseDn, String filter, SearchScope scope, String... attributes ) 2195 throws LdapException 2196 { 2197 if ( baseDn == null ) 2198 { 2199 if ( LOG.isDebugEnabled() ) 2200 { 2201 LOG.debug( I18n.msg( I18n.MSG_04138_NULL_DN_SEARCH ) ); 2202 } 2203 2204 throw new IllegalArgumentException( I18n.err( I18n.ERR_04129_NULL_BASE_DN ) ); 2205 } 2206 2207 // Create a new SearchRequest object 2208 SearchRequest searchRequest = new SearchRequestImpl(); 2209 2210 searchRequest.setBase( baseDn ); 2211 searchRequest.setFilter( filter ); 2212 searchRequest.setScope( scope ); 2213 searchRequest.addAttributes( attributes ); 2214 searchRequest.setDerefAliases( AliasDerefMode.DEREF_ALWAYS ); 2215 2216 // Process the request in blocking mode 2217 return new EntryCursorImpl( search( searchRequest ) ); 2218 } 2219 2220 2221 /** 2222 * {@inheritDoc} 2223 */ 2224 @Override 2225 public EntryCursor search( String baseDn, String filter, SearchScope scope, String... attributes ) 2226 throws LdapException 2227 { 2228 return search( new Dn( baseDn ), filter, scope, attributes ); 2229 } 2230 2231 2232 /** 2233 * {@inheritDoc} 2234 */ 2235 @Override 2236 public SearchFuture searchAsync( Dn baseDn, String filter, SearchScope scope, String... attributes ) 2237 throws LdapException 2238 { 2239 // Create a new SearchRequest object 2240 SearchRequest searchRequest = new SearchRequestImpl(); 2241 2242 searchRequest.setBase( baseDn ); 2243 searchRequest.setFilter( filter ); 2244 searchRequest.setScope( scope ); 2245 searchRequest.addAttributes( attributes ); 2246 searchRequest.setDerefAliases( AliasDerefMode.DEREF_ALWAYS ); 2247 2248 // Process the request in blocking mode 2249 return searchAsync( searchRequest ); 2250 } 2251 2252 2253 /** 2254 * {@inheritDoc} 2255 */ 2256 @Override 2257 public SearchFuture searchAsync( String baseDn, String filter, SearchScope scope, String... attributes ) 2258 throws LdapException 2259 { 2260 return searchAsync( new Dn( baseDn ), filter, scope, attributes ); 2261 } 2262 2263 2264 /** 2265 * {@inheritDoc} 2266 */ 2267 @Override 2268 public SearchFuture searchAsync( SearchRequest searchRequest ) throws LdapException 2269 { 2270 if ( searchRequest == null ) 2271 { 2272 String msg = I18n.err( I18n.ERR_04130_CANNOT_PROCESS_NULL_SEARCH_REQ ); 2273 2274 if ( LOG.isDebugEnabled() ) 2275 { 2276 LOG.debug( msg ); 2277 } 2278 2279 throw new IllegalArgumentException( msg ); 2280 } 2281 2282 if ( searchRequest.getBase() == null ) 2283 { 2284 String msg = I18n.err( I18n.ERR_04131_CANNOT_PROCESS_SEARCH_NULL_DN ); 2285 2286 if ( LOG.isDebugEnabled() ) 2287 { 2288 LOG.debug( msg ); 2289 } 2290 2291 throw new IllegalArgumentException( msg ); 2292 } 2293 2294 // try to connect, if we aren't already connected. 2295 connect(); 2296 2297 // If the session has not been establish, or is closed, we get out immediately 2298 checkSession(); 2299 2300 int newId = messageId.incrementAndGet(); 2301 searchRequest.setMessageId( newId ); 2302 2303 if ( searchRequest.isIgnoreReferrals() ) 2304 { 2305 // We want to ignore the referral, inject the ManageDSAIT control in the request 2306 searchRequest.addControl( new ManageDsaITImpl() ); 2307 } 2308 2309 if ( LOG.isDebugEnabled() ) 2310 { 2311 LOG.debug( I18n.msg( I18n.MSG_04104_SENDING_REQUEST, searchRequest ) ); 2312 } 2313 2314 SearchFuture searchFuture = new SearchFuture( this, searchRequest.getMessageId() ); 2315 addToFutureMap( searchRequest.getMessageId(), searchFuture ); 2316 2317 // Send the request to the server 2318 writeRequest( searchRequest ); 2319 2320 // Check that the future hasn't be canceled 2321 if ( searchFuture.isCancelled() ) 2322 { 2323 // Throw an exception here 2324 throw new LdapException( searchFuture.getCause() ); 2325 } 2326 2327 // Ok, done return the future 2328 return searchFuture; 2329 } 2330 2331 2332 /** 2333 * {@inheritDoc} 2334 */ 2335 @Override 2336 public SearchCursor search( SearchRequest searchRequest ) throws LdapException 2337 { 2338 if ( searchRequest == null ) 2339 { 2340 String msg = I18n.err( I18n.ERR_04130_CANNOT_PROCESS_NULL_SEARCH_REQ ); 2341 2342 if ( LOG.isDebugEnabled() ) 2343 { 2344 LOG.debug( msg ); 2345 } 2346 2347 throw new IllegalArgumentException( msg ); 2348 } 2349 2350 SearchFuture searchFuture = searchAsync( searchRequest ); 2351 2352 long searchTimeout = getTimeout( timeout, searchRequest.getTimeLimit() ); 2353 2354 return new SearchCursorImpl( searchFuture, searchTimeout, TimeUnit.MILLISECONDS ); 2355 } 2356 2357 2358 //------------------------ The LDAP operations ------------------------// 2359 // Unbind operations // 2360 //---------------------------------------------------------------------// 2361 /** 2362 * {@inheritDoc} 2363 */ 2364 @Override 2365 public void unBind() throws LdapException 2366 { 2367 // If the session has not been establish, or is closed, we get out immediately 2368 checkSession(); 2369 2370 // Creates the messageID and stores it into the 2371 // initial message and the transmitted message. 2372 int newId = messageId.incrementAndGet(); 2373 2374 // Create the UnbindRequest 2375 UnbindRequest unbindRequest = new UnbindRequestImpl(); 2376 unbindRequest.setMessageId( newId ); 2377 2378 if ( LOG.isDebugEnabled() ) 2379 { 2380 LOG.debug( I18n.msg( I18n.MSG_04132_SENDING_UNBIND, unbindRequest ) ); 2381 } 2382 2383 // Send the request to the server 2384 // Use this for logging instead: WriteFuture unbindFuture = ldapSession.write( unbindRequest ) 2385 WriteFuture unbindFuture = ldapSession.write( unbindRequest ); 2386 2387 unbindFuture.awaitUninterruptibly( timeout ); 2388 2389 authenticated.set( false ); 2390 2391 // Close all the Future for this session 2392 for ( ResponseFuture<? extends Response> responseFuture : futureMap.values() ) 2393 { 2394 responseFuture.cancel(); 2395 } 2396 2397 // clear the mappings 2398 clearMaps(); 2399 2400 // We now have to close the session 2401 close(); 2402 2403 connected.set( false ); 2404 2405 // Last, not least, reset the MessageId value 2406 messageId.set( 0 ); 2407 2408 // And get out 2409 if ( LOG.isDebugEnabled() ) 2410 { 2411 LOG.debug( I18n.msg( I18n.MSG_04133_UNBINDSUCCESSFUL ) ); 2412 } 2413 } 2414 2415 2416 /** 2417 * Set the connector to use. 2418 * 2419 * @param connector The connector to use 2420 */ 2421 public void setConnector( IoConnector connector ) 2422 { 2423 this.connector = connector; 2424 } 2425 2426 2427 /** 2428 * {@inheritDoc} 2429 */ 2430 @Override 2431 public void setTimeOut( long timeout ) 2432 { 2433 if ( timeout <= 0 ) 2434 { 2435 // Set a date in the far future : 100 years 2436 this.timeout = 1000L * 60L * 60L * 24L * 365L * 100L; 2437 } 2438 else 2439 { 2440 this.timeout = timeout; 2441 } 2442 } 2443 2444 2445 /** 2446 * Handle the exception we got. 2447 * 2448 * @param session The session we got the exception on 2449 * @param cause The exception cause 2450 * @throws Exception If we have had another exception 2451 */ 2452 @Override 2453 public void exceptionCaught( IoSession session, Throwable cause ) throws Exception 2454 { 2455 if ( LOG.isWarnEnabled() ) 2456 { 2457 LOG.warn( cause.getMessage(), cause ); 2458 } 2459 2460 session.setAttribute( EXCEPTION_KEY, cause ); 2461 2462 if ( cause instanceof ProtocolEncoderException ) 2463 { 2464 Throwable realCause = ( ( ProtocolEncoderException ) cause ).getCause(); 2465 2466 if ( realCause instanceof MessageEncoderException ) 2467 { 2468 int messageId = ( ( MessageEncoderException ) realCause ).getMessageId(); 2469 2470 ResponseFuture<?> response = futureMap.get( messageId ); 2471 response.cancel( true ); 2472 response.setCause( realCause ); 2473 } 2474 } 2475 2476 session.closeNow(); 2477 } 2478 2479 2480 /** 2481 * Check if the message is a NoticeOfDisconnect message 2482 * 2483 * @param message The message to check 2484 * @return <tt>true</tt> if the message is a Notice of Disconnect 2485 */ 2486 private boolean isNoticeOfDisconnect( Message message ) 2487 { 2488 if ( message instanceof ExtendedResponse ) 2489 { 2490 String responseName = ( ( ExtendedResponse ) message ).getResponseName(); 2491 2492 if ( NoticeOfDisconnect.EXTENSION_OID.equals( responseName ) ) 2493 { 2494 return true; 2495 } 2496 } 2497 2498 return false; 2499 } 2500 2501 2502 /** 2503 * Process the AddResponse received from the server 2504 * 2505 * @param addResponse The AddResponse to process 2506 * @param addFuture The AddFuture to feed 2507 * @param responseId The associated request message ID 2508 * @throws InterruptedException If the Future is interrupted 2509 */ 2510 private void addReceived( AddResponse addResponse, AddFuture addFuture, int responseId ) throws InterruptedException 2511 { 2512 // remove the listener from the listener map 2513 if ( LOG.isDebugEnabled() ) 2514 { 2515 if ( addResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 2516 { 2517 // Everything is fine, return the response 2518 LOG.debug( I18n.msg( I18n.MSG_04108_ADD_SUCCESSFUL, addResponse ) ); 2519 } 2520 else 2521 { 2522 // We have had an error 2523 LOG.debug( I18n.msg( I18n.MSG_04107_ADD_FAILED, addResponse ) ); 2524 } 2525 } 2526 2527 // Store the response into the future 2528 addFuture.set( addResponse ); 2529 2530 // Remove the future from the map 2531 removeFromFutureMaps( responseId ); 2532 } 2533 2534 2535 /** 2536 * Process the BindResponse received from the server 2537 * 2538 * @param bindResponse The BindResponse to process 2539 * @param bindFuture The BindFuture to feed 2540 * @param responseId The associated request message ID 2541 * @throws InterruptedException If the Future is interrupted 2542 */ 2543 private void bindReceived( BindResponse bindResponse, BindFuture bindFuture, int responseId ) 2544 throws InterruptedException 2545 { 2546 // remove the listener from the listener map 2547 if ( bindResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 2548 { 2549 authenticated.set( true ); 2550 2551 // Everything is fine, return the response 2552 if ( LOG.isDebugEnabled() ) 2553 { 2554 LOG.debug( I18n.msg( I18n.MSG_04101_BIND_SUCCESSFUL, bindResponse ) ); 2555 } 2556 } 2557 else 2558 { 2559 // We have had an error 2560 if ( LOG.isDebugEnabled() ) 2561 { 2562 LOG.debug( I18n.msg( I18n.MSG_04100_BIND_FAIL, bindResponse ) ); 2563 } 2564 } 2565 2566 // Store the response into the future 2567 bindFuture.set( bindResponse ); 2568 2569 // Remove the future from the map 2570 removeFromFutureMaps( responseId ); 2571 } 2572 2573 2574 /** 2575 * Process the CompareResponse received from the server 2576 * 2577 * @param compareResponse The CompareResponse to process 2578 * @param compareFuture The CompareFuture to feed 2579 * @param responseId The associated request message ID 2580 * @throws InterruptedException If the Future is interrupted 2581 */ 2582 private void compareReceived( CompareResponse compareResponse, CompareFuture compareFuture, int responseId ) 2583 throws InterruptedException 2584 { 2585 // remove the listener from the listener map 2586 if ( LOG.isDebugEnabled() ) 2587 { 2588 if ( compareResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 2589 { 2590 // Everything is fine, return the response 2591 LOG.debug( I18n.msg( I18n.MSG_04114_COMPARE_SUCCESSFUL, compareResponse ) ); 2592 } 2593 else 2594 { 2595 // We have had an error 2596 LOG.debug( I18n.msg( I18n.MSG_04113_COMPARE_FAILED, compareResponse ) ); 2597 } 2598 } 2599 2600 // Store the response into the future 2601 compareFuture.set( compareResponse ); 2602 2603 // Remove the future from the map 2604 removeFromFutureMaps( responseId ); 2605 } 2606 2607 2608 /** 2609 * Process the DeleteResponse received from the server 2610 * 2611 * @param deleteResponse The DeleteResponse to process 2612 * @param deleteFuture The DeleteFuture to feed 2613 * @param responseId The associated request message ID 2614 * @throws InterruptedException If the Future is interrupted 2615 */ 2616 private void deleteReceived( DeleteResponse deleteResponse, DeleteFuture deleteFuture, int responseId ) 2617 throws InterruptedException 2618 { 2619 if ( LOG.isDebugEnabled() ) 2620 { 2621 if ( deleteResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 2622 { 2623 // Everything is fine, return the response 2624 LOG.debug( I18n.msg( I18n.MSG_04116_DELETE_SUCCESSFUL, deleteResponse ) ); 2625 } 2626 else 2627 { 2628 // We have had an error 2629 LOG.debug( I18n.msg( I18n.MSG_04115_DELETE_FAILED, deleteResponse ) ); 2630 } 2631 } 2632 2633 // Store the response into the future 2634 deleteFuture.set( deleteResponse ); 2635 2636 // Remove the future from the map 2637 removeFromFutureMaps( responseId ); 2638 } 2639 2640 2641 /** 2642 * Process the ExtendedResponse received from the server 2643 * 2644 * @param extendedResponse The ExtendedResponse to process 2645 * @param extendedFuture The ExtendedFuture to feed 2646 * @param responseId The associated request message ID 2647 * @throws InterruptedException If the Future is interrupted 2648 * @throws DecoderException If the response cannot be decoded 2649 */ 2650 private void extendedReceived( ExtendedResponse extendedResponse, ExtendedFuture extendedFuture, int responseId ) 2651 throws InterruptedException, DecoderException 2652 { 2653 if ( LOG.isDebugEnabled() ) 2654 { 2655 if ( extendedResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 2656 { 2657 // Everything is fine, return the response 2658 LOG.debug( I18n.msg( I18n.MSG_04118_EXTENDED_SUCCESSFUL, extendedResponse ) ); 2659 } 2660 else 2661 { 2662 // We have had an error 2663 LOG.debug( I18n.msg( I18n.MSG_04117_EXTENDED_FAILED, extendedResponse ) ); 2664 } 2665 } 2666 2667 extendedResponse = handleOpaqueResponse( extendedResponse, extendedFuture ); 2668 2669 // Store the response into the future 2670 extendedFuture.set( extendedResponse ); 2671 2672 // Remove the future from the map 2673 removeFromFutureMaps( responseId ); 2674 } 2675 2676 2677 /** 2678 * Process the IntermediateResponse received from the server 2679 * 2680 * @param intermediateResponse The IntermediateResponse to process 2681 * @param responseFuture The ResponseFuture to feed 2682 * @throws InterruptedException If the Future is interrupted 2683 */ 2684 private void intermediateReceived( IntermediateResponse intermediateResponse, ResponseFuture<? extends Response> responseFuture ) 2685 throws InterruptedException 2686 { 2687 // Store the response into the future 2688 if ( responseFuture instanceof SearchFuture ) 2689 { 2690 ( ( SearchFuture ) responseFuture ).set( intermediateResponse ); 2691 } 2692 else if ( responseFuture instanceof ExtendedFuture ) 2693 { 2694 ( ( ExtendedFuture ) responseFuture ).set( intermediateResponse ); 2695 } 2696 else 2697 { 2698 // currently we only support IR for search and extended operations 2699 throw new UnsupportedOperationException( I18n.err( I18n.ERR_04111_UNKNOWN_RESPONSE_FUTURE_TYPE, 2700 responseFuture.getClass().getName() ) ); 2701 } 2702 2703 // Do not remove the future from the map, that's done when receiving search result done 2704 } 2705 2706 2707 /** 2708 * Process the ModifyResponse received from the server 2709 * 2710 * @param modifyResponse The ModifyResponse to process 2711 * @param modifyFuture The ModifyFuture to feed 2712 * @param responseId The associated request message ID 2713 * @throws InterruptedException If the Future is interrupted 2714 */ 2715 private void modifyReceived( ModifyResponse modifyResponse, ModifyFuture modifyFuture, int responseId ) 2716 throws InterruptedException 2717 { 2718 if ( LOG.isDebugEnabled() ) 2719 { 2720 if ( modifyResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 2721 { 2722 // Everything is fine, return the response 2723 if ( LOG.isDebugEnabled() ) 2724 { 2725 LOG.debug( I18n.msg( I18n.MSG_04123_MODIFY_SUCCESSFUL, modifyResponse ) ); 2726 } 2727 } 2728 else 2729 { 2730 // We have had an error 2731 if ( LOG.isDebugEnabled() ) 2732 { 2733 LOG.debug( I18n.msg( I18n.MSG_04122_MODIFY_FAILED, modifyResponse ) ); 2734 } 2735 } 2736 } 2737 2738 // Store the response into the future 2739 modifyFuture.set( modifyResponse ); 2740 2741 // Remove the future from the map 2742 removeFromFutureMaps( responseId ); 2743 } 2744 2745 2746 /** 2747 * Process the ModifyDnResponse received from the server 2748 * 2749 * @param modifyDnResponse The ModifyDnResponse to process 2750 * @param modifyDnFuture The ModifyDnFuture to feed 2751 * @param responseId The associated request message ID 2752 * @throws InterruptedException If the Future is interrupted 2753 */ 2754 private void modifyDnReceived( ModifyDnResponse modifyDnResponse, ModifyDnFuture modifyDnFuture, int responseId ) 2755 throws InterruptedException 2756 { 2757 if ( LOG.isDebugEnabled() ) 2758 { 2759 if ( modifyDnResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 2760 { 2761 // Everything is fine, return the response 2762 LOG.debug( I18n.msg( I18n.MSG_04125_MODIFYDN_SUCCESSFUL, modifyDnResponse ) ); 2763 } 2764 else 2765 { 2766 // We have had an error 2767 LOG.debug( I18n.msg( I18n.MSG_04124_MODIFYDN_FAILED, modifyDnResponse ) ); 2768 } 2769 } 2770 2771 // Store the response into the future 2772 modifyDnFuture.set( modifyDnResponse ); 2773 2774 // Remove the future from the map 2775 removeFromFutureMaps( responseId ); 2776 } 2777 2778 2779 /** 2780 * Process the SearchResultDone received from the server 2781 * 2782 * @param searchResultDone The SearchResultDone to process 2783 * @param searchFuture The SearchFuture to feed 2784 * @param responseId The associated request message ID 2785 * @throws InterruptedException If the Future is interrupted 2786 */ 2787 private void searchResultDoneReceived( SearchResultDone searchResultDone, SearchFuture searchFuture, 2788 int responseId ) throws InterruptedException 2789 { 2790 if ( LOG.isDebugEnabled() ) 2791 { 2792 if ( searchResultDone.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 2793 { 2794 // Everything is fine, return the response 2795 LOG.debug( I18n.msg( I18n.MSG_04131_SEARCH_SUCCESSFUL, searchResultDone ) ); 2796 } 2797 else 2798 { 2799 // We have had an error 2800 LOG.debug( I18n.msg( I18n.MSG_04129_SEARCH_FAILED, searchResultDone ) ); 2801 } 2802 } 2803 2804 // Store the response into the future 2805 searchFuture.set( searchResultDone ); 2806 2807 // Remove the future from the map 2808 removeFromFutureMaps( responseId ); 2809 } 2810 2811 2812 /** 2813 * Process the SearchResultEntry received from the server 2814 * 2815 * @param searchResultEntry The SearchResultEntry to process 2816 * @param searchFuture The SearchFuture to feed 2817 * @throws InterruptedException If the Future is interrupted 2818 * @throws LdapException If we weren't able to create a new Entry 2819 */ 2820 private void searchResultEntryReceived( SearchResultEntry searchResultEntry, SearchFuture searchFuture ) 2821 throws InterruptedException, LdapException 2822 { 2823 if ( schemaManager != null ) 2824 { 2825 searchResultEntry.setEntry( new DefaultEntry( schemaManager, searchResultEntry.getEntry() ) ); 2826 } 2827 2828 if ( LOG.isDebugEnabled() ) 2829 { 2830 LOG.debug( I18n.msg( I18n.MSG_04128_SEARCH_ENTRY_FOUND, searchResultEntry ) ); 2831 } 2832 2833 // Store the response into the future 2834 searchFuture.set( searchResultEntry ); 2835 } 2836 2837 2838 /** 2839 * Process the SearchResultEntry received from the server 2840 * 2841 * @param searchResultReference The SearchResultReference to process 2842 * @param searchFuture The SearchFuture to feed 2843 * @throws InterruptedException If the Future is interrupted 2844 */ 2845 private void searchResultReferenceReceived( SearchResultReference searchResultReference, SearchFuture searchFuture ) 2846 throws InterruptedException 2847 { 2848 if ( LOG.isDebugEnabled() ) 2849 { 2850 LOG.debug( I18n.msg( I18n.MSG_04130_SEARCH_REFERENCE_FOUND, searchResultReference ) ); 2851 } 2852 2853 // Store the response into the future 2854 searchFuture.set( searchResultReference ); 2855 } 2856 2857 2858 /** 2859 * Handle the incoming LDAP messages. This is where we feed the cursor for search 2860 * requests, or call the listener. 2861 * 2862 * @param session The session that received a message 2863 * @param message The received message 2864 * @throws Exception If there is some error while processing the message 2865 */ 2866 @Override 2867 public void messageReceived( IoSession session, Object message ) throws Exception 2868 { 2869 // Feed the response and store it into the session 2870 Response response = ( Response ) message; 2871 2872 if ( LOG.isDebugEnabled() ) 2873 { 2874 LOG.debug( I18n.msg( I18n.MSG_04142_MESSAGE_RECEIVED, response ) ); 2875 } 2876 2877 int responseId = response.getMessageId(); 2878 2879 // this check is necessary to prevent adding an abandoned operation's 2880 // result(s) to corresponding queue 2881 ResponseFuture<? extends Response> responseFuture = peekFromFutureMap( responseId ); 2882 2883 boolean isNoD = isNoticeOfDisconnect( response ); 2884 2885 if ( ( responseFuture == null ) && !isNoD ) 2886 { 2887 if ( LOG.isInfoEnabled() ) 2888 { 2889 LOG.info( I18n.msg( I18n.MSG_04166_NO_FUTURE_ASSOCIATED_TO_MSG_ID_IGNORING, responseId ) ); 2890 } 2891 2892 return; 2893 } 2894 2895 if ( isNoD ) 2896 { 2897 // close the session 2898 session.closeNow(); 2899 2900 return; 2901 } 2902 2903 switch ( response.getType() ) 2904 { 2905 case ADD_RESPONSE: 2906 addReceived( ( AddResponse ) response, ( AddFuture ) responseFuture, responseId ); 2907 2908 break; 2909 2910 case BIND_RESPONSE: 2911 bindReceived( ( BindResponse ) response, ( BindFuture ) responseFuture, responseId ); 2912 2913 break; 2914 2915 case COMPARE_RESPONSE: 2916 compareReceived( ( CompareResponse ) response, ( CompareFuture ) responseFuture, responseId ); 2917 2918 break; 2919 2920 case DEL_RESPONSE: 2921 deleteReceived( ( DeleteResponse ) response, ( DeleteFuture ) responseFuture, responseId ); 2922 2923 break; 2924 2925 case EXTENDED_RESPONSE: 2926 extendedReceived( ( ExtendedResponse ) response, ( ExtendedFuture ) responseFuture, responseId ); 2927 2928 break; 2929 2930 case INTERMEDIATE_RESPONSE: 2931 intermediateReceived( ( IntermediateResponse ) response, responseFuture ); 2932 2933 break; 2934 2935 case MODIFY_RESPONSE: 2936 modifyReceived( ( ModifyResponse ) response, ( ModifyFuture ) responseFuture, responseId ); 2937 2938 break; 2939 2940 case MODIFYDN_RESPONSE: 2941 modifyDnReceived( ( ModifyDnResponse ) response, ( ModifyDnFuture ) responseFuture, responseId ); 2942 2943 break; 2944 2945 case SEARCH_RESULT_DONE: 2946 searchResultDoneReceived( ( SearchResultDone ) response, ( SearchFuture ) responseFuture, responseId ); 2947 2948 break; 2949 2950 case SEARCH_RESULT_ENTRY: 2951 searchResultEntryReceived( ( SearchResultEntry ) response, ( SearchFuture ) responseFuture ); 2952 2953 break; 2954 2955 case SEARCH_RESULT_REFERENCE: 2956 searchResultReferenceReceived( ( SearchResultReference ) response, ( SearchFuture ) responseFuture ); 2957 2958 break; 2959 2960 default: 2961 throw new IllegalStateException( I18n.err( I18n.ERR_04132_UNEXPECTED_RESPONSE_TYPE, response.getType() ) ); 2962 } 2963 } 2964 2965 2966 private ExtendedResponse handleOpaqueResponse( ExtendedResponse extendedResponse, ExtendedFuture extendedFuture ) 2967 throws DecoderException 2968 { 2969 if ( ( extendedResponse instanceof OpaqueExtendedResponse ) 2970 && ( Strings.isEmpty( extendedResponse.getResponseName() ) ) ) 2971 { 2972 ExtendedOperationFactory factory = codec.getExtendedResponseFactories(). 2973 get( extendedFuture.getExtendedRequest().getRequestName() ); 2974 2975 byte[] responseValue = ( ( OpaqueExtendedResponse ) extendedResponse ).getResponseValue(); 2976 2977 ExtendedResponse response; 2978 if ( responseValue != null ) 2979 { 2980 response = factory.newResponse( responseValue ); 2981 } 2982 else 2983 { 2984 response = factory.newResponse(); 2985 } 2986 2987 // Copy the controls 2988 for ( Control control : extendedResponse.getControls().values() ) 2989 { 2990 response.addControl( control ); 2991 } 2992 2993 // copy the LDAPResult 2994 response.getLdapResult().setDiagnosticMessage( extendedResponse.getLdapResult().getDiagnosticMessage() ); 2995 response.getLdapResult().setMatchedDn( extendedResponse.getLdapResult().getMatchedDn() ); 2996 response.getLdapResult().setReferral( extendedResponse.getLdapResult().getReferral() ); 2997 response.getLdapResult().setResultCode( extendedResponse.getLdapResult().getResultCode() ); 2998 2999 return response; 3000 } 3001 else 3002 { 3003 return extendedResponse; 3004 } 3005 } 3006 3007 /** 3008 * {@inheritDoc} 3009 */ 3010 @Override 3011 public void modify( Entry entry, ModificationOperation modOp ) throws LdapException 3012 { 3013 if ( entry == null ) 3014 { 3015 if ( LOG.isDebugEnabled() ) 3016 { 3017 LOG.debug( I18n.msg( I18n.MSG_04140_NULL_ENTRY_MODIFY ) ); 3018 } 3019 3020 throw new IllegalArgumentException( I18n.err( I18n.ERR_04133_NULL_MODIFIED_ENTRY ) ); 3021 } 3022 3023 ModifyRequest modReq = new ModifyRequestImpl(); 3024 modReq.setName( entry.getDn() ); 3025 3026 Iterator<Attribute> itr = entry.iterator(); 3027 3028 while ( itr.hasNext() ) 3029 { 3030 modReq.addModification( itr.next(), modOp ); 3031 } 3032 3033 ModifyResponse modifyResponse = modify( modReq ); 3034 3035 processResponse( modifyResponse ); 3036 } 3037 3038 3039 /** 3040 * {@inheritDoc} 3041 */ 3042 @Override 3043 public void modify( Dn dn, Modification... modifications ) throws LdapException 3044 { 3045 if ( dn == null ) 3046 { 3047 if ( LOG.isDebugEnabled() ) 3048 { 3049 LOG.debug( I18n.msg( I18n.MSG_04139_NULL_DN_MODIFY ) ); 3050 } 3051 3052 throw new IllegalArgumentException( I18n.err( I18n.ERR_04134_NULL_MODIFIED_DN ) ); 3053 } 3054 3055 if ( ( modifications == null ) || ( modifications.length == 0 ) ) 3056 { 3057 String msg = I18n.err( I18n.ERR_04135_CANNOT_PROCESS_NO_MODIFICATION_MOD ); 3058 3059 if ( LOG.isDebugEnabled() ) 3060 { 3061 LOG.debug( msg ); 3062 } 3063 3064 throw new IllegalArgumentException( msg ); 3065 } 3066 3067 ModifyRequest modReq = new ModifyRequestImpl(); 3068 modReq.setName( dn ); 3069 3070 for ( Modification modification : modifications ) 3071 { 3072 modReq.addModification( modification ); 3073 } 3074 3075 ModifyResponse modifyResponse = modify( modReq ); 3076 3077 processResponse( modifyResponse ); 3078 } 3079 3080 3081 /** 3082 * {@inheritDoc} 3083 */ 3084 @Override 3085 public void modify( String dn, Modification... modifications ) throws LdapException 3086 { 3087 modify( new Dn( dn ), modifications ); 3088 } 3089 3090 3091 /** 3092 * {@inheritDoc} 3093 */ 3094 @Override 3095 public ModifyResponse modify( ModifyRequest modRequest ) throws LdapException 3096 { 3097 if ( modRequest == null ) 3098 { 3099 String msg = I18n.err( I18n.ERR_04136_CANNOT_PROCESS_NULL_MOD_REQ ); 3100 3101 if ( LOG.isDebugEnabled() ) 3102 { 3103 LOG.debug( msg ); 3104 } 3105 3106 throw new IllegalArgumentException( msg ); 3107 } 3108 3109 ModifyFuture modifyFuture = modifyAsync( modRequest ); 3110 3111 // Get the result from the future 3112 try 3113 { 3114 // Read the response, waiting for it if not available immediately 3115 // Get the response, blocking 3116 ModifyResponse modifyResponse = modifyFuture.get( timeout, TimeUnit.MILLISECONDS ); 3117 3118 if ( modifyResponse == null ) 3119 { 3120 // We didn't received anything : this is an error 3121 if ( LOG.isErrorEnabled() ) 3122 { 3123 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Modify" ) ); 3124 } 3125 3126 throw new LdapException( TIME_OUT_ERROR ); 3127 } 3128 3129 if ( modifyResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 3130 { 3131 // Everything is fine, return the response 3132 if ( LOG.isDebugEnabled() ) 3133 { 3134 LOG.debug( I18n.msg( I18n.MSG_04123_MODIFY_SUCCESSFUL, modifyResponse ) ); 3135 } 3136 } 3137 else 3138 { 3139 if ( modifyResponse instanceof ModifyNoDResponse ) 3140 { 3141 // A NoticeOfDisconnect : deserves a special treatment 3142 throw new LdapException( modifyResponse.getLdapResult().getDiagnosticMessage() ); 3143 } 3144 3145 // We have had an error 3146 if ( LOG.isDebugEnabled() ) 3147 { 3148 LOG.debug( I18n.msg( I18n.MSG_04122_MODIFY_FAILED, modifyResponse ) ); 3149 } 3150 } 3151 3152 return modifyResponse; 3153 } 3154 catch ( Exception ie ) 3155 { 3156 // Catch all other exceptions 3157 LOG.error( NO_RESPONSE_ERROR, ie ); 3158 3159 // Send an abandon request 3160 if ( !modifyFuture.isCancelled() ) 3161 { 3162 abandon( modRequest.getMessageId() ); 3163 } 3164 3165 throw new LdapException( ie.getMessage(), ie ); 3166 } 3167 } 3168 3169 3170 /** 3171 * {@inheritDoc} 3172 */ 3173 @Override 3174 public ModifyFuture modifyAsync( ModifyRequest modRequest ) throws LdapException 3175 { 3176 if ( modRequest == null ) 3177 { 3178 String msg = I18n.err( I18n.ERR_04136_CANNOT_PROCESS_NULL_MOD_REQ ); 3179 3180 if ( LOG.isDebugEnabled() ) 3181 { 3182 LOG.debug( msg ); 3183 } 3184 3185 throw new IllegalArgumentException( msg ); 3186 } 3187 3188 if ( modRequest.getName() == null ) 3189 { 3190 String msg = I18n.err( I18n.ERR_04137_CANNOT_PROCESS_MOD_NULL_DN ); 3191 3192 if ( LOG.isDebugEnabled() ) 3193 { 3194 LOG.debug( msg ); 3195 } 3196 3197 throw new IllegalArgumentException( msg ); 3198 } 3199 3200 // try to connect, if we aren't already connected. 3201 connect(); 3202 3203 checkSession(); 3204 3205 int newId = messageId.incrementAndGet(); 3206 modRequest.setMessageId( newId ); 3207 3208 ModifyFuture modifyFuture = new ModifyFuture( this, newId ); 3209 addToFutureMap( newId, modifyFuture ); 3210 3211 // Send the request to the server 3212 writeRequest( modRequest ); 3213 3214 // Ok, done return the future 3215 return modifyFuture; 3216 } 3217 3218 3219 /** 3220 * {@inheritDoc} 3221 */ 3222 @Override 3223 public void rename( String entryDn, String newRdn ) throws LdapException 3224 { 3225 rename( entryDn, newRdn, true ); 3226 } 3227 3228 3229 /** 3230 * {@inheritDoc} 3231 */ 3232 @Override 3233 public void rename( Dn entryDn, Rdn newRdn ) throws LdapException 3234 { 3235 rename( entryDn, newRdn, true ); 3236 } 3237 3238 3239 /** 3240 * {@inheritDoc} 3241 */ 3242 @Override 3243 public void rename( String entryDn, String newRdn, boolean deleteOldRdn ) throws LdapException 3244 { 3245 if ( entryDn == null ) 3246 { 3247 String msg = I18n.err( I18n.ERR_04138_CANNOT_PROCESS_RENAME_NULL_DN ); 3248 3249 if ( LOG.isDebugEnabled() ) 3250 { 3251 LOG.debug( msg ); 3252 } 3253 3254 throw new IllegalArgumentException( msg ); 3255 } 3256 3257 if ( newRdn == null ) 3258 { 3259 String msg = I18n.err( I18n.ERR_04139_CANNOT_PROCESS_RENAME_NULL_RDN ); 3260 3261 if ( LOG.isDebugEnabled() ) 3262 { 3263 LOG.debug( msg ); 3264 } 3265 3266 throw new IllegalArgumentException( msg ); 3267 } 3268 3269 try 3270 { 3271 rename( new Dn( entryDn ), new Rdn( newRdn ), deleteOldRdn ); 3272 } 3273 catch ( LdapInvalidDnException e ) 3274 { 3275 LOG.error( e.getMessage(), e ); 3276 throw new LdapException( e.getMessage(), e ); 3277 } 3278 } 3279 3280 3281 /** 3282 * {@inheritDoc} 3283 */ 3284 @Override 3285 public void rename( Dn entryDn, Rdn newRdn, boolean deleteOldRdn ) throws LdapException 3286 { 3287 if ( entryDn == null ) 3288 { 3289 String msg = I18n.err( I18n.ERR_04138_CANNOT_PROCESS_RENAME_NULL_DN ); 3290 3291 if ( LOG.isDebugEnabled() ) 3292 { 3293 LOG.debug( msg ); 3294 } 3295 3296 throw new IllegalArgumentException( msg ); 3297 } 3298 3299 if ( newRdn == null ) 3300 { 3301 String msg = I18n.err( I18n.ERR_04139_CANNOT_PROCESS_RENAME_NULL_RDN ); 3302 3303 if ( LOG.isDebugEnabled() ) 3304 { 3305 LOG.debug( msg ); 3306 } 3307 3308 throw new IllegalArgumentException( msg ); 3309 } 3310 3311 ModifyDnRequest modDnRequest = new ModifyDnRequestImpl(); 3312 modDnRequest.setName( entryDn ); 3313 modDnRequest.setNewRdn( newRdn ); 3314 modDnRequest.setDeleteOldRdn( deleteOldRdn ); 3315 3316 ModifyDnResponse modifyDnResponse = modifyDn( modDnRequest ); 3317 3318 processResponse( modifyDnResponse ); 3319 } 3320 3321 3322 /** 3323 * {@inheritDoc} 3324 */ 3325 @Override 3326 public void move( String entryDn, String newSuperiorDn ) throws LdapException 3327 { 3328 if ( entryDn == null ) 3329 { 3330 String msg = I18n.err( I18n.ERR_04140_CANNOT_PROCESS_MOVE_NULL_DN ); 3331 3332 if ( LOG.isDebugEnabled() ) 3333 { 3334 LOG.debug( msg ); 3335 } 3336 3337 throw new IllegalArgumentException( msg ); 3338 } 3339 3340 if ( newSuperiorDn == null ) 3341 { 3342 String msg = I18n.err( I18n.ERR_04141_CANNOT_PROCESS_MOVE_NULL_SUPERIOR ); 3343 3344 if ( LOG.isDebugEnabled() ) 3345 { 3346 LOG.debug( msg ); 3347 } 3348 3349 throw new IllegalArgumentException( msg ); 3350 } 3351 3352 try 3353 { 3354 move( new Dn( entryDn ), new Dn( newSuperiorDn ) ); 3355 } 3356 catch ( LdapInvalidDnException e ) 3357 { 3358 LOG.error( e.getMessage(), e ); 3359 throw new LdapException( e.getMessage(), e ); 3360 } 3361 } 3362 3363 3364 /** 3365 * {@inheritDoc} 3366 */ 3367 @Override 3368 public void move( Dn entryDn, Dn newSuperiorDn ) throws LdapException 3369 { 3370 if ( entryDn == null ) 3371 { 3372 String msg = I18n.err( I18n.ERR_04140_CANNOT_PROCESS_MOVE_NULL_DN ); 3373 3374 if ( LOG.isDebugEnabled() ) 3375 { 3376 LOG.debug( msg ); 3377 } 3378 3379 throw new IllegalArgumentException( msg ); 3380 } 3381 3382 if ( newSuperiorDn == null ) 3383 { 3384 String msg = I18n.err( I18n.ERR_04141_CANNOT_PROCESS_MOVE_NULL_SUPERIOR ); 3385 3386 if ( LOG.isDebugEnabled() ) 3387 { 3388 LOG.debug( msg ); 3389 } 3390 3391 throw new IllegalArgumentException( msg ); 3392 } 3393 3394 ModifyDnRequest modDnRequest = new ModifyDnRequestImpl(); 3395 modDnRequest.setName( entryDn ); 3396 modDnRequest.setNewSuperior( newSuperiorDn ); 3397 3398 modDnRequest.setNewRdn( entryDn.getRdn() ); 3399 3400 ModifyDnResponse modifyDnResponse = modifyDn( modDnRequest ); 3401 3402 processResponse( modifyDnResponse ); 3403 } 3404 3405 3406 /** 3407 * {@inheritDoc} 3408 */ 3409 @Override 3410 public void moveAndRename( Dn entryDn, Dn newDn ) throws LdapException 3411 { 3412 moveAndRename( entryDn, newDn, true ); 3413 } 3414 3415 3416 /** 3417 * {@inheritDoc} 3418 */ 3419 @Override 3420 public void moveAndRename( String entryDn, String newDn ) throws LdapException 3421 { 3422 moveAndRename( new Dn( entryDn ), new Dn( newDn ), true ); 3423 } 3424 3425 3426 /** 3427 * {@inheritDoc} 3428 */ 3429 @Override 3430 public void moveAndRename( Dn entryDn, Dn newDn, boolean deleteOldRdn ) throws LdapException 3431 { 3432 // Check the parameters first 3433 if ( entryDn == null ) 3434 { 3435 throw new IllegalArgumentException( I18n.err( I18n.ERR_04142_NULL_ENTRY_DN ) ); 3436 } 3437 3438 if ( entryDn.isRootDse() ) 3439 { 3440 throw new IllegalArgumentException( I18n.err( I18n.ERR_04143_CANNOT_MOVE_ROOT_DSE ) ); 3441 } 3442 3443 if ( newDn == null ) 3444 { 3445 throw new IllegalArgumentException( I18n.err( I18n.ERR_04144_NULL_NEW_DN ) ); 3446 } 3447 3448 if ( newDn.isRootDse() ) 3449 { 3450 throw new IllegalArgumentException( I18n.err( I18n.ERR_04145_ROOT_DSE_CANNOT_BE_TARGET ) ); 3451 } 3452 3453 // Create the request 3454 ModifyDnRequest modDnRequest = new ModifyDnRequestImpl(); 3455 modDnRequest.setName( entryDn ); 3456 modDnRequest.setNewRdn( newDn.getRdn() ); 3457 3458 // Check if we really need to specify newSuperior. 3459 // newSuperior is optional [RFC4511, section 4.9] 3460 // Some servers (e.g. OpenDJ 2.6) require a special privilege if 3461 // newSuperior is specified even if it is the same as the old one. Therefore let's not 3462 // specify it if we do not need it. This is better interoperability. 3463 Dn newDnParent = newDn.getParent(); 3464 if ( newDnParent != null && !newDnParent.equals( entryDn.getParent() ) ) 3465 { 3466 modDnRequest.setNewSuperior( newDnParent ); 3467 } 3468 3469 modDnRequest.setDeleteOldRdn( deleteOldRdn ); 3470 3471 ModifyDnResponse modifyDnResponse = modifyDn( modDnRequest ); 3472 3473 processResponse( modifyDnResponse ); 3474 } 3475 3476 3477 /** 3478 * {@inheritDoc} 3479 */ 3480 @Override 3481 public void moveAndRename( String entryDn, String newDn, boolean deleteOldRdn ) throws LdapException 3482 { 3483 moveAndRename( new Dn( entryDn ), new Dn( newDn ), true ); 3484 } 3485 3486 3487 /** 3488 * {@inheritDoc} 3489 */ 3490 @Override 3491 public ModifyDnResponse modifyDn( ModifyDnRequest modDnRequest ) throws LdapException 3492 { 3493 if ( modDnRequest == null ) 3494 { 3495 String msg = I18n.err( I18n.ERR_04145_ROOT_DSE_CANNOT_BE_TARGET ); 3496 3497 if ( LOG.isDebugEnabled() ) 3498 { 3499 LOG.debug( msg ); 3500 } 3501 3502 throw new IllegalArgumentException( msg ); 3503 } 3504 3505 ModifyDnFuture modifyDnFuture = modifyDnAsync( modDnRequest ); 3506 3507 // Get the result from the future 3508 try 3509 { 3510 // Read the response, waiting for it if not available immediately 3511 // Get the response, blocking 3512 ModifyDnResponse modifyDnResponse = modifyDnFuture.get( timeout, TimeUnit.MILLISECONDS ); 3513 3514 if ( modifyDnResponse == null ) 3515 { 3516 // We didn't received anything : this is an error 3517 if ( LOG.isErrorEnabled() ) 3518 { 3519 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "ModifyDn" ) ); 3520 } 3521 3522 throw new LdapException( TIME_OUT_ERROR ); 3523 } 3524 3525 if ( modifyDnResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 3526 { 3527 // Everything is fine, return the response 3528 if ( LOG.isDebugEnabled() ) 3529 { 3530 LOG.debug( I18n.msg( I18n.MSG_04125_MODIFYDN_SUCCESSFUL, modifyDnResponse ) ); 3531 } 3532 } 3533 else 3534 { 3535 // We have had an error 3536 if ( LOG.isDebugEnabled() ) 3537 { 3538 LOG.debug( I18n.msg( I18n.MSG_04124_MODIFYDN_FAILED, modifyDnResponse ) ); 3539 } 3540 } 3541 3542 return modifyDnResponse; 3543 } 3544 catch ( Exception ie ) 3545 { 3546 // Catch all other exceptions 3547 LOG.error( NO_RESPONSE_ERROR, ie ); 3548 3549 // Send an abandon request 3550 if ( !modifyDnFuture.isCancelled() ) 3551 { 3552 abandon( modDnRequest.getMessageId() ); 3553 } 3554 3555 throw new LdapException( NO_RESPONSE_ERROR, ie ); 3556 } 3557 } 3558 3559 3560 /** 3561 * {@inheritDoc} 3562 */ 3563 @Override 3564 public ModifyDnFuture modifyDnAsync( ModifyDnRequest modDnRequest ) throws LdapException 3565 { 3566 if ( modDnRequest == null ) 3567 { 3568 String msg = I18n.err( I18n.ERR_04145_ROOT_DSE_CANNOT_BE_TARGET ); 3569 3570 if ( LOG.isDebugEnabled() ) 3571 { 3572 LOG.debug( msg ); 3573 } 3574 3575 throw new IllegalArgumentException( msg ); 3576 } 3577 3578 if ( modDnRequest.getName() == null ) 3579 { 3580 String msg = I18n.err( I18n.ERR_04137_CANNOT_PROCESS_MOD_NULL_DN ); 3581 3582 if ( LOG.isDebugEnabled() ) 3583 { 3584 LOG.debug( msg ); 3585 } 3586 3587 throw new IllegalArgumentException( msg ); 3588 } 3589 3590 if ( ( modDnRequest.getNewSuperior() == null ) && ( modDnRequest.getNewRdn() == null ) ) 3591 { 3592 String msg = I18n.err( I18n.ERR_04147_CANNOT_PROCESS_MOD_NULL_DN_SUP ); 3593 3594 if ( LOG.isDebugEnabled() ) 3595 { 3596 LOG.debug( msg ); 3597 } 3598 3599 throw new IllegalArgumentException( msg ); 3600 } 3601 3602 // try to connect, if we aren't already connected. 3603 connect(); 3604 3605 checkSession(); 3606 3607 int newId = messageId.incrementAndGet(); 3608 modDnRequest.setMessageId( newId ); 3609 3610 ModifyDnFuture modifyDnFuture = new ModifyDnFuture( this, newId ); 3611 addToFutureMap( newId, modifyDnFuture ); 3612 3613 // Send the request to the server 3614 writeRequest( modDnRequest ); 3615 3616 // Ok, done return the future 3617 return modifyDnFuture; 3618 } 3619 3620 3621 /** 3622 * {@inheritDoc} 3623 */ 3624 @Override 3625 public void delete( String dn ) throws LdapException 3626 { 3627 delete( new Dn( dn ) ); 3628 } 3629 3630 3631 /** 3632 * {@inheritDoc} 3633 */ 3634 @Override 3635 public void delete( Dn dn ) throws LdapException 3636 { 3637 DeleteRequest deleteRequest = new DeleteRequestImpl(); 3638 deleteRequest.setName( dn ); 3639 3640 DeleteResponse deleteResponse = delete( deleteRequest ); 3641 3642 processResponse( deleteResponse ); 3643 } 3644 3645 3646 /** 3647 * deletes the entry with the given Dn, and all its children 3648 * 3649 * @param dn the target entry's Dn 3650 * @throws LdapException If the Dn is not valid or if the deletion failed 3651 */ 3652 public void deleteTree( Dn dn ) throws LdapException 3653 { 3654 String treeDeleteOid = "1.2.840.113556.1.4.805"; 3655 3656 if ( isControlSupported( treeDeleteOid ) ) 3657 { 3658 DeleteRequest deleteRequest = new DeleteRequestImpl(); 3659 deleteRequest.setName( dn ); 3660 deleteRequest.addControl( new OpaqueControl( treeDeleteOid ) ); 3661 DeleteResponse deleteResponse = delete( deleteRequest ); 3662 3663 processResponse( deleteResponse ); 3664 } 3665 else 3666 { 3667 String msg = I18n.err( I18n.ERR_04148_SUBTREE_CONTROL_NOT_SUPPORTED ); 3668 LOG.error( msg ); 3669 throw new LdapException( msg ); 3670 } 3671 } 3672 3673 3674 /** 3675 * deletes the entry with the given Dn, and all its children 3676 * 3677 * @param dn the target entry's Dn as a String 3678 * @throws LdapException If the Dn is not valid or if the deletion failed 3679 */ 3680 public void deleteTree( String dn ) throws LdapException 3681 { 3682 try 3683 { 3684 String treeDeleteOid = "1.2.840.113556.1.4.805"; 3685 Dn newDn = new Dn( dn ); 3686 3687 if ( isControlSupported( treeDeleteOid ) ) 3688 { 3689 DeleteRequest deleteRequest = new DeleteRequestImpl(); 3690 deleteRequest.setName( newDn ); 3691 deleteRequest.addControl( new OpaqueControl( treeDeleteOid ) ); 3692 DeleteResponse deleteResponse = delete( deleteRequest ); 3693 3694 processResponse( deleteResponse ); 3695 } 3696 else 3697 { 3698 String msg = I18n.err( I18n.ERR_04148_SUBTREE_CONTROL_NOT_SUPPORTED ); 3699 LOG.error( msg ); 3700 throw new LdapException( msg ); 3701 } 3702 } 3703 catch ( LdapInvalidDnException e ) 3704 { 3705 LOG.error( e.getMessage(), e ); 3706 throw new LdapException( e.getMessage(), e ); 3707 } 3708 } 3709 3710 3711 /** 3712 * {@inheritDoc} 3713 */ 3714 @Override 3715 public DeleteResponse delete( DeleteRequest deleteRequest ) throws LdapException 3716 { 3717 if ( deleteRequest == null ) 3718 { 3719 String msg = I18n.err( I18n.ERR_04149_CANNOT_PROCESS_NULL_DEL_REQ ); 3720 3721 if ( LOG.isDebugEnabled() ) 3722 { 3723 LOG.debug( msg ); 3724 } 3725 3726 throw new IllegalArgumentException( msg ); 3727 } 3728 3729 DeleteFuture deleteFuture = deleteAsync( deleteRequest ); 3730 3731 // Get the result from the future 3732 try 3733 { 3734 // Read the response, waiting for it if not available immediately 3735 // Get the response, blocking 3736 DeleteResponse delResponse = deleteFuture.get( timeout, TimeUnit.MILLISECONDS ); 3737 3738 if ( delResponse == null ) 3739 { 3740 // We didn't received anything : this is an error 3741 if ( LOG.isErrorEnabled() ) 3742 { 3743 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Delete" ) ); 3744 } 3745 3746 throw new LdapException( TIME_OUT_ERROR ); 3747 } 3748 3749 if ( delResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 3750 { 3751 // Everything is fine, return the response 3752 if ( LOG.isDebugEnabled() ) 3753 { 3754 LOG.debug( I18n.msg( I18n.MSG_04116_DELETE_SUCCESSFUL, delResponse ) ); 3755 } 3756 } 3757 else 3758 { 3759 // We have had an error 3760 if ( LOG.isDebugEnabled() ) 3761 { 3762 LOG.debug( I18n.msg( I18n.MSG_04115_DELETE_FAILED, delResponse ) ); 3763 } 3764 } 3765 3766 return delResponse; 3767 } 3768 catch ( Exception ie ) 3769 { 3770 // Catch all other exceptions 3771 LOG.error( NO_RESPONSE_ERROR, ie ); 3772 3773 // Send an abandon request 3774 if ( !deleteFuture.isCancelled() ) 3775 { 3776 abandon( deleteRequest.getMessageId() ); 3777 } 3778 3779 throw new LdapException( NO_RESPONSE_ERROR, ie ); 3780 } 3781 } 3782 3783 3784 /** 3785 * {@inheritDoc} 3786 */ 3787 @Override 3788 public DeleteFuture deleteAsync( DeleteRequest deleteRequest ) throws LdapException 3789 { 3790 if ( deleteRequest == null ) 3791 { 3792 String msg = I18n.err( I18n.ERR_04149_CANNOT_PROCESS_NULL_DEL_REQ ); 3793 3794 if ( LOG.isDebugEnabled() ) 3795 { 3796 LOG.debug( msg ); 3797 } 3798 3799 throw new IllegalArgumentException( msg ); 3800 } 3801 3802 if ( deleteRequest.getName() == null ) 3803 { 3804 String msg = I18n.err( I18n.ERR_04150_CANNOT_PROCESS_NULL_DEL_NULL_DN ); 3805 3806 if ( LOG.isDebugEnabled() ) 3807 { 3808 LOG.debug( msg ); 3809 } 3810 3811 throw new IllegalArgumentException( msg ); 3812 } 3813 3814 // try to connect, if we aren't already connected. 3815 connect(); 3816 3817 checkSession(); 3818 3819 int newId = messageId.incrementAndGet(); 3820 3821 deleteRequest.setMessageId( newId ); 3822 3823 DeleteFuture deleteFuture = new DeleteFuture( this, newId ); 3824 addToFutureMap( newId, deleteFuture ); 3825 3826 // Send the request to the server 3827 writeRequest( deleteRequest ); 3828 3829 // Ok, done return the future 3830 return deleteFuture; 3831 } 3832 3833 3834 /** 3835 * {@inheritDoc} 3836 */ 3837 @Override 3838 public boolean compare( String dn, String attributeName, String value ) throws LdapException 3839 { 3840 return compare( new Dn( dn ), attributeName, value ); 3841 } 3842 3843 3844 /** 3845 * {@inheritDoc} 3846 */ 3847 @Override 3848 public boolean compare( String dn, String attributeName, byte[] value ) throws LdapException 3849 { 3850 return compare( new Dn( dn ), attributeName, value ); 3851 } 3852 3853 3854 /** 3855 * {@inheritDoc} 3856 */ 3857 @Override 3858 public boolean compare( String dn, String attributeName, Value value ) throws LdapException 3859 { 3860 return compare( new Dn( dn ), attributeName, value ); 3861 } 3862 3863 3864 /** 3865 * {@inheritDoc} 3866 */ 3867 @Override 3868 public boolean compare( Dn dn, String attributeName, String value ) throws LdapException 3869 { 3870 CompareRequest compareRequest = new CompareRequestImpl(); 3871 compareRequest.setName( dn ); 3872 compareRequest.setAttributeId( attributeName ); 3873 compareRequest.setAssertionValue( value ); 3874 3875 CompareResponse compareResponse = compare( compareRequest ); 3876 3877 return processResponse( compareResponse ); 3878 } 3879 3880 3881 /** 3882 * {@inheritDoc} 3883 */ 3884 @Override 3885 public boolean compare( Dn dn, String attributeName, byte[] value ) throws LdapException 3886 { 3887 CompareRequest compareRequest = new CompareRequestImpl(); 3888 compareRequest.setName( dn ); 3889 compareRequest.setAttributeId( attributeName ); 3890 compareRequest.setAssertionValue( value ); 3891 3892 CompareResponse compareResponse = compare( compareRequest ); 3893 3894 return processResponse( compareResponse ); 3895 } 3896 3897 3898 /** 3899 * {@inheritDoc} 3900 */ 3901 @Override 3902 public boolean compare( Dn dn, String attributeName, Value value ) throws LdapException 3903 { 3904 CompareRequest compareRequest = new CompareRequestImpl(); 3905 compareRequest.setName( dn ); 3906 compareRequest.setAttributeId( attributeName ); 3907 3908 if ( value.isHumanReadable() ) 3909 { 3910 compareRequest.setAssertionValue( value.getString() ); 3911 } 3912 else 3913 { 3914 compareRequest.setAssertionValue( value.getBytes() ); 3915 } 3916 3917 CompareResponse compareResponse = compare( compareRequest ); 3918 3919 return processResponse( compareResponse ); 3920 } 3921 3922 3923 /** 3924 * {@inheritDoc} 3925 */ 3926 @Override 3927 public CompareResponse compare( CompareRequest compareRequest ) throws LdapException 3928 { 3929 if ( compareRequest == null ) 3930 { 3931 String msg = I18n.err( I18n.ERR_04151_CANNOT_PROCESS_NULL_COMP_REQ ); 3932 3933 if ( LOG.isDebugEnabled() ) 3934 { 3935 LOG.debug( msg ); 3936 } 3937 3938 throw new IllegalArgumentException( msg ); 3939 } 3940 3941 CompareFuture compareFuture = compareAsync( compareRequest ); 3942 3943 // Get the result from the future 3944 try 3945 { 3946 // Read the response, waiting for it if not available immediately 3947 // Get the response, blocking 3948 CompareResponse compareResponse = compareFuture.get( timeout, TimeUnit.MILLISECONDS ); 3949 3950 if ( compareResponse == null ) 3951 { 3952 // We didn't received anything : this is an error 3953 if ( LOG.isErrorEnabled() ) 3954 { 3955 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Compare" ) ); 3956 } 3957 3958 throw new LdapException( TIME_OUT_ERROR ); 3959 } 3960 3961 if ( compareResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 3962 { 3963 // Everything is fine, return the response 3964 if ( LOG.isDebugEnabled() ) 3965 { 3966 LOG.debug( I18n.msg( I18n.MSG_04114_COMPARE_SUCCESSFUL, compareResponse ) ); 3967 } 3968 } 3969 else 3970 { 3971 // We have had an error 3972 if ( LOG.isDebugEnabled() ) 3973 { 3974 LOG.debug( I18n.msg( I18n.MSG_04113_COMPARE_FAILED, compareResponse ) ); 3975 } 3976 } 3977 3978 return compareResponse; 3979 } 3980 catch ( Exception ie ) 3981 { 3982 // Catch all other exceptions 3983 LOG.error( NO_RESPONSE_ERROR, ie ); 3984 3985 // Send an abandon request 3986 if ( !compareFuture.isCancelled() ) 3987 { 3988 abandon( compareRequest.getMessageId() ); 3989 } 3990 3991 throw new LdapException( NO_RESPONSE_ERROR, ie ); 3992 } 3993 } 3994 3995 3996 /** 3997 * {@inheritDoc} 3998 */ 3999 @Override 4000 public CompareFuture compareAsync( CompareRequest compareRequest ) throws LdapException 4001 { 4002 if ( compareRequest == null ) 4003 { 4004 String msg = I18n.err( I18n.ERR_04151_CANNOT_PROCESS_NULL_COMP_REQ ); 4005 4006 if ( LOG.isDebugEnabled() ) 4007 { 4008 LOG.debug( msg ); 4009 } 4010 4011 throw new IllegalArgumentException( msg ); 4012 } 4013 4014 if ( compareRequest.getName() == null ) 4015 { 4016 String msg = I18n.err( I18n.ERR_04152_CANNOT_PROCESS_NULL_DN_COMP_REQ ); 4017 4018 if ( LOG.isDebugEnabled() ) 4019 { 4020 LOG.debug( msg ); 4021 } 4022 4023 throw new IllegalArgumentException( msg ); 4024 } 4025 4026 // try to connect, if we aren't already connected. 4027 connect(); 4028 4029 checkSession(); 4030 4031 int newId = messageId.incrementAndGet(); 4032 4033 compareRequest.setMessageId( newId ); 4034 4035 CompareFuture compareFuture = new CompareFuture( this, newId ); 4036 addToFutureMap( newId, compareFuture ); 4037 4038 // Send the request to the server 4039 writeRequest( compareRequest ); 4040 4041 // Ok, done return the future 4042 return compareFuture; 4043 } 4044 4045 4046 /** 4047 * {@inheritDoc} 4048 */ 4049 @Override 4050 public ExtendedResponse extended( String oid ) throws LdapException 4051 { 4052 return extended( oid, null ); 4053 } 4054 4055 4056 /** 4057 * {@inheritDoc} 4058 */ 4059 @Override 4060 public ExtendedResponse extended( String oid, byte[] value ) throws LdapException 4061 { 4062 try 4063 { 4064 return extended( Oid.fromString( oid ), value ); 4065 } 4066 catch ( DecoderException e ) 4067 { 4068 String msg = I18n.err( I18n.ERR_04153_OID_DECODING_FAILURE, oid ); 4069 LOG.error( msg ); 4070 throw new LdapException( msg, e ); 4071 } 4072 } 4073 4074 4075 /** 4076 * {@inheritDoc} 4077 */ 4078 @Override 4079 public ExtendedResponse extended( Oid oid ) throws LdapException 4080 { 4081 return extended( oid, null ); 4082 } 4083 4084 4085 /** 4086 * {@inheritDoc} 4087 */ 4088 @Override 4089 public ExtendedResponse extended( Oid oid, byte[] value ) throws LdapException 4090 { 4091 Map<String, ExtendedOperationFactory> factories = LdapApiServiceFactory.getSingleton().getExtendedRequestFactories(); 4092 String oidStr = oid.toString(); 4093 4094 ExtendedOperationFactory factory = factories.get( oidStr ); 4095 4096 if ( factory != null ) 4097 { 4098 try 4099 { 4100 if ( value == null ) 4101 { 4102 return extended( factory.newRequest() ); 4103 } 4104 else 4105 { 4106 return extended( factory.newRequest( value ) ); 4107 } 4108 } 4109 catch ( DecoderException de ) 4110 { 4111 throw new LdapNoSuchObjectException( de.getMessage() ); 4112 } 4113 } 4114 else 4115 { 4116 return extended( new OpaqueExtendedRequest( oidStr, value ) ); 4117 } 4118 } 4119 4120 4121 /** 4122 * {@inheritDoc} 4123 */ 4124 @Override 4125 public ExtendedResponse extended( ExtendedRequest extendedRequest ) throws LdapException 4126 { 4127 if ( extendedRequest == null ) 4128 { 4129 String msg = I18n.err( I18n.ERR_04154_CANNOT_PROCESS_NULL_EXT_REQ ); 4130 4131 if ( LOG.isDebugEnabled() ) 4132 { 4133 LOG.debug( msg ); 4134 } 4135 4136 throw new IllegalArgumentException( msg ); 4137 } 4138 4139 ExtendedFuture extendedFuture = extendedAsync( extendedRequest ); 4140 4141 // Get the result from the future 4142 try 4143 { 4144 // Read the response, waiting for it if not available immediately 4145 // Get the response, blocking 4146 ExtendedResponse response = ( ExtendedResponse ) extendedFuture 4147 .get( timeout, TimeUnit.MILLISECONDS ); 4148 4149 if ( response == null ) 4150 { 4151 // We didn't received anything : this is an error 4152 if ( LOG.isErrorEnabled() ) 4153 { 4154 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Extended" ) ); 4155 } 4156 4157 throw new LdapException( TIME_OUT_ERROR ); 4158 } 4159 4160 if ( response.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 4161 { 4162 // Everything is fine, return the response 4163 if ( LOG.isDebugEnabled() ) 4164 { 4165 LOG.debug( I18n.msg( I18n.MSG_04118_EXTENDED_SUCCESSFUL, response ) ); 4166 } 4167 } 4168 else 4169 { 4170 // We have had an error 4171 if ( LOG.isDebugEnabled() ) 4172 { 4173 LOG.debug( I18n.msg( I18n.MSG_04117_EXTENDED_FAILED, response ) ); 4174 } 4175 } 4176 4177 // Get back the response. It's still an opaque response 4178 if ( Strings.isEmpty( response.getResponseName() ) ) 4179 { 4180 response.setResponseName( extendedRequest.getRequestName() ); 4181 } 4182 4183 // Decode the payload now 4184 return response; 4185 } 4186 catch ( Exception ie ) 4187 { 4188 if ( ie instanceof LdapException ) 4189 { 4190 throw ( LdapException ) ie; 4191 } 4192 4193 // Catch all other exceptions 4194 LOG.error( NO_RESPONSE_ERROR, ie ); 4195 4196 // Send an abandon request 4197 if ( !extendedFuture.isCancelled() ) 4198 { 4199 abandon( extendedRequest.getMessageId() ); 4200 } 4201 4202 throw new LdapException( NO_RESPONSE_ERROR, ie ); 4203 } 4204 } 4205 4206 4207 /** 4208 * {@inheritDoc} 4209 */ 4210 @Override 4211 public ExtendedFuture extendedAsync( ExtendedRequest extendedRequest ) throws LdapException 4212 { 4213 if ( extendedRequest == null ) 4214 { 4215 String msg = I18n.err( I18n.ERR_04154_CANNOT_PROCESS_NULL_EXT_REQ ); 4216 4217 if ( LOG.isDebugEnabled() ) 4218 { 4219 LOG.debug( msg ); 4220 } 4221 4222 throw new IllegalArgumentException( msg ); 4223 } 4224 4225 // try to connect, if we aren't already connected. 4226 connect(); 4227 4228 checkSession(); 4229 4230 int newId = messageId.incrementAndGet(); 4231 4232 extendedRequest.setMessageId( newId ); 4233 ExtendedFuture extendedFuture = new ExtendedFuture( this, newId ); 4234 extendedFuture.setExtendedRequest( extendedRequest ); 4235 addToFutureMap( newId, extendedFuture ); 4236 4237 // Send the request to the server 4238 writeRequest( extendedRequest ); 4239 4240 // Ok, done return the future 4241 return extendedFuture; 4242 } 4243 4244 4245 /** 4246 * {@inheritDoc} 4247 */ 4248 @Override 4249 public boolean exists( String dn ) throws LdapException 4250 { 4251 return exists( new Dn( dn ) ); 4252 } 4253 4254 4255 /** 4256 * {@inheritDoc} 4257 */ 4258 @Override 4259 public boolean exists( Dn dn ) throws LdapException 4260 { 4261 try 4262 { 4263 Entry entry = lookup( dn, SchemaConstants.NO_ATTRIBUTE_ARRAY ); 4264 4265 return entry != null; 4266 } 4267 catch ( LdapNoPermissionException lnpe ) 4268 { 4269 // Special case to deal with insufficient permissions 4270 return false; 4271 } 4272 catch ( LdapException le ) 4273 { 4274 throw le; 4275 } 4276 } 4277 4278 4279 /** 4280 * {@inheritDoc} 4281 */ 4282 @Override 4283 public Entry getRootDse() throws LdapException 4284 { 4285 return lookup( Dn.ROOT_DSE, SchemaConstants.ALL_USER_ATTRIBUTES_ARRAY ); 4286 } 4287 4288 4289 /** 4290 * {@inheritDoc} 4291 */ 4292 @Override 4293 public Entry getRootDse( String... attributes ) throws LdapException 4294 { 4295 return lookup( Dn.ROOT_DSE, attributes ); 4296 } 4297 4298 4299 /** 4300 * {@inheritDoc} 4301 */ 4302 @Override 4303 public Entry lookup( Dn dn ) throws LdapException 4304 { 4305 return lookup( dn, SchemaConstants.ALL_USER_ATTRIBUTES_ARRAY ); 4306 } 4307 4308 4309 /** 4310 * {@inheritDoc} 4311 */ 4312 @Override 4313 public Entry lookup( String dn ) throws LdapException 4314 { 4315 return lookup( dn, SchemaConstants.ALL_USER_ATTRIBUTES_ARRAY ); 4316 } 4317 4318 4319 /** 4320 * {@inheritDoc} 4321 */ 4322 @Override 4323 public Entry lookup( Dn dn, String... attributes ) throws LdapException 4324 { 4325 return lookup( dn, null, attributes ); 4326 } 4327 4328 4329 /** 4330 * {@inheritDoc} 4331 */ 4332 @Override 4333 public Entry lookup( Dn dn, Control[] controls, String... attributes ) throws LdapException 4334 { 4335 Entry entry = null; 4336 4337 try 4338 { 4339 SearchRequest searchRequest = new SearchRequestImpl(); 4340 4341 searchRequest.setBase( dn ); 4342 searchRequest.setFilter( LdapConstants.OBJECT_CLASS_STAR ); 4343 searchRequest.setScope( SearchScope.OBJECT ); 4344 searchRequest.addAttributes( attributes ); 4345 searchRequest.setDerefAliases( AliasDerefMode.DEREF_ALWAYS ); 4346 4347 if ( ( controls != null ) && ( controls.length > 0 ) ) 4348 { 4349 searchRequest.addAllControls( controls ); 4350 } 4351 4352 try ( Cursor<Response> cursor = search( searchRequest ) ) 4353 { 4354 // Read the response 4355 if ( cursor.next() ) 4356 { 4357 // cursor will always hold SearchResultEntry objects cause there is no ManageDsaITControl passed with search request 4358 entry = ( ( SearchResultEntry ) cursor.get() ).getEntry(); 4359 } 4360 4361 // Pass through the SaerchResultDone, or stop 4362 // if we have other responses 4363 cursor.next(); 4364 } 4365 } 4366 catch ( CursorException e ) 4367 { 4368 throw new LdapException( e.getMessage(), e ); 4369 } 4370 catch ( IOException ioe ) 4371 { 4372 throw new LdapException( ioe.getMessage(), ioe ); 4373 } 4374 4375 return entry; 4376 } 4377 4378 4379 /** 4380 * {@inheritDoc} 4381 */ 4382 @Override 4383 public Entry lookup( String dn, String... attributes ) throws LdapException 4384 { 4385 return lookup( new Dn( dn ), null, attributes ); 4386 } 4387 4388 4389 /** 4390 * {@inheritDoc} 4391 */ 4392 @Override 4393 public Entry lookup( String dn, Control[] controls, String... attributes ) throws LdapException 4394 { 4395 return lookup( new Dn( dn ), controls, attributes ); 4396 } 4397 4398 4399 /** 4400 * {@inheritDoc} 4401 */ 4402 @Override 4403 public boolean isControlSupported( String controlOID ) throws LdapException 4404 { 4405 return getSupportedControls().contains( controlOID ); 4406 } 4407 4408 4409 /** 4410 * {@inheritDoc} 4411 */ 4412 @Override 4413 public List<String> getSupportedControls() throws LdapException 4414 { 4415 if ( supportedControls != null ) 4416 { 4417 return supportedControls; 4418 } 4419 4420 if ( rootDse == null ) 4421 { 4422 fetchRootDSE(); 4423 } 4424 4425 supportedControls = new ArrayList<>(); 4426 4427 Attribute attr = rootDse.get( SchemaConstants.SUPPORTED_CONTROL_AT ); 4428 4429 if ( attr == null ) 4430 { 4431 // Unlikely. Perhaps the server does not respond properly to "+" attribute query 4432 // (such as 389ds server). So let's try again and let's be more explicit. 4433 fetchRootDSE( SchemaConstants.ALL_USER_ATTRIBUTES, 4434 SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.SUPPORTED_CONTROL_AT ); 4435 attr = rootDse.get( SchemaConstants.SUPPORTED_CONTROL_AT ); 4436 if ( attr == null ) 4437 { 4438 return supportedControls; 4439 } 4440 } 4441 4442 for ( Value value : attr ) 4443 { 4444 supportedControls.add( value.getString() ); 4445 } 4446 4447 return supportedControls; 4448 } 4449 4450 4451 /** 4452 * {@inheritDoc} 4453 */ 4454 @Override 4455 public void loadSchema() throws LdapException 4456 { 4457 loadSchema( new DefaultSchemaLoader( this ) ); 4458 } 4459 4460 4461 /** 4462 * {@inheritDoc} 4463 */ 4464 @Override 4465 public void loadSchemaRelaxed() throws LdapException 4466 { 4467 loadSchema( new DefaultSchemaLoader( this, true ) ); 4468 } 4469 4470 4471 /** 4472 * loads schema using the specified schema loader 4473 * 4474 * @param loader the {@link SchemaLoader} to be used to load schema 4475 * @throws LdapException If the schema loading failed 4476 */ 4477 public void loadSchema( SchemaLoader loader ) throws LdapException 4478 { 4479 try 4480 { 4481 SchemaManager tmp = new DefaultSchemaManager( loader ); 4482 4483 tmp.loadAllEnabled(); 4484 4485 if ( !tmp.getErrors().isEmpty() && loader.isStrict() ) 4486 { 4487 String msg = I18n.err( I18n.ERR_04115_ERROR_LOADING_SCHEMA ); 4488 4489 if ( LOG.isErrorEnabled() ) 4490 { 4491 LOG.error( I18n.err( I18n.ERR_05114_ERROR_MESSAGE, msg, Strings.listToString( tmp.getErrors() ) ) ); 4492 } 4493 4494 throw new LdapException( msg ); 4495 } 4496 4497 schemaManager = tmp; 4498 4499 // Change the container's BinaryDetector 4500 ldapSession.setAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR, 4501 new LdapMessageContainer<>( codec, 4502 new SchemaBinaryAttributeDetector( schemaManager ) ) ); 4503 4504 } 4505 catch ( LdapException le ) 4506 { 4507 throw le; 4508 } 4509 catch ( Exception e ) 4510 { 4511 LOG.error( I18n.err( I18n.ERR_04116_FAIL_LOAD_SCHEMA ), e ); 4512 throw new LdapException( e ); 4513 } 4514 } 4515 4516 4517 /** 4518 * parses the given schema file present in OpenLDAP schema format 4519 * and adds all the SchemaObjects present in it to the SchemaManager 4520 * 4521 * @param schemaFile the schema file in OpenLDAP schema format 4522 * @throws LdapException in case of any errors while parsing 4523 */ 4524 public void addSchema( File schemaFile ) throws LdapException 4525 { 4526 try 4527 { 4528 if ( schemaManager == null ) 4529 { 4530 loadSchema(); 4531 } 4532 4533 if ( schemaManager == null ) 4534 { 4535 throw new LdapException( I18n.err( I18n.ERR_04116_FAIL_LOAD_SCHEMA ) ); 4536 } 4537 4538 OpenLdapSchemaParser olsp = new OpenLdapSchemaParser(); 4539 olsp.setQuirksMode( true ); 4540 olsp.parse( schemaFile ); 4541 4542 Registries registries = schemaManager.getRegistries(); 4543 4544 for ( AttributeType atType : olsp.getAttributeTypes() ) 4545 { 4546 registries.buildReference( atType ); 4547 registries.getAttributeTypeRegistry().register( atType ); 4548 } 4549 4550 for ( ObjectClass oc : olsp.getObjectClasses() ) 4551 { 4552 registries.buildReference( oc ); 4553 registries.getObjectClassRegistry().register( oc ); 4554 } 4555 4556 if ( LOG.isInfoEnabled() ) 4557 { 4558 LOG.info( I18n.msg( I18n.MSG_04167_SCHEMA_LOADED_SUCCESSFULLY, schemaFile.getAbsolutePath() ) ); 4559 } 4560 } 4561 catch ( Exception e ) 4562 { 4563 LOG.error( I18n.err( I18n.ERR_04117_FAIL_LOAD_SCHEMA_FILE, schemaFile.getAbsolutePath() ) ); 4564 throw new LdapException( e ); 4565 } 4566 } 4567 4568 4569 /** 4570 * @see #addSchema(File) 4571 * @param schemaFileName The schema file name to add 4572 * @throws LdapException If the schema addition failed 4573 */ 4574 public void addSchema( String schemaFileName ) throws LdapException 4575 { 4576 addSchema( new File( schemaFileName ) ); 4577 } 4578 4579 4580 /** 4581 * {@inheritDoc} 4582 */ 4583 @Override 4584 public LdapApiService getCodecService() 4585 { 4586 return codec; 4587 } 4588 4589 4590 /** 4591 * {@inheritDoc} 4592 */ 4593 @Override 4594 public SchemaManager getSchemaManager() 4595 { 4596 return schemaManager; 4597 } 4598 4599 4600 /** 4601 * fetches the rootDSE from the server 4602 * 4603 * @param explicitAttributes The list of requested attributes 4604 * @throws LdapException If we weren't bale to fetch the RootDSE 4605 */ 4606 private void fetchRootDSE( String... explicitAttributes ) throws LdapException 4607 { 4608 EntryCursor cursor = null; 4609 4610 String[] attributes = explicitAttributes; 4611 if ( attributes.length == 0 ) 4612 { 4613 attributes = new String[] 4614 { SchemaConstants.ALL_USER_ATTRIBUTES, SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES }; 4615 } 4616 4617 try 4618 { 4619 cursor = search( "", LdapConstants.OBJECT_CLASS_STAR, SearchScope.OBJECT, attributes ); 4620 if ( cursor.next() ) 4621 { 4622 rootDse = cursor.get(); 4623 } 4624 else 4625 { 4626 throw new LdapException( I18n.err( I18n.ERR_04155_ROOT_DSE_SEARCH_FAILED ) ); 4627 } 4628 } 4629 catch ( Exception e ) 4630 { 4631 String msg = I18n.err( I18n.ERR_04156_FAILED_FETCHING_ROOT_DSE ); 4632 LOG.error( msg ); 4633 throw new LdapException( msg, e ); 4634 } 4635 finally 4636 { 4637 if ( cursor != null ) 4638 { 4639 try 4640 { 4641 cursor.close(); 4642 } 4643 catch ( Exception e ) 4644 { 4645 LOG.error( I18n.err( I18n.ERR_04114_CURSOR_CLOSE_FAIL ), e ); 4646 } 4647 } 4648 } 4649 } 4650 4651 4652 /** 4653 * gives the configuration information of the connection 4654 * 4655 * @return the configuration of the connection 4656 */ 4657 @Override 4658 public LdapConnectionConfig getConfig() 4659 { 4660 return config; 4661 } 4662 4663 4664 /** 4665 * removes the Objects associated with the given message ID 4666 * from future and response queue maps 4667 * 4668 * @param msgId id of the message 4669 */ 4670 private void removeFromFutureMaps( int msgId ) 4671 { 4672 getFromFutureMap( msgId ); 4673 } 4674 4675 4676 /** 4677 * clears the async listener, responseQueue and future mapppings to the corresponding request IDs 4678 */ 4679 private void clearMaps() 4680 { 4681 futureMap.clear(); 4682 } 4683 4684 4685 /** 4686 * {@inheritDoc} 4687 */ 4688 @Override 4689 public boolean isRequestCompleted( int messageId ) 4690 { 4691 ResponseFuture<?> responseFuture = futureMap.get( messageId ); 4692 4693 return responseFuture == null; 4694 } 4695 4696 4697 /** 4698 * {@inheritDoc} 4699 */ 4700 @Override 4701 public boolean doesFutureExistFor( int messageId ) 4702 { 4703 ResponseFuture<?> responseFuture = futureMap.get( messageId ); 4704 return responseFuture != null; 4705 } 4706 4707 4708 /** 4709 * Adds the connection closed event listener. 4710 * 4711 * @param ccListener the connection closed listener 4712 */ 4713 public void addConnectionClosedEventListener( ConnectionClosedEventListener ccListener ) 4714 { 4715 if ( conCloseListeners == null ) 4716 { 4717 conCloseListeners = new ArrayList<>(); 4718 } 4719 4720 conCloseListeners.add( ccListener ); 4721 } 4722 4723 4724 /** 4725 * {@inheritDoc} 4726 */ 4727 @Override 4728 public void inputClosed( IoSession session ) throws Exception 4729 { 4730 session.closeNow(); 4731 } 4732 4733 4734 /** 4735 * This method is called when a new session is created. We will store some 4736 * informations that the session will need to process incoming requests. 4737 * 4738 * @param session the newly created session 4739 */ 4740 @Override 4741 public void sessionCreated( IoSession session ) throws Exception 4742 { 4743 // Last, store the message container 4744 LdapMessageContainer<Message> ldapMessageContainer = 4745 new LdapMessageContainer<>( 4746 codec, config.getBinaryAttributeDetector() ); 4747 4748 session.setAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR, ldapMessageContainer ); 4749 connected.set( true ); 4750 } 4751 4752 4753 /** 4754 * {@inheritDoc} 4755 */ 4756 @Override 4757 public void sessionClosed( IoSession session ) throws Exception 4758 { 4759 // no need to handle if this session was closed by the user 4760 if ( ldapSession == null || !connected.get() ) 4761 { 4762 return; 4763 } 4764 4765 ldapSession.closeNow(); 4766 connected.set( false ); 4767 // Reset the messageId 4768 messageId.set( 0 ); 4769 4770 connectorMutex.lock(); 4771 4772 try 4773 { 4774 if ( connector != null ) 4775 { 4776 connector.dispose(); 4777 connector = null; 4778 } 4779 } 4780 finally 4781 { 4782 connectorMutex.unlock(); 4783 } 4784 4785 clearMaps(); 4786 4787 if ( conCloseListeners != null ) 4788 { 4789 if ( LOG.isDebugEnabled() ) 4790 { 4791 LOG.debug( I18n.msg( I18n.MSG_04136_NOTIFYING_CLOSE_LISTENERS ) ); 4792 } 4793 4794 for ( ConnectionClosedEventListener listener : conCloseListeners ) 4795 { 4796 listener.connectionClosed(); 4797 } 4798 } 4799 } 4800 4801 4802 /** 4803 * Sends the StartTLS extended request to server and adds a security layer 4804 * upon receiving a response with successful result. Note that we will use 4805 * the default LDAP connection. 4806 * 4807 * @throws LdapException If the StartTLS operation failed 4808 */ 4809 public void startTls() throws LdapException 4810 { 4811 try 4812 { 4813 if ( config.isUseSsl() ) 4814 { 4815 throw new LdapException( I18n.err( I18n.ERR_04157_CANNOT_USE_TLS_WITH_SSL_FLAG ) ); 4816 } 4817 4818 // try to connect, if we aren't already connected. 4819 connect(); 4820 4821 checkSession(); 4822 4823 if ( ldapSession.isSecured() ) 4824 { 4825 if ( LOG.isDebugEnabled() ) 4826 { 4827 LOG.debug( I18n.msg( I18n.MSG_04121_LDAP_ALREADY_USING_START_TLS ) ); 4828 } 4829 4830 return; 4831 } 4832 4833 ExtendedResponse resp = extended( new StartTlsRequestImpl() ); 4834 LdapResult result = resp.getLdapResult(); 4835 4836 if ( result.getResultCode() == ResultCodeEnum.SUCCESS ) 4837 { 4838 addSslFilter(); 4839 } 4840 else 4841 { 4842 throw new LdapOperationException( result.getResultCode(), result.getDiagnosticMessage() ); 4843 } 4844 } 4845 catch ( LdapException e ) 4846 { 4847 throw e; 4848 } 4849 catch ( Exception e ) 4850 { 4851 throw new LdapException( e ); 4852 } 4853 } 4854 4855 4856 /** 4857 * Adds {@link SslFilter} to the IOConnector or IOSession's filter chain 4858 * 4859 * @throws LdapException If the SSL filter addition failed 4860 */ 4861 private void addSslFilter() throws LdapException 4862 { 4863 try 4864 { 4865 SSLContext sslContext = SSLContext.getInstance( config.getSslProtocol() ); 4866 4867 TrustManager[] trustManagers = config.getTrustManagers(); 4868 4869 if ( ( trustManagers == null ) || ( trustManagers.length == 0 ) ) 4870 { 4871 trustManagers = new TrustManager[] { new NoVerificationTrustManager() }; 4872 } 4873 4874 sslContext.init( config.getKeyManagers(), trustManagers, config.getSecureRandom() ); 4875 4876 SslFilter sslFilter = new SslFilter( sslContext ); 4877 sslFilter.setUseClientMode( true ); 4878 4879 // Configure the enabled cipher lists 4880 String[] enabledCipherSuite = config.getEnabledCipherSuites(); 4881 4882 if ( ( enabledCipherSuite != null ) && ( enabledCipherSuite.length != 0 ) ) 4883 { 4884 sslFilter.setEnabledCipherSuites( enabledCipherSuite ); 4885 } 4886 4887 // Be sure we disable SSLV3 4888 String[] enabledProtocols = config.getEnabledProtocols(); 4889 4890 if ( ( enabledProtocols != null ) && ( enabledProtocols.length != 0 ) ) 4891 { 4892 sslFilter.setEnabledProtocols( enabledProtocols ); 4893 } 4894 else 4895 { 4896 // Default to TLS 4897 sslFilter.setEnabledProtocols( new String[] 4898 { "TLSv1", "TLSv1.1", "TLSv1.2" } ); 4899 } 4900 4901 // for LDAPS/TLS 4902 handshakeFuture = new HandshakeFuture(); 4903 4904 if ( ( ldapSession == null ) || !connected.get() ) 4905 { 4906 connector.getFilterChain().addFirst( SSL_FILTER_KEY, sslFilter ); 4907 } 4908 else 4909 // for StartTLS 4910 { 4911 ldapSession.getFilterChain().addFirst( SSL_FILTER_KEY, sslFilter ); 4912 4913 boolean isSecured = handshakeFuture.get( timeout, TimeUnit.MILLISECONDS ); 4914 4915 if ( !isSecured ) 4916 { 4917 Throwable cause = ( Throwable ) ldapSession.getAttribute( EXCEPTION_KEY ); 4918 throw new LdapTlsHandshakeException( I18n.err( I18n.ERR_04120_TLS_HANDSHAKE_ERROR ), cause ); 4919 } 4920 } 4921 } 4922 catch ( Exception e ) 4923 { 4924 if ( e instanceof LdapException ) 4925 { 4926 throw ( LdapException ) e; 4927 } 4928 4929 String msg = I18n.err( I18n.ERR_04122_SSL_CONTEXT_INIT_FAILURE ); 4930 LOG.error( msg, e ); 4931 throw new LdapException( msg, e ); 4932 } 4933 } 4934 4935 4936 /** 4937 * Process the SASL Bind. It's a dialog with the server, we will send a first BindRequest, receive 4938 * a response and the, if this response is a challenge, continue by sending a new BindRequest with 4939 * the requested informations. 4940 * 4941 * @param saslRequest The SASL request object containing all the needed parameters 4942 * @return A {@link BindResponse} containing the result 4943 * @throws LdapException if some error occurred 4944 */ 4945 public BindFuture bindSasl( SaslRequest saslRequest ) throws LdapException 4946 { 4947 // First switch to anonymous state 4948 authenticated.set( false ); 4949 4950 // try to connect, if we aren't already connected. 4951 connect(); 4952 4953 // If the session has not been establish, or is closed, we get out immediately 4954 checkSession(); 4955 4956 BindRequest bindRequest = createBindRequest( ( String ) null, null, 4957 saslRequest.getSaslMechanism(), saslRequest.getControls() ); 4958 4959 // Update the messageId 4960 int newId = messageId.incrementAndGet(); 4961 bindRequest.setMessageId( newId ); 4962 4963 if ( LOG.isDebugEnabled() ) 4964 { 4965 LOG.debug( I18n.msg( I18n.MSG_04104_SENDING_REQUEST, bindRequest ) ); 4966 } 4967 4968 // Create a future for this Bind operation 4969 BindFuture bindFuture = new BindFuture( this, newId ); 4970 4971 // Store it in the future Map 4972 addToFutureMap( newId, bindFuture ); 4973 4974 try 4975 { 4976 BindResponse bindResponse; 4977 byte[] response; 4978 ResultCodeEnum result; 4979 4980 // Creating a map for SASL properties 4981 Map<String, Object> properties = new HashMap<>(); 4982 4983 // Quality of Protection SASL property 4984 if ( saslRequest.getQualityOfProtection() != null ) 4985 { 4986 properties.put( Sasl.QOP, saslRequest.getQualityOfProtection().getValue() ); 4987 } 4988 4989 // Security Strength SASL property 4990 if ( saslRequest.getSecurityStrength() != null ) 4991 { 4992 properties.put( Sasl.STRENGTH, saslRequest.getSecurityStrength().getValue() ); 4993 } 4994 4995 // Mutual Authentication SASL property 4996 if ( saslRequest.isMutualAuthentication() ) 4997 { 4998 properties.put( Sasl.SERVER_AUTH, "true" ); 4999 } 5000 5001 // Creating a SASL Client 5002 SaslClient sc = Sasl.createSaslClient( 5003 new String[] 5004 { bindRequest.getSaslMechanism() }, 5005 saslRequest.getAuthorizationId(), 5006 "ldap", 5007 config.getLdapHost(), 5008 properties, 5009 new SaslCallbackHandler( saslRequest ) ); 5010 5011 // If the SaslClient wasn't created, that means we can't create the SASL client 5012 // for the requested mechanism. We then produce an Exception 5013 if ( sc == null ) 5014 { 5015 String message = I18n.err( I18n.ERR_04158_CANNOT_FIND_SASL_FACTORY_FOR_MECH, bindRequest.getSaslMechanism() ); 5016 LOG.error( message ); 5017 throw new LdapException( message ); 5018 } 5019 5020 // Corner case : the SASL mech might send an initial challenge, and we have to 5021 // deal with it immediately. 5022 if ( sc.hasInitialResponse() ) 5023 { 5024 byte[] challengeResponse = sc.evaluateChallenge( Strings.EMPTY_BYTES ); 5025 5026 // Stores the challenge's response, and send it to the server 5027 bindRequest.setCredentials( challengeResponse ); 5028 writeRequest( bindRequest ); 5029 5030 // Get the server's response, blocking 5031 bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS ); 5032 5033 if ( bindResponse == null ) 5034 { 5035 // We didn't received anything : this is an error 5036 if ( LOG.isErrorEnabled() ) 5037 { 5038 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Bind" ) ); 5039 } 5040 5041 throw new LdapException( TIME_OUT_ERROR ); 5042 } 5043 5044 result = bindResponse.getLdapResult().getResultCode(); 5045 } 5046 else 5047 { 5048 // Copy the bindRequest without setting the credentials 5049 BindRequest bindRequestCopy = new BindRequestImpl(); 5050 bindRequestCopy.setMessageId( newId ); 5051 5052 bindRequestCopy.setName( bindRequest.getName() ); 5053 bindRequestCopy.setSaslMechanism( bindRequest.getSaslMechanism() ); 5054 bindRequestCopy.setSimple( bindRequest.isSimple() ); 5055 bindRequestCopy.setVersion3( bindRequest.getVersion3() ); 5056 bindRequestCopy.addAllControls( bindRequest.getControls().values().toArray( new Control[0] ) ); 5057 5058 writeRequest( bindRequestCopy ); 5059 5060 bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS ); 5061 5062 if ( bindResponse == null ) 5063 { 5064 // We didn't received anything : this is an error 5065 if ( LOG.isErrorEnabled() ) 5066 { 5067 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Bind" ) ); 5068 } 5069 5070 throw new LdapException( TIME_OUT_ERROR ); 5071 } 5072 5073 result = bindResponse.getLdapResult().getResultCode(); 5074 } 5075 5076 while ( !sc.isComplete() 5077 && ( ( result == ResultCodeEnum.SASL_BIND_IN_PROGRESS ) || ( result == ResultCodeEnum.SUCCESS ) ) ) 5078 { 5079 response = sc.evaluateChallenge( bindResponse.getServerSaslCreds() ); 5080 5081 if ( result == ResultCodeEnum.SUCCESS ) 5082 { 5083 if ( response != null ) 5084 { 5085 throw new LdapException( I18n.err( I18n.ERR_04159_PROTOCOL_ERROR ) ); 5086 } 5087 } 5088 else 5089 { 5090 newId = messageId.incrementAndGet(); 5091 bindRequest.setMessageId( newId ); 5092 bindRequest.setCredentials( response ); 5093 5094 addToFutureMap( newId, bindFuture ); 5095 5096 writeRequest( bindRequest ); 5097 5098 bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS ); 5099 5100 if ( bindResponse == null ) 5101 { 5102 // We didn't received anything : this is an error 5103 if ( LOG.isErrorEnabled() ) 5104 { 5105 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Bind" ) ); 5106 } 5107 5108 throw new LdapException( TIME_OUT_ERROR ); 5109 } 5110 5111 result = bindResponse.getLdapResult().getResultCode(); 5112 } 5113 } 5114 5115 bindFuture.set( bindResponse ); 5116 5117 return bindFuture; 5118 } 5119 catch ( LdapException e ) 5120 { 5121 throw e; 5122 } 5123 catch ( Exception e ) 5124 { 5125 LOG.error( e.getMessage() ); 5126 throw new LdapException( e ); 5127 } 5128 } 5129 5130 5131 /** 5132 * A reusable code block to be used in various bind methods 5133 * 5134 * @param request The request to send 5135 * @throws LdapException If the request was ot properly sent 5136 */ 5137 private void writeRequest( Request request ) throws LdapException 5138 { 5139 // Send the request to the server 5140 WriteFuture writeFuture = ldapSession.write( request ); 5141 5142 long localTimeout = timeout; 5143 5144 while ( localTimeout > 0 ) 5145 { 5146 // Wait only 100 ms 5147 boolean done = writeFuture.awaitUninterruptibly( 100 ); 5148 5149 if ( done ) 5150 { 5151 return; 5152 } 5153 5154 // Wait for the message to be sent to the server 5155 if ( !ldapSession.isConnected() ) 5156 { 5157 // We didn't received anything : this is an error 5158 if ( LOG.isErrorEnabled() ) 5159 { 5160 LOG.error( I18n.err( I18n.ERR_04118_SOMETHING_WRONG_HAPPENED ) ); 5161 } 5162 5163 Exception exception = ( Exception ) ldapSession.removeAttribute( EXCEPTION_KEY ); 5164 5165 if ( exception instanceof LdapException ) 5166 { 5167 throw ( LdapException ) exception; 5168 } 5169 else if ( exception != null ) 5170 { 5171 throw new InvalidConnectionException( exception.getMessage(), exception ); 5172 } 5173 5174 throw new InvalidConnectionException( I18n.err( I18n.ERR_04160_SESSION_HAS_BEEN_CLOSED ) ); 5175 } 5176 5177 localTimeout -= 100; 5178 } 5179 5180 if ( LOG.isErrorEnabled() ) 5181 { 5182 LOG.error( I18n.err( I18n.ERR_04119_TIMEOUT ) ); 5183 } 5184 5185 throw new LdapException( TIME_OUT_ERROR ); 5186 } 5187 5188 5189 /** 5190 * method to write the kerberos config in the standard MIT kerberos format 5191 * 5192 * This is required cause the JGSS api is not able to recognize the port value set 5193 * in the system property java.security.krb5.kdc this issue makes it impossible 5194 * to set a kdc running non standard ports (other than 88) 5195 * 5196 * e.g localhost:6088 5197 * 5198 * <pre> 5199 * [libdefaults] 5200 * default_realm = EXAMPLE.COM 5201 * 5202 * [realms] 5203 * EXAMPLE.COM = { 5204 * kdc = localhost:6088 5205 * } 5206 * </pre> 5207 * 5208 * @param realmName The realm name 5209 * @param kdcHost The Kerberos server host 5210 * @param kdcPort The Kerberos server port 5211 * @return the full path of the config file 5212 * @throws IOException If the config file cannot be created 5213 */ 5214 private String createKrb5ConfFile( String realmName, String kdcHost, int kdcPort ) throws IOException 5215 { 5216 StringBuilder sb = new StringBuilder(); 5217 5218 sb.append( "[libdefaults]" ) 5219 .append( "\n\t" ); 5220 sb.append( "default_realm = " ) 5221 .append( realmName ) 5222 .append( "\n" ); 5223 5224 sb.append( "[realms]" ) 5225 .append( "\n\t" ); 5226 5227 sb.append( realmName ) 5228 .append( " = {" ) 5229 .append( "\n\t\t" ); 5230 sb.append( "kdc = " ) 5231 .append( kdcHost ) 5232 .append( ":" ) 5233 .append( kdcPort ) 5234 .append( "\n\t}\n" ); 5235 5236 File krb5Conf = File.createTempFile( "client-api-krb5", ".conf" ); 5237 krb5Conf.deleteOnExit(); 5238 5239 try ( Writer writer = new OutputStreamWriter( Files.newOutputStream( Paths.get( krb5Conf.getPath() ) ), 5240 Charset.defaultCharset() ) ) 5241 { 5242 writer.write( sb.toString() ); 5243 } 5244 5245 String krb5ConfPath = krb5Conf.getAbsolutePath(); 5246 5247 if ( LOG.isDebugEnabled() ) 5248 { 5249 LOG.debug( I18n.msg( I18n.MSG_04135_KRB5_FILE_CREATED, krb5ConfPath ) ); 5250 } 5251 5252 return krb5ConfPath; 5253 } 5254 5255 5256 /** 5257 * {@inheritDoc} 5258 */ 5259 @Override 5260 public BinaryAttributeDetector getBinaryAttributeDetector() 5261 { 5262 if ( config != null ) 5263 { 5264 return config.getBinaryAttributeDetector(); 5265 } 5266 else 5267 { 5268 return null; 5269 } 5270 } 5271 5272 5273 /** 5274 * {@inheritDoc} 5275 */ 5276 @Override 5277 public void setBinaryAttributeDetector( BinaryAttributeDetector binaryAttributeDetector ) 5278 { 5279 if ( config != null ) 5280 { 5281 config.setBinaryAttributeDetector( binaryAttributeDetector ); 5282 } 5283 } 5284 5285 5286 /** 5287 * {@inheritDoc} 5288 */ 5289 @Override 5290 public void setSchemaManager( SchemaManager schemaManager ) 5291 { 5292 this.schemaManager = schemaManager; 5293 } 5294 5295 5296 /** 5297 * @return the socketSessionConfig 5298 */ 5299 public SocketSessionConfig getSocketSessionConfig() 5300 { 5301 return socketSessionConfig; 5302 } 5303 5304 5305 /** 5306 * @param socketSessionConfig the socketSessionConfig to set 5307 */ 5308 public void setSocketSessionConfig( SocketSessionConfig socketSessionConfig ) 5309 { 5310 this.socketSessionConfig = socketSessionConfig; 5311 } 5312 5313 5314 /** 5315 * {@inheritDoc} 5316 */ 5317 @Override 5318 public void event( IoSession session, FilterEvent event ) throws Exception 5319 { 5320 // Check if it's a SSLevent 5321 if ( ( event instanceof SslEvent ) && ( ( SslEvent ) event == SslEvent.SECURED ) ) 5322 { 5323 handshakeFuture.secured(); 5324 } 5325 } 5326}