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 * https://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.template; 021 022 023import java.util.ArrayList; 024import java.util.List; 025 026import org.apache.directory.api.i18n.I18n; 027import org.apache.directory.api.ldap.extras.controls.ppolicy.PasswordPolicyResponse; 028import org.apache.directory.api.ldap.extras.controls.ppolicy.PasswordPolicyResponseImpl; 029import org.apache.directory.api.ldap.model.entry.Attribute; 030import org.apache.directory.api.ldap.model.entry.Entry; 031import org.apache.directory.api.ldap.model.entry.Value; 032import org.apache.directory.api.ldap.model.exception.LdapException; 033import org.apache.directory.api.ldap.model.message.AddRequest; 034import org.apache.directory.api.ldap.model.message.AddResponse; 035import org.apache.directory.api.ldap.model.message.BindRequest; 036import org.apache.directory.api.ldap.model.message.BindRequestImpl; 037import org.apache.directory.api.ldap.model.message.DeleteRequest; 038import org.apache.directory.api.ldap.model.message.DeleteResponse; 039import org.apache.directory.api.ldap.model.message.ModifyRequest; 040import org.apache.directory.api.ldap.model.message.ModifyRequestImpl; 041import org.apache.directory.api.ldap.model.message.ModifyResponse; 042import org.apache.directory.api.ldap.model.message.ResultCodeEnum; 043import org.apache.directory.api.ldap.model.message.ResultResponse; 044import org.apache.directory.api.ldap.model.message.SearchRequest; 045import org.apache.directory.api.ldap.model.message.SearchScope; 046import org.apache.directory.api.ldap.model.name.Dn; 047import org.apache.directory.ldap.client.api.EntryCursorImpl; 048import org.apache.directory.ldap.client.api.LdapConnection; 049import org.apache.directory.ldap.client.api.LdapConnectionPool; 050import org.apache.directory.ldap.client.api.search.FilterBuilder; 051import org.apache.directory.ldap.client.template.exception.LdapRequestUnsuccessfulException; 052import org.apache.directory.ldap.client.template.exception.LdapRuntimeException; 053import org.apache.directory.ldap.client.template.exception.PasswordException; 054import org.slf4j.Logger; 055import org.slf4j.LoggerFactory; 056 057 058/** 059 * A facade for LDAP operations that handles all of the boiler plate code for 060 * you allowing more concise operations through the use of callbacks. 061 * 062 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 063 * 064 * @see <a href="http://en.wikipedia.org/wiki/Template_method_pattern">Template method pattern</a> 065 */ 066public class LdapConnectionTemplate implements LdapConnectionOperations, ModelFactory 067{ 068 private static final Logger LOG = LoggerFactory.getLogger( LdapConnectionTemplate.class ); 069 private static final EntryMapper<Dn> DN_ENTRY_MAPPER = new EntryMapper<Dn>() 070 { 071 @Override 072 public Dn map( Entry entry ) throws LdapException 073 { 074 return entry.getDn(); 075 } 076 }; 077 078 private LdapConnectionPool connectionPool; 079 private final PasswordPolicyResponse passwordPolicyRequestControl; 080 private PasswordPolicyResponder passwordPolicyResponder; 081 private ModelFactory modelFactory; 082 083 084 /** 085 * Creates a new instance of LdapConnectionTemplate. 086 * 087 * @param connectionPool The pool to obtain connections from. 088 */ 089 public LdapConnectionTemplate( LdapConnectionPool connectionPool ) 090 { 091 if ( LOG.isDebugEnabled() ) 092 { 093 LOG.debug( I18n.msg( I18n.MSG_04174_CREATING_NEW_CONNECTION_TEMPLATE ) ); 094 } 095 096 this.connectionPool = connectionPool; 097 this.passwordPolicyRequestControl = new PasswordPolicyResponseImpl(); 098 this.passwordPolicyResponder = new PasswordPolicyResponderImpl( 099 connectionPool.getLdapApiService() ); 100 this.modelFactory = new ModelFactoryImpl(); 101 } 102 103 104 /** 105 * {@inheritDoc} 106 */ 107 @Override 108 public AddResponse add( Dn dn, final Attribute... attributes ) 109 { 110 return add( dn, 111 new RequestBuilder<AddRequest>() 112 { 113 @Override 114 public void buildRequest( AddRequest request ) throws LdapException 115 { 116 request.getEntry().add( attributes ); 117 } 118 } ); 119 } 120 121 122 /** 123 * {@inheritDoc} 124 */ 125 @Override 126 public AddResponse add( Dn dn, RequestBuilder<AddRequest> requestBuilder ) 127 { 128 AddRequest addRequest = newAddRequest( newEntry( dn ) ); 129 try 130 { 131 requestBuilder.buildRequest( addRequest ); 132 } 133 catch ( LdapException e ) 134 { 135 throw new LdapRuntimeException( e ); 136 } 137 return add( addRequest ); 138 } 139 140 141 /** 142 * {@inheritDoc} 143 */ 144 @Override 145 public AddResponse add( AddRequest addRequest ) 146 { 147 LdapConnection connection = null; 148 try 149 { 150 connection = connectionPool.getConnection(); 151 return connection.add( addRequest ); 152 } 153 catch ( LdapException e ) 154 { 155 throw new LdapRuntimeException( e ); 156 } 157 finally 158 { 159 returnLdapConnection( connection ); 160 } 161 } 162 163 164 /** 165 * {@inheritDoc} 166 */ 167 @Override 168 public PasswordWarning authenticate( String baseDn, String filter, SearchScope scope, char[] password ) 169 throws PasswordException 170 { 171 return authenticate( newSearchRequest( baseDn, filter, scope ), password ); 172 } 173 174 175 /** 176 * {@inheritDoc} 177 */ 178 @Override 179 public PasswordWarning authenticate( Dn baseDn, String filter, SearchScope scope, char[] password ) 180 throws PasswordException 181 { 182 return authenticate( newSearchRequest( baseDn, filter, scope ), password ); 183 } 184 185 186 /** 187 * {@inheritDoc} 188 */ 189 @Override 190 public PasswordWarning authenticate( SearchRequest searchRequest, char[] password ) throws PasswordException 191 { 192 Dn userDn = searchFirst( searchRequest, DN_ENTRY_MAPPER ); 193 if ( userDn == null ) 194 { 195 throw new PasswordException().setResultCode( ResultCodeEnum.INVALID_CREDENTIALS ); 196 } 197 198 return authenticate( userDn, password ); 199 } 200 201 202 /** 203 * {@inheritDoc} 204 */ 205 @Override 206 public PasswordWarning authenticate( Dn userDn, char[] password ) throws PasswordException 207 { 208 LdapConnection connection = null; 209 try 210 { 211 connection = connectionPool.getConnection(); 212 return authenticateConnection( connection, userDn, password ); 213 } 214 catch ( LdapException e ) 215 { 216 throw new LdapRuntimeException( e ); 217 } 218 finally 219 { 220 returnLdapConnection( connection ); 221 } 222 } 223 224 225 private PasswordWarning authenticateConnection( final LdapConnection connection, 226 final Dn userDn, final char[] password ) throws PasswordException 227 { 228 return passwordPolicyResponder.process( 229 new PasswordPolicyOperation() 230 { 231 @Override 232 public ResultResponse process() throws LdapException 233 { 234 MemoryClearingBuffer passwordBuffer = MemoryClearingBuffer.newInstance( password ); 235 try 236 { 237 BindRequest bindRequest = new BindRequestImpl() 238 .setDn( userDn ) 239 .setCredentials( passwordBuffer.getBytes() ) 240 .addControl( passwordPolicyRequestControl ); 241 242 return connection.bind( bindRequest ); 243 } 244 finally 245 { 246 passwordBuffer.clear(); 247 } 248 } 249 } ); 250 } 251 252 253 /** 254 * {@inheritDoc} 255 */ 256 @Override 257 public DeleteResponse delete( Dn dn ) 258 { 259 return delete( dn, null ); 260 } 261 262 263 /** 264 * {@inheritDoc} 265 */ 266 @Override 267 public DeleteResponse delete( Dn dn, RequestBuilder<DeleteRequest> requestBuilder ) 268 { 269 DeleteRequest deleteRequest = newDeleteRequest( dn ); 270 if ( requestBuilder != null ) 271 { 272 try 273 { 274 requestBuilder.buildRequest( deleteRequest ); 275 } 276 catch ( LdapException e ) 277 { 278 throw new LdapRuntimeException( e ); 279 } 280 } 281 return delete( deleteRequest ); 282 } 283 284 285 /** 286 * {@inheritDoc} 287 */ 288 @Override 289 public DeleteResponse delete( DeleteRequest deleteRequest ) 290 { 291 LdapConnection connection = null; 292 try 293 { 294 connection = connectionPool.getConnection(); 295 return connection.delete( deleteRequest ); 296 } 297 catch ( LdapException e ) 298 { 299 throw new LdapRuntimeException( e ); 300 } 301 finally 302 { 303 returnLdapConnection( connection ); 304 } 305 } 306 307 308 /** 309 * {@inheritDoc} 310 */ 311 @Override 312 public <T> T execute( ConnectionCallback<T> connectionCallback ) 313 { 314 LdapConnection connection = null; 315 try 316 { 317 connection = connectionPool.getConnection(); 318 return connectionCallback.doWithConnection( connection ); 319 } 320 catch ( LdapException e ) 321 { 322 throw new LdapRuntimeException( e ); 323 } 324 finally 325 { 326 returnLdapConnection( connection ); 327 } 328 } 329 330 331 /** 332 * {@inheritDoc} 333 */ 334 @Override 335 public <T> T lookup( Dn dn, EntryMapper<T> entryMapper ) 336 { 337 return lookup( dn, null, entryMapper ); 338 } 339 340 341 /** 342 * {@inheritDoc} 343 */ 344 @Override 345 public <T> T lookup( Dn dn, String[] attributes, EntryMapper<T> entryMapper ) 346 { 347 LdapConnection connection = null; 348 try 349 { 350 connection = connectionPool.getConnection(); 351 Entry entry = attributes == null 352 ? connection.lookup( dn ) 353 : connection.lookup( dn, attributes ); 354 return entry == null ? null : entryMapper.map( entry ); 355 } 356 catch ( LdapException e ) 357 { 358 throw new LdapRuntimeException( e ); 359 } 360 finally 361 { 362 returnLdapConnection( connection ); 363 } 364 } 365 366 367 private void modifyPassword( final LdapConnection connection, final Dn userDn, 368 final char[] newPassword ) throws PasswordException 369 { 370 passwordPolicyResponder.process( 371 new PasswordPolicyOperation() 372 { 373 @Override 374 public ResultResponse process() throws PasswordException, LdapException 375 { 376 // Can't use Password Modify: 377 // https://issues.apache.org/jira/browse/DIRSERVER-1935 378 // So revert to regular Modify 379 MemoryClearingBuffer newPasswordBuffer = MemoryClearingBuffer.newInstance( newPassword ); 380 try 381 { 382 ModifyRequest modifyRequest = new ModifyRequestImpl() 383 .setName( userDn ) 384 .replace( "userPassword", newPasswordBuffer.getComputedBytes() ) 385 .addControl( passwordPolicyRequestControl ); 386 387 return connection.modify( modifyRequest ); 388 } 389 finally 390 { 391 newPasswordBuffer.clear(); 392 } 393 } 394 } ); 395 396 } 397 398 399 /** 400 * {@inheritDoc} 401 */ 402 @Override 403 public void modifyPassword( Dn userDn, char[] newPassword ) 404 throws PasswordException 405 { 406 modifyPassword( userDn, null, newPassword, true ); 407 } 408 409 410 /** 411 * {@inheritDoc} 412 */ 413 @Override 414 public void modifyPassword( Dn userDn, char[] oldPassword, 415 char[] newPassword ) throws PasswordException 416 { 417 modifyPassword( userDn, oldPassword, newPassword, false ); 418 } 419 420 421 /** 422 * {@inheritDoc} 423 */ 424 @Override 425 public void modifyPassword( Dn userDn, char[] oldPassword, 426 char[] newPassword, boolean asAdmin ) throws PasswordException 427 { 428 LdapConnection connection = null; 429 try 430 { 431 connection = connectionPool.getConnection(); 432 if ( !asAdmin ) 433 { 434 authenticateConnection( connection, userDn, oldPassword ); 435 } 436 437 modifyPassword( connection, userDn, newPassword ); 438 } 439 catch ( LdapException e ) 440 { 441 throw new LdapRuntimeException( e ); 442 } 443 finally 444 { 445 returnLdapConnection( connection ); 446 } 447 } 448 449 450 /** 451 * {@inheritDoc} 452 */ 453 @Override 454 public ModifyResponse modify( Dn dn, RequestBuilder<ModifyRequest> requestBuilder ) 455 { 456 ModifyRequest modifyRequest = newModifyRequest( dn ); 457 try 458 { 459 requestBuilder.buildRequest( modifyRequest ); 460 } 461 catch ( LdapException e ) 462 { 463 throw new LdapRuntimeException( e ); 464 } 465 return modify( modifyRequest ); 466 } 467 468 469 /** 470 * {@inheritDoc} 471 */ 472 @Override 473 public ModifyResponse modify( ModifyRequest modifyRequest ) 474 { 475 LdapConnection connection = null; 476 try 477 { 478 connection = connectionPool.getConnection(); 479 return connection.modify( modifyRequest ); 480 } 481 catch ( LdapException e ) 482 { 483 throw new LdapRuntimeException( e ); 484 } 485 finally 486 { 487 returnLdapConnection( connection ); 488 } 489 } 490 491 492 /** 493 * {@inheritDoc} 494 */ 495 @Override 496 public AddRequest newAddRequest( Entry entry ) 497 { 498 return modelFactory.newAddRequest( entry ); 499 } 500 501 502 /** 503 * {@inheritDoc} 504 */ 505 @Override 506 public Attribute newAttribute( String name ) 507 { 508 return modelFactory.newAttribute( name ); 509 } 510 511 512 /** 513 * {@inheritDoc} 514 */ 515 public Attribute newAttribute( String name, byte[]... values ) 516 { 517 return modelFactory.newAttribute( name, values ); 518 } 519 520 521 /** 522 * {@inheritDoc} 523 */ 524 @Override 525 public Attribute newAttribute( String name, String... values ) 526 { 527 return modelFactory.newAttribute( name, values ); 528 } 529 530 531 /** 532 * {@inheritDoc} 533 */ 534 @Override 535 public Attribute newAttribute( String name, Value... values ) 536 { 537 return modelFactory.newAttribute( name, values ); 538 } 539 540 541 /** 542 * {@inheritDoc} 543 */ 544 @Override 545 public DeleteRequest newDeleteRequest( Dn dn ) 546 { 547 return modelFactory.newDeleteRequest( dn ); 548 } 549 550 551 /** 552 * {@inheritDoc} 553 */ 554 @Override 555 public Dn newDn( String dn ) 556 { 557 return modelFactory.newDn( dn ); 558 } 559 560 561 /** 562 * {@inheritDoc} 563 */ 564 @Override 565 public Entry newEntry( String dn ) 566 { 567 return modelFactory.newEntry( dn ); 568 } 569 570 571 /** 572 * {@inheritDoc} 573 */ 574 @Override 575 public Entry newEntry( Dn dn ) 576 { 577 return modelFactory.newEntry( dn ); 578 } 579 580 581 /** 582 * {@inheritDoc} 583 */ 584 @Override 585 public ModifyRequest newModifyRequest( String dn ) 586 { 587 return modelFactory.newModifyRequest( dn ); 588 } 589 590 591 /** 592 * {@inheritDoc} 593 */ 594 @Override 595 public ModifyRequest newModifyRequest( Dn dn ) 596 { 597 return modelFactory.newModifyRequest( dn ); 598 } 599 600 601 /** 602 * {@inheritDoc} 603 */ 604 @Override 605 public SearchRequest newSearchRequest( String baseDn, FilterBuilder filter, SearchScope scope ) 606 { 607 return modelFactory.newSearchRequest( baseDn, filter, scope ); 608 } 609 610 611 /** 612 * {@inheritDoc} 613 */ 614 @Override 615 public SearchRequest newSearchRequest( String baseDn, String filter, SearchScope scope ) 616 { 617 return modelFactory.newSearchRequest( baseDn, filter, scope ); 618 } 619 620 621 /** 622 * {@inheritDoc} 623 */ 624 @Override 625 public SearchRequest newSearchRequest( Dn baseDn, FilterBuilder filter, SearchScope scope ) 626 { 627 return modelFactory.newSearchRequest( baseDn, filter, scope ); 628 } 629 630 631 /** 632 * {@inheritDoc} 633 */ 634 @Override 635 public SearchRequest newSearchRequest( Dn baseDn, String filter, SearchScope scope ) 636 { 637 return modelFactory.newSearchRequest( baseDn, filter, scope ); 638 } 639 640 641 /** 642 * {@inheritDoc} 643 */ 644 @Override 645 public SearchRequest newSearchRequest( String baseDn, FilterBuilder filter, SearchScope scope, String... attributes ) 646 { 647 return modelFactory.newSearchRequest( baseDn, filter, scope, attributes ); 648 } 649 650 651 /** 652 * {@inheritDoc} 653 */ 654 @Override 655 public SearchRequest newSearchRequest( String baseDn, String filter, SearchScope scope, String... attributes ) 656 { 657 return modelFactory.newSearchRequest( baseDn, filter, scope, attributes ); 658 } 659 660 661 /** 662 * {@inheritDoc} 663 */ 664 @Override 665 public SearchRequest newSearchRequest( Dn baseDn, FilterBuilder filter, SearchScope scope, String... attributes ) 666 { 667 return modelFactory.newSearchRequest( baseDn, filter, scope, attributes ); 668 } 669 670 671 /** 672 * {@inheritDoc} 673 */ 674 @Override 675 public SearchRequest newSearchRequest( Dn baseDn, String filter, SearchScope scope, String... attributes ) 676 { 677 return modelFactory.newSearchRequest( baseDn, filter, scope, attributes ); 678 } 679 680 681 /** 682 * {@inheritDoc} 683 */ 684 @Override 685 public <T extends ResultResponse> T responseOrException( T response ) 686 { 687 if ( ResultCodeEnum.SUCCESS != response.getLdapResult().getResultCode() ) 688 { 689 throw new LdapRequestUnsuccessfulException( response ); 690 } 691 return response; 692 } 693 694 695 private void returnLdapConnection( LdapConnection connection ) 696 { 697 if ( connection != null ) 698 { 699 try 700 { 701 connectionPool.releaseConnection( connection ); 702 } 703 catch ( LdapException e ) 704 { 705 throw new LdapRuntimeException( e ); 706 } 707 } 708 } 709 710 711 /** 712 * {@inheritDoc} 713 */ 714 @Override 715 public <T> List<T> search( String baseDn, FilterBuilder filter, SearchScope scope, 716 EntryMapper<T> entryMapper ) 717 { 718 return search( 719 modelFactory.newSearchRequest( baseDn, filter, scope ), 720 entryMapper ); 721 } 722 723 724 /** 725 * {@inheritDoc} 726 */ 727 @Override 728 public <T> List<T> search( String baseDn, String filter, SearchScope scope, 729 EntryMapper<T> entryMapper ) 730 { 731 return search( 732 modelFactory.newSearchRequest( baseDn, filter, scope ), 733 entryMapper ); 734 } 735 736 737 /** 738 * {@inheritDoc} 739 */ 740 @Override 741 public <T> List<T> search( Dn baseDn, FilterBuilder filter, SearchScope scope, 742 EntryMapper<T> entryMapper ) 743 { 744 return search( 745 modelFactory.newSearchRequest( baseDn, filter, scope ), 746 entryMapper ); 747 } 748 749 750 /** 751 * {@inheritDoc} 752 */ 753 @Override 754 public <T> List<T> search( Dn baseDn, String filter, SearchScope scope, 755 EntryMapper<T> entryMapper ) 756 { 757 return search( 758 modelFactory.newSearchRequest( baseDn, filter, scope ), 759 entryMapper ); 760 } 761 762 763 /** 764 * {@inheritDoc} 765 */ 766 @Override 767 public <T> List<T> search( String baseDn, FilterBuilder filter, SearchScope scope, 768 String[] attributes, EntryMapper<T> entryMapper ) 769 { 770 return search( 771 modelFactory.newSearchRequest( baseDn, filter, scope, attributes ), 772 entryMapper ); 773 } 774 775 776 /** 777 * {@inheritDoc} 778 */ 779 @Override 780 public <T> List<T> search( String baseDn, String filter, SearchScope scope, 781 String[] attributes, EntryMapper<T> entryMapper ) 782 { 783 return search( 784 modelFactory.newSearchRequest( baseDn, filter, scope, attributes ), 785 entryMapper ); 786 } 787 788 789 /** 790 * {@inheritDoc} 791 */ 792 @Override 793 public <T> List<T> search( Dn baseDn, FilterBuilder filter, SearchScope scope, 794 String[] attributes, EntryMapper<T> entryMapper ) 795 { 796 return search( 797 modelFactory.newSearchRequest( baseDn, filter, scope, attributes ), 798 entryMapper ); 799 } 800 801 802 /** 803 * {@inheritDoc} 804 */ 805 @Override 806 public <T> List<T> search( Dn baseDn, String filter, SearchScope scope, 807 String[] attributes, EntryMapper<T> entryMapper ) 808 { 809 return search( 810 modelFactory.newSearchRequest( baseDn, filter, scope, attributes ), 811 entryMapper ); 812 } 813 814 815 /** 816 * {@inheritDoc} 817 */ 818 @Override 819 public <T> List<T> search( SearchRequest searchRequest, 820 EntryMapper<T> entryMapper ) 821 { 822 List<T> entries = new ArrayList<>(); 823 824 LdapConnection connection = null; 825 try 826 { 827 connection = connectionPool.getConnection(); 828 829 for ( Entry entry : new EntryCursorImpl( connection.search( searchRequest ) ) ) 830 { 831 entries.add( entryMapper.map( entry ) ); 832 } 833 } 834 catch ( LdapException e ) 835 { 836 throw new LdapRuntimeException( e ); 837 } 838 finally 839 { 840 returnLdapConnection( connection ); 841 } 842 843 return entries; 844 } 845 846 847 /** 848 * {@inheritDoc} 849 */ 850 @Override 851 public <T> T searchFirst( String baseDn, FilterBuilder filter, SearchScope scope, 852 EntryMapper<T> entryMapper ) 853 { 854 return searchFirst( 855 modelFactory.newSearchRequest( baseDn, filter, scope ), 856 entryMapper ); 857 } 858 859 860 /** 861 * {@inheritDoc} 862 */ 863 @Override 864 public <T> T searchFirst( String baseDn, String filter, SearchScope scope, 865 EntryMapper<T> entryMapper ) 866 { 867 return searchFirst( 868 modelFactory.newSearchRequest( baseDn, filter, scope ), 869 entryMapper ); 870 } 871 872 873 /** 874 * {@inheritDoc} 875 */ 876 @Override 877 public <T> T searchFirst( Dn baseDn, FilterBuilder filter, SearchScope scope, 878 EntryMapper<T> entryMapper ) 879 { 880 return searchFirst( 881 modelFactory.newSearchRequest( baseDn, filter, scope ), 882 entryMapper ); 883 } 884 885 886 /** 887 * {@inheritDoc} 888 */ 889 @Override 890 public <T> T searchFirst( Dn baseDn, String filter, SearchScope scope, 891 EntryMapper<T> entryMapper ) 892 { 893 return searchFirst( 894 modelFactory.newSearchRequest( baseDn, filter, scope ), 895 entryMapper ); 896 } 897 898 899 /** 900 * {@inheritDoc} 901 */ 902 @Override 903 public <T> T searchFirst( String baseDn, FilterBuilder filter, SearchScope scope, 904 String[] attributes, EntryMapper<T> entryMapper ) 905 { 906 return searchFirst( 907 modelFactory.newSearchRequest( baseDn, filter, scope, attributes ), 908 entryMapper ); 909 } 910 911 912 /** 913 * {@inheritDoc} 914 */ 915 @Override 916 public <T> T searchFirst( String baseDn, String filter, SearchScope scope, 917 String[] attributes, EntryMapper<T> entryMapper ) 918 { 919 return searchFirst( 920 modelFactory.newSearchRequest( baseDn, filter, scope, attributes ), 921 entryMapper ); 922 } 923 924 925 /** 926 * {@inheritDoc} 927 */ 928 @Override 929 public <T> T searchFirst( Dn baseDn, FilterBuilder filter, SearchScope scope, 930 String[] attributes, EntryMapper<T> entryMapper ) 931 { 932 return searchFirst( 933 modelFactory.newSearchRequest( baseDn, filter, scope, attributes ), 934 entryMapper ); 935 } 936 937 938 /** 939 * {@inheritDoc} 940 */ 941 @Override 942 public <T> T searchFirst( Dn baseDn, String filter, SearchScope scope, 943 String[] attributes, EntryMapper<T> entryMapper ) 944 { 945 return searchFirst( 946 modelFactory.newSearchRequest( baseDn, filter, scope, attributes ), 947 entryMapper ); 948 } 949 950 951 /** 952 * {@inheritDoc} 953 */ 954 @Override 955 public <T> T searchFirst( SearchRequest searchRequest, 956 EntryMapper<T> entryMapper ) 957 { 958 // in case the caller did not set size limit, we cache original value, 959 // set to 1, then set back to original value before returning... 960 long originalSizeLimit = searchRequest.getSizeLimit(); 961 try 962 { 963 searchRequest.setSizeLimit( 1 ); 964 List<T> entries = search( searchRequest, entryMapper ); 965 return entries.isEmpty() ? null : entries.get( 0 ); 966 } 967 finally 968 { 969 searchRequest.setSizeLimit( originalSizeLimit ); 970 } 971 } 972 973 974 /** 975 * Sets the <code>modelFactory</code> implementation for this facade. 976 * 977 * @param modelFactory The model factory implementation 978 */ 979 public void setModelFactory( ModelFactory modelFactory ) 980 { 981 this.modelFactory = modelFactory; 982 } 983 984 985 /** 986 * Sets the <code>passwordPolicyResponder</code> implementation for this 987 * facade. 988 * 989 * @param passwordPolicyResponder The password policy responder 990 * implementation 991 */ 992 public void setPasswordPolicyResponder( PasswordPolicyResponder passwordPolicyResponder ) 993 { 994 this.passwordPolicyResponder = passwordPolicyResponder; 995 } 996}