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