001/** 002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 003 * 004 * Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved 005 * 006 * The contents of this file are subject to the terms 007 * of the Common Development and Distribution License 008 * (the License). You may not use this file except in 009 * compliance with the License. 010 * 011 * You can obtain a copy of the License at 012 * https://opensso.dev.java.net/public/CDDLv1.0.html or 013 * opensso/legal/CDDLv1.0.txt 014 * See the License for the specific language governing 015 * permission and limitations under the License. 016 * 017 * When distributing Covered Code, include this CDDL 018 * Header Notice in each file and include the License file 019 * at opensso/legal/CDDLv1.0.txt. 020 * If applicable, add the following below the CDDL Header, 021 * with the fields enclosed by brackets [] replaced by 022 * your own identifying information: 023 * "Portions Copyrighted [year] [name of copyright owner]" 024 * 025 * $Id: DataLayer.java,v 1.19 2009/11/20 23:52:52 ww203982 Exp $ 026 * 027 */ 028 029/** 030 * Portions Copyrighted [2011] [ForgeRock AS] 031 */ 032package com.iplanet.ums; 033 034import com.iplanet.am.util.SystemProperties; 035import com.iplanet.services.ldap.Attr; 036import com.iplanet.services.ldap.AttrSet; 037import com.iplanet.services.ldap.DSConfigMgr; 038import com.iplanet.services.ldap.LDAPServiceException; 039import com.iplanet.services.ldap.LDAPUser; 040import com.iplanet.services.ldap.ModSet; 041import com.iplanet.services.ldap.ServerInstance; 042import com.iplanet.services.ldap.event.EventService; 043import com.iplanet.services.util.I18n; 044import com.sun.identity.common.LDAPConnectionPool; 045import com.sun.identity.security.ServerInstanceAction; 046import com.sun.identity.shared.debug.Debug; 047import java.security.AccessController; 048import java.util.ArrayList; 049import java.util.Arrays; 050import java.util.Collection; 051import java.util.Collections; 052import java.util.HashMap; 053import java.util.HashSet; 054import java.util.Iterator; 055import java.util.StringTokenizer; 056import com.sun.identity.shared.ldap.LDAPAttribute; 057import com.sun.identity.shared.ldap.LDAPAttributeSet; 058import com.sun.identity.shared.ldap.LDAPBind; 059import com.sun.identity.shared.ldap.LDAPConnection; 060import com.sun.identity.shared.ldap.LDAPControl; 061import com.sun.identity.shared.ldap.LDAPEntry; 062import com.sun.identity.shared.ldap.LDAPException; 063import com.sun.identity.shared.ldap.LDAPModification; 064import com.sun.identity.shared.ldap.LDAPRequestParser; 065import com.sun.identity.shared.ldap.LDAPSchema; 066import com.sun.identity.shared.ldap.LDAPSchemaElement; 067import com.sun.identity.shared.ldap.LDAPSearchConstraints; 068import com.sun.identity.shared.ldap.LDAPAddRequest; 069import com.sun.identity.shared.ldap.LDAPDeleteRequest; 070import com.sun.identity.shared.ldap.LDAPModifyRequest; 071import com.sun.identity.shared.ldap.LDAPModifyRDNRequest; 072import com.sun.identity.shared.ldap.LDAPSearchRequest; 073import com.sun.identity.shared.ldap.LDAPSearchResults; 074import com.sun.identity.shared.ldap.LDAPSortKey; 075import com.sun.identity.shared.ldap.controls.LDAPProxiedAuthControl; 076import com.sun.identity.shared.ldap.controls.LDAPSortControl; 077import com.sun.identity.shared.ldap.controls.LDAPVirtualListControl; 078import org.forgerock.util.thread.listener.ShutdownListener; 079import org.forgerock.util.thread.listener.ShutdownManager; 080 081/** 082 * DataLayer (A PACKAGE SCOPE CLASS) to access LDAP or other database 083 * 084 * TODO: 1. Needs to subclass and isolate the current implementation of 085 * DataLayer as DSLayer for ldap specific operations 2. Improvements needed for 086 * _ldapPool: destroy(), initial bind user, tunning for MIN and MAX initial 087 * settings etc 3. May choose to extend implementation of _ldapPool from 088 * LDAPConnectionPool so that there is load balance between connections. Also 089 * _ldapPool may be implemented with a HashTable of (host,port) for mulitple 090 * pools of connections for mulitple (host,port) to DS servers instead of single 091 * host and port. 092 * 093 * @supported.api 094 */ 095public class DataLayer implements java.io.Serializable { 096 097 /** 098 * Static section to retrieve the debug object. 099 */ 100 private static Debug debug; 101 102 private static I18n i18n = I18n.getInstance(IUMSConstants.UMS_PKG); 103 104 /** 105 * Default minimal connections if none is defined in configuration 106 */ 107 108 /** 109 * Default maximum connections if none is defined in configuration 110 */ 111 static final int MAX_CONN = 20; 112 113 /** 114 * Default maximum backlog queue size 115 */ 116 static final int MAX_BACKLOG = 100; 117 118 static final String LDAP_MAXBACKLOG = "maxbacklog"; 119 120 static final String LDAP_RELEASECONNBEFORESEARCH = 121 "releaseconnectionbeforesearchcompletes"; 122 123 static final String LDAP_REFERRAL = "referral"; 124 125 private static int replicaRetryNum = 1; 126 127 private static long replicaRetryInterval = 1000; 128 129 private static final String LDAP_CONNECTION_NUM_RETRIES = 130 "com.iplanet.am.ldap.connection.num.retries"; 131 132 private static final String LDAP_CONNECTION_RETRY_INTERVAL = 133 "com.iplanet.am.ldap.connection.delay.between.retries"; 134 135 private static final String LDAP_CONNECTION_ERROR_CODES = 136 "com.iplanet.am.ldap.connection.ldap.error.codes.retries"; 137 138 private static int connNumRetry = 3; 139 140 private static int connRetryInterval = 1000; 141 142 private static HashSet retryErrorCodes = new HashSet(); 143 144 static { 145 debug = Debug.getInstance(IUMSConstants.UMS_DEBUG); 146 initConnectionParams(); 147 } 148 149 public static void initConnectionParams() { 150 String numRetryStr = SystemProperties.get(LDAP_CONNECTION_NUM_RETRIES); 151 if (numRetryStr != null) { 152 try { 153 connNumRetry = Integer.parseInt(numRetryStr); 154 } catch (NumberFormatException e) { 155 if (debug.warningEnabled()) { 156 debug.warning("Invalid value for " 157 + LDAP_CONNECTION_NUM_RETRIES); 158 } 159 } 160 } 161 162 String retryIntervalStr = SystemProperties 163 .get(LDAP_CONNECTION_RETRY_INTERVAL); 164 if (retryIntervalStr != null) { 165 try { 166 connRetryInterval = Integer.parseInt(retryIntervalStr); 167 } catch (NumberFormatException e) { 168 if (debug.warningEnabled()) { 169 debug.warning("Invalid value for " 170 + LDAP_CONNECTION_RETRY_INTERVAL); 171 } 172 } 173 } 174 175 String retryErrs = SystemProperties.get(LDAP_CONNECTION_ERROR_CODES); 176 if (retryErrs != null) { 177 StringTokenizer stz = new StringTokenizer(retryErrs, ","); 178 while (stz.hasMoreTokens()) { 179 retryErrorCodes.add(stz.nextToken().trim()); 180 } 181 } 182 183 if (debug.messageEnabled()) { 184 debug.message("DataLayer: number of retry = " + connNumRetry); 185 debug.message("DataLayer: retry interval = " + connRetryInterval); 186 debug.message("DataLayer: retry error codes = " + retryErrorCodes); 187 } 188 } 189 190 /** 191 * DataLayer constructor 192 */ 193 private DataLayer() { 194 } 195 196 /** 197 * Constructor given the extra parameter of guid and pwd identifying an 198 * authenticated principal 199 * 200 * @param host 201 * LDAP host 202 * @param port 203 * LDAP port 204 * @param pwd 205 * Password for the user 206 */ 207 private DataLayer(String id, String pwd, String host, int port) 208 throws UMSException { 209 m_proxyUser = id; 210 m_proxyPassword = pwd; 211 m_host = host; 212 m_port = port; 213 214 initReplicaProperties(); 215 initLdapPool(); 216 } 217 218 /** 219 * create the singelton DataLayer object if it doesn't exist already. 220 * 221 * @supported.api 222 */ 223 public synchronized static DataLayer getInstance(ServerInstance serverCfg) 224 throws UMSException { 225 // Make sure only one instance of this class is created. 226 if (m_instance == null) { 227 String host = "localhost"; 228 int port = 389; 229 String pUser = ""; 230 String pPwd = ""; 231 232 if (serverCfg != null) { 233 host = serverCfg.getServerName(); 234 port = serverCfg.getPort(); 235 pUser = serverCfg.getAuthID(); 236 pPwd = (String) AccessController 237 .doPrivileged(new ServerInstanceAction(serverCfg)); 238 } 239 m_instance = new DataLayer(pUser, pPwd, host, port); 240 241 // Start the EventService thread if it has not already started. 242 initializeEventService(); 243 } 244 return m_instance; 245 } 246 247 /** 248 * create the singelton DataLayer object if it doesn't exist already. 249 * Assumes the server instance for "LDAPUser.Type.AUTH_PROXY". 250 * 251 * @supported.api 252 */ 253 public static DataLayer getInstance() throws UMSException { 254 // Make sure only one instance of this class is created. 255 if (m_instance == null) { 256 try { 257 DSConfigMgr cfgMgr = DSConfigMgr.getDSConfigMgr(); 258 ServerInstance serverCfg = cfgMgr 259 .getServerInstance(LDAPUser.Type.AUTH_PROXY); 260 m_instance = getInstance(serverCfg); 261 } catch (LDAPServiceException ex) { 262 debug.error("Error: Unable to get server config instance " 263 + ex.getMessage()); 264 } 265 } 266 return m_instance; 267 } 268 269 /** 270 * Get connection from pool. Reauthenticate if necessary 271 * 272 * @return connection that is available to use. 273 * 274 * @supported.api 275 */ 276 public LDAPConnection getConnection(java.security.Principal principal) { 277 if (_ldapPool == null) 278 return null; 279 280 if (debug.messageEnabled()) { 281 debug.message("Invoking _ldapPool.getConnection()"); 282 } 283 284 // proxy as given principal 285 LDAPProxiedAuthControl proxyCtrl = new LDAPProxiedAuthControl(principal 286 .getName(), true); 287 LDAPConnection conn = _ldapPool.getConnection(); 288 if (debug.messageEnabled()) { 289 debug.message("Got Connection : " + conn); 290 } 291 LDAPSearchConstraints cons = conn.getSearchConstraints(); 292 cons.setServerControls(proxyCtrl); 293 conn.setSearchConstraints(cons); 294 295 return conn; 296 } 297 298 /** 299 * Just call the pool method to release the connection so that the given 300 * connection is free for others to use 301 * 302 * @param conn 303 * connection in the pool to be released for others to use 304 * 305 * @supported.api 306 */ 307 public void releaseConnection(LDAPConnection conn) { 308 if (_ldapPool == null || conn == null) 309 return; 310 311 // reset the original constraints 312 // TODO: check with ldapjdk and see if this is appropriate 313 // to restore the default constraints. 314 // 315 conn.setSearchConstraints(_defaultSearchConstraints); 316 317 // A soft close on the connection. Returns the connection to the pool 318 // and 319 // make it available. 320 if (debug.messageEnabled()) { 321 debug.message("Invoking _ldapPool.close(conn) : " + conn); 322 } 323 _ldapPool.close(conn); 324 if (debug.messageEnabled()) { 325 debug.message("Released Connection : " + conn); 326 } 327 } 328 329 /** 330 * Just call the pool method to release the connection so that the given 331 * connection is free for others to use 332 * 333 * @param conn 334 * connection in the pool to be released for others to use 335 * @param ldapErrCode ldap exception error code used to determine failover. 336 * 337 * @supported.api 338 */ 339 public void releaseConnection( LDAPConnection conn , int ldapErrCode) 340 { 341 if (_ldapPool == null || conn == null) return; 342 343 // reset the original constraints 344 // TODO: check with ldapjdk and see if this is appropriate 345 // to restore the default constraints. 346 // 347 conn.setSearchConstraints(_defaultSearchConstraints); 348 349 // A soft close on the connection. 350 // Returns the connection to the pool and 351 // make it available. 352 if (debug.messageEnabled()) { 353 debug.message("Invoking _ldapPool.close(conn,ldapErrCode) : " + 354 conn + ":" + ldapErrCode); 355 } 356 _ldapPool.close( conn, ldapErrCode ); 357 if (debug.messageEnabled()) { 358 debug.message("Released Connection:close(conn,ldapErrCode) : " + 359 conn); 360 } 361 } 362 363 /** 364 * Returns String values of the attribute. 365 * 366 * @param principal Authentication Principal. 367 * @param guid distinguished name. 368 * @param attrName attribute name. 369 * 370 * @supported.api 371 */ 372 public String[] getAttributeString( 373 java.security.Principal principal, 374 Guid guid, 375 String attrName 376 ) { 377 String id = guid.getDn(); 378 LDAPEntry ldapEntry = null; 379 LDAPSearchRequest request = 380 LDAPRequestParser.parseReadRequest(id); 381 try { 382 ldapEntry = readLDAPEntry(principal, request); 383 } catch (Exception e) { 384 if (debug.warningEnabled()) { 385 debug.warning( 386 "Exception in DataLayer.getAttributeString for DN: " 387 + id, e); 388 } 389 return null; 390 } 391 LDAPAttribute attr = ldapEntry.getAttribute(attrName); 392 return attr.getStringValueArray(); 393 } 394 395 /** 396 * Returns <code>Attr</code> from the given attribute name. 397 * 398 * @param principal Authentication Principal. 399 * @param guid Distinguished name. 400 * @param attrName Attribute name. 401 * 402 * @supported.api 403 */ 404 public Attr getAttribute( 405 java.security.Principal principal, 406 Guid guid, 407 String attrName 408 ) { 409 String id = guid.getDn(); 410 LDAPEntry ldapEntry = null; 411 try { 412 String[] attrNames = new String[1]; 413 attrNames[0] = attrName; 414 LDAPSearchRequest request = LDAPRequestParser.parseReadRequest(id, 415 attrNames); 416 ldapEntry = readLDAPEntry(principal, request); 417 } catch (Exception e) { 418 if (debug.warningEnabled()) { 419 debug.warning("Exception in DataLayer.getAttribute for DN: " 420 + id, e); 421 } 422 return null; 423 } 424 LDAPAttribute ldapAttr = ldapEntry.getAttribute(attrName); 425 if (ldapAttr == null) { 426 return null; 427 } else { 428 return new Attr(ldapAttr); 429 } 430 } 431 432 /** 433 * Returns attributes for the given attribute names. 434 * 435 * @param principal Authentication Principal. 436 * @param guid Distinguished name. 437 * @param attrNames Attribute names. 438 * @return collection of Attr. 439 * 440 * @supported.api 441 */ 442 public Collection getAttributes( 443 java.security.Principal principal, 444 Guid guid, 445 Collection attrNames 446 ) { 447 Collection attributes = new ArrayList(); 448 String id = guid.getDn(); 449 LDAPSearchRequest request = LDAPRequestParser.parseReadRequest(id, 450 (String[]) attrNames.toArray(EMPTY_STRING_ARRAY)); 451 LDAPEntry ldapEntry = null; 452 try { 453 ldapEntry = readLDAPEntry(principal, request); 454 } catch (Exception e) { 455 if (debug.warningEnabled()) { 456 debug.warning("Exception in DataLayer.getAttributes for DN: " 457 + id, e); 458 } 459 return null; 460 } 461 if (ldapEntry == null) { 462 debug.warning("No attributes returned may not have " + 463 "permission to read"); 464 return Collections.EMPTY_SET; 465 } 466 Iterator iter = attrNames.iterator(); 467 while (iter.hasNext()) { 468 String attrName = (String) iter.next(); 469 LDAPAttribute ldapAttribute = ldapEntry.getAttribute(attrName); 470 if (ldapAttribute != null) { 471 attributes.add(new Attr(ldapAttribute)); 472 } 473 } 474 return attributes; 475 } 476 477 /** 478 * Adds entry to the server. 479 * 480 * @param principal Authenticated Principal. 481 * @param guid Distinguished name. 482 * @param attrSet attribute set containing name/value pairs. 483 * @exception AccessRightsException if insufficient access> 484 * @exception EntryAlreadyExistsException if the entry already exists. 485 * @exception UMSException if fail to add entry. 486 * 487 * @supported.api 488 */ 489 public void addEntry( 490 java.security.Principal principal, 491 Guid guid, 492 AttrSet attrSet 493 ) throws AccessRightsException, EntryAlreadyExistsException, UMSException { 494 LDAPConnection conn = null; 495 String id = guid.getDn(); 496 int errorCode = 0; 497 498 try { 499 LDAPEntry entry = new LDAPEntry(id, attrSet.toLDAPAttributeSet()); 500 LDAPAddRequest request = LDAPRequestParser.parseAddRequest(entry); 501 int retry = 0; 502 while (retry <= connNumRetry) { 503 if (debug.messageEnabled()) { 504 debug.message("DataLayer.addEntry retry: " + retry); 505 } 506 507 try { 508 conn = getConnection(principal); 509 conn.add(request); 510 return; 511 } catch (LDAPException e) { 512 errorCode = e.getLDAPResultCode(); 513 releaseConnection(conn, errorCode); 514 conn = null; 515 if (!retryErrorCodes.contains("" + e.getLDAPResultCode()) 516 || retry == connNumRetry) { 517 throw e; 518 } 519 retry++; 520 try { 521 Thread.sleep(connRetryInterval); 522 } catch (InterruptedException ex) { 523 } 524 } 525 } 526 } catch (LDAPException e) { 527 if (debug.warningEnabled()) { 528 debug.warning("Exception in DataLayer.addEntry for DN: " + id, 529 e); 530 } 531 errorCode = e.getLDAPResultCode(); 532 String[] args = { id }; 533 switch (errorCode) { 534 case LDAPException.ENTRY_ALREADY_EXISTS: 535 throw new EntryAlreadyExistsException(i18n.getString( 536 IUMSConstants.ENTRY_ALREADY_EXISTS, args), e); 537 case LDAPException.INSUFFICIENT_ACCESS_RIGHTS: 538 throw new AccessRightsException(i18n.getString( 539 IUMSConstants.INSUFFICIENT_ACCESS_ADD, args), e); 540 default: 541 throw new UMSException(i18n.getString( 542 IUMSConstants.UNABLE_TO_ADD_ENTRY, args), e); 543 } 544 } finally { 545 if (conn != null) { 546 releaseConnection(conn); 547 } 548 } 549 } 550 551 /** 552 * Delete entry from the server 553 * 554 * @param guid 555 * globally unique identifier for the entry 556 * @exception AccessRightsException 557 * insufficient access 558 * @exception EntryNotFoundException 559 * if the entry is not found 560 * @exception UMSException 561 * Fail to delete the entry 562 * 563 * @supported.api 564 */ 565 public void deleteEntry(java.security.Principal principal, Guid guid) 566 throws AccessRightsException, EntryNotFoundException, UMSException { 567 if (guid == null) { 568 String msg = i18n.getString(IUMSConstants.BAD_ID); 569 throw new IllegalArgumentException(msg); 570 } 571 LDAPConnection conn = null; 572 String id = guid.getDn(); 573 int errorCode = 0; 574 575 try { 576 LDAPDeleteRequest request = 577 LDAPRequestParser.parseDeleteRequest(id); 578 int retry = 0; 579 while (retry <= connNumRetry) { 580 if (debug.messageEnabled()) { 581 debug.message("DataLayer.deleteEntry retry: " + retry); 582 } 583 584 try { 585 conn = getConnection(principal); 586 conn.delete(request); 587 return; 588 } catch (LDAPException e) { 589 errorCode = e.getLDAPResultCode(); 590 releaseConnection(conn, errorCode); 591 conn = null; 592 if (!retryErrorCodes.contains("" + e.getLDAPResultCode()) 593 || retry == connNumRetry) { 594 throw e; 595 } 596 retry++; 597 try { 598 Thread.sleep(connRetryInterval); 599 } catch (InterruptedException ex) { 600 } 601 } 602 } 603 } catch (LDAPException e) { 604 debug.error("Exception in DataLayer.deleteEntry for DN: " + id, e); 605 errorCode = e.getLDAPResultCode(); 606 String[] args = { id }; 607 switch (errorCode) { 608 case LDAPException.NO_SUCH_OBJECT: 609 throw new EntryNotFoundException(i18n.getString( 610 IUMSConstants.ENTRY_NOT_FOUND, args), e); 611 case LDAPException.INSUFFICIENT_ACCESS_RIGHTS: 612 throw new AccessRightsException(i18n.getString( 613 IUMSConstants.INSUFFICIENT_ACCESS_DELETE, args), e); 614 default: 615 throw new UMSException(i18n.getString( 616 IUMSConstants.UNABLE_TO_DELETE_ENTRY, args), e); 617 } 618 } finally { 619 if (conn != null) { 620 releaseConnection(conn); 621 } 622 } 623 } 624 625 /** 626 * Read an ldap entry 627 * 628 * @param guid 629 * globally unique identifier for the entry 630 * @return an attribute set representing the entry in ldap, all non 631 * operational attributes are read 632 * @exception EntryNotFoundException 633 * if the entry is not found 634 * @exception UMSException 635 * Fail to read the entry 636 * 637 * @supported.api 638 */ 639 public AttrSet read(java.security.Principal principal, Guid guid) 640 throws EntryNotFoundException, UMSException { 641 return read(principal, guid, null); 642 } 643 644 /** 645 * Reads an ldap entry. 646 * 647 * @param principal Authentication Principal. 648 * @param guid Globally unique identifier for the entry. 649 * @param attrNames Attributes to read. 650 * @return an attribute set representing the entry in LDAP. 651 * @exception EntryNotFoundException if the entry is not found. 652 * @exception UMSException if fail to read the entry. 653 * 654 * @supported.api 655 */ 656 public AttrSet read( 657 java.security.Principal principal, 658 Guid guid, 659 String attrNames[] 660 ) throws EntryNotFoundException, UMSException { 661 String id = guid.getDn(); 662 LDAPEntry entry = null; 663 LDAPSearchRequest request = LDAPRequestParser.parseReadRequest(id, 664 attrNames); 665 666 try { 667 entry = readLDAPEntry(principal, request); 668 } catch (LDAPException e) { 669 if (debug.warningEnabled()) { 670 debug.warning("Exception in DataLayer.read for DN: " + id); 671 debug.warning("LDAPException: " + e); 672 } 673 int errorCode = e.getLDAPResultCode(); 674 String[] args = { id }; 675 if (errorCode == LDAPException.NO_SUCH_OBJECT) { 676 throw new EntryNotFoundException(i18n.getString( 677 IUMSConstants.ENTRY_NOT_FOUND, args), e); 678 } else { 679 throw new UMSException(i18n.getString( 680 IUMSConstants.UNABLE_TO_READ_ENTRY, args), e); 681 } 682 } 683 684 if (entry == null) { 685 throw new AccessRightsException(id); 686 } 687 688 LDAPAttributeSet ldapAttrSet = entry.getAttributeSet(); 689 if (ldapAttrSet == null) { 690 String[] args = { id }; 691 throw new EntryNotFoundException(i18n.getString( 692 IUMSConstants.ENTRY_NOT_FOUND, args)); 693 } 694 695 return new AttrSet(ldapAttrSet); 696 } 697 698 public void rename(java.security.Principal principal, Guid guid, 699 String newName, boolean deleteOldName) 700 throws AccessRightsException, EntryNotFoundException, UMSException { 701 LDAPConnection conn = null; 702 String id = guid.getDn(); 703 int errorCode = 0; 704 705 try { 706 LDAPModifyRDNRequest request = 707 LDAPRequestParser.parseModifyRDNRequest(id, newName, 708 deleteOldName); 709 int retry = 0; 710 while (retry <= connNumRetry) { 711 if (debug.messageEnabled()) { 712 debug.message("DataLayer.rename retry: " + retry); 713 } 714 715 try { 716 conn = getConnection(principal); 717 conn.rename(request); 718 return; 719 } catch (LDAPException e) { 720 errorCode = e.getLDAPResultCode(); 721 releaseConnection(conn, errorCode); 722 conn = null; 723 if (!retryErrorCodes.contains("" + e.getLDAPResultCode()) 724 || retry == connNumRetry) { 725 throw e; 726 } 727 retry++; 728 try { 729 Thread.sleep(connRetryInterval); 730 } catch (InterruptedException ex) { 731 } 732 } 733 } 734 } catch (LDAPException e) { 735 if (debug.warningEnabled()) { 736 debug.warning("Exception in DataLayer.rename for DN: " + id, e); 737 } 738 errorCode = e.getLDAPResultCode(); 739 switch (errorCode) { 740 case LDAPException.NO_SUCH_OBJECT: 741 throw new EntryNotFoundException(id, e); 742 case LDAPException.INSUFFICIENT_ACCESS_RIGHTS: 743 throw new AccessRightsException(id, e); 744 default: 745 throw new UMSException(id, e); 746 } 747 } finally { 748 if (conn != null) { 749 releaseConnection(conn); 750 } 751 } 752 } 753 754 /** 755 * Modifies an ldap entry. 756 * 757 * @param principal Authentication Principal. 758 * @param guid globally unique identifier for the entry. 759 * @param modSet Set of modifications for the entry. 760 * @exception AccessRightsException if insufficient access 761 * @exception EntryNotFoundException if the entry is not found. 762 * @exception UMSException if failure 763 * 764 * @supported.api 765 */ 766 public void modify( 767 java.security.Principal principal, 768 Guid guid, 769 ModSet modSet 770 ) throws AccessRightsException, EntryNotFoundException, UMSException { 771 LDAPConnection conn = null; 772 String id = guid.getDn(); 773 int errorCode = 0; 774 775 try { 776 LDAPModifyRequest request = LDAPRequestParser.parseModifyRequest( 777 id, modSet); 778 int retry = 0; 779 while (retry <= connNumRetry) { 780 if (debug.messageEnabled()) { 781 debug.message("DataLayer.modify retry: " + retry); 782 } 783 784 try { 785 conn = getConnection(principal); 786 conn.modify(request); 787 return; 788 } catch (LDAPException e) { 789 errorCode = e.getLDAPResultCode(); 790 releaseConnection(conn, errorCode); 791 conn = null; 792 if (!retryErrorCodes.contains("" + e.getLDAPResultCode()) 793 || retry == connNumRetry) { 794 throw e; 795 } 796 retry++; 797 try { 798 Thread.sleep(connRetryInterval); 799 } catch (InterruptedException ex) { 800 } 801 } 802 } 803 } catch (LDAPException e) { 804 if (debug.warningEnabled()) { 805 debug.warning("Exception in DataLayer.modify for DN: " + id, e); 806 } 807 errorCode = e.getLDAPResultCode(); 808 switch (errorCode) { 809 case LDAPException.NO_SUCH_OBJECT: 810 throw new EntryNotFoundException(id, e); 811 case LDAPException.INSUFFICIENT_ACCESS_RIGHTS: 812 throw new AccessRightsException(id, e); 813 default: 814 throw new UMSException(id, e); 815 } 816 } finally { 817 if (conn != null) { 818 releaseConnection(conn); 819 } 820 } 821 } 822 823 /** 824 * Changes user password. 825 * 826 * @param guid globally unique identifier for the entry. 827 * @param attrName password attribute name 828 * @param oldPassword old password 829 * @param newPassword new password 830 * @exception AccessRightsException if insufficient access 831 * @exception EntryNotFoundException if the entry is not found. 832 * @exception UMSException if failure 833 * 834 * @supported.api 835 */ 836 public void changePassword(Guid guid, String attrName, String oldPassword, 837 String newPassword) 838 throws AccessRightsException, EntryNotFoundException, UMSException { 839 840 ModSet modSet = new ModSet(); 841 modSet.add(LDAPModification.REPLACE, 842 new LDAPAttribute(attrName, newPassword)); 843 844 String id = guid.getDn(); 845 846 LDAPConnection ldc = null; 847 int resultCode = 0; 848 try { 849 DSConfigMgr dsCfg = DSConfigMgr.getDSConfigMgr(); 850 String hostAndPort = dsCfg.getHostName("default"); 851 852 ldc = new LDAPConnection(); 853 ldc.connect(hostAndPort, 389, id, oldPassword); 854 855 ldc.modify(id, modSet); 856 } catch (LDAPException ldex) { 857 if (debug.warningEnabled()) { 858 debug.warning("DataLayer.changePassword:", ldex); 859 } 860 int errorCode = ldex.getLDAPResultCode(); 861 switch (errorCode) { 862 case LDAPException.NO_SUCH_OBJECT: 863 throw new EntryNotFoundException(id, ldex); 864 case LDAPException.INSUFFICIENT_ACCESS_RIGHTS: 865 throw new AccessRightsException(id, ldex); 866 default: 867 throw new UMSException(id, ldex); 868 } 869 } catch (LDAPServiceException ex) { 870 debug.error("DataLayer.changePassword:", ex); 871 throw new UMSException(id, ex); 872 } finally { 873 if (ldc != null) { 874 try { 875 ldc.disconnect(); 876 } catch (LDAPException lde) { 877 } 878 } 879 } 880 } 881 882 /** 883 * Adds value for an attribute and saves the change in the database. 884 * 885 * @param principal Authenticated Principal. 886 * @param guid ID of the entry to which to add the attribute value. 887 * @param name name of the attribute to which value is being added. 888 * @param value Value to be added to the attribute. 889 * @throws UMSException if there is any error while adding the value 890 * 891 * @supported.api 892 */ 893 public void addAttributeValue( 894 java.security.Principal principal, 895 Guid guid, 896 String name, 897 String value 898 ) throws UMSException { 899 ModSet modSet = new ModSet(); 900 modSet.add(LDAPModification.ADD, new LDAPAttribute(name, value)); 901 902 // Delegate to the other modify() method. 903 modify(principal, guid, modSet); 904 } 905 906 /** 907 * Removes value for an attribute and saves the change in the database. 908 * 909 * @param principal Authenticated Principal. 910 * @param guid the id of the entry from which to remove the attribute value. 911 * @param name Name of the attribute from which value is being removed 912 * @param value Value to be removed from the attribute. 913 * @throws UMSException if there is any error while removing the value. 914 * 915 * @supported.api 916 */ 917 public void removeAttributeValue(java.security.Principal principal, 918 Guid guid, String name, String value) throws UMSException { 919 ModSet modSet = new ModSet(); 920 modSet.add(LDAPModification.DELETE, new LDAPAttribute(name, value)); 921 922 // Delegate to the other modify() method. 923 modify(principal, guid, modSet); 924 } 925 926 /** 927 * retrive LDAPConnection for search. 928 */ 929 private LDAPConnection getSearchConnection( 930 java.security.Principal principal, SearchControl searchControl) { 931 LDAPConnection conn = getConnection(principal); 932 933 if (searchControl != null) { 934 LDAPSearchConstraints constraints; 935 int[] vlvRange = searchControl.getVLVRange(); 936 SortKey[] sortKeys = searchControl.getSortKeys(); 937 LDAPSortKey[] ldapSortKeys; 938 ArrayList ctrls = new ArrayList(); // will hold all server controls 939 940 if (sortKeys != null) { 941 ldapSortKeys = new LDAPSortKey[sortKeys.length]; 942 for (int i = 0; i < ldapSortKeys.length; i++) { 943 ldapSortKeys[i] = new LDAPSortKey( 944 sortKeys[i].attributeName, sortKeys[i].reverse); 945 } 946 947 ctrls.add(new LDAPSortControl(ldapSortKeys, false)); 948 949 if (vlvRange != null) { 950 if (searchControl.getVLVJumpTo() == null) { 951 ctrls.add(new LDAPVirtualListControl(vlvRange[0], 952 vlvRange[1], vlvRange[2], 0)); 953 } else { 954 ctrls.add(new LDAPVirtualListControl(searchControl 955 .getVLVJumpTo(), vlvRange[1], vlvRange[2])); 956 } 957 } 958 } 959 960 constraints = conn.getSearchConstraints(); 961 LDAPControl[] existingCtrls = constraints.getServerControls(); 962 ctrls.addAll(Arrays.asList(existingCtrls)); 963 964 // This should be 0 if intermediate results are not needed, 965 // and 1 if results are to be processed as they come in. 966 // (By default, this is 1.) 967 constraints.setBatchSize(1); 968 constraints.setMaxResults(searchControl.getMaxResults()); 969 constraints.setServerTimeLimit(searchControl.getTimeOut()); 970 if (sortKeys != null) { 971 constraints.setServerControls((LDAPControl[]) ctrls 972 .toArray(new LDAPControl[0])); 973 } 974 975 searchControl.set("constraints", constraints); 976 } 977 978 return conn; 979 } 980 981 /** 982 * Performs synchronous search based on specified ldap filter. This is low 983 * level API which assumes caller knows how to construct a data store filer. 984 * 985 * @param principal Authenticated Principal. 986 * @param guid Unique identifier for the entry. 987 * @param scope Scope can be either <code>SCOPE_ONE</code>, 988 * <code>SCOPE_SUB</code> or <code>SCOPE_BASE</code>. 989 * @param searchFilter Search filter for this search. 990 * @param attrNames Attribute name for retrieving. 991 * @param attrOnly if true, returns the names but not the values of the 992 * attributes found. 993 * @param searchControl Search Control. 994 * @exception UMSException if failure. 995 * @exception InvalidSearchFilterException if failure 996 * 997 * @supported.api 998 */ 999 public SearchResults search( 1000 java.security.Principal principal, 1001 Guid guid, 1002 int scope, 1003 String searchFilter, 1004 String attrNames[], 1005 boolean attrOnly, 1006 SearchControl searchControl 1007 ) throws InvalidSearchFilterException, UMSException { 1008 LDAPConnection conn = null; 1009 String id = guid.getDn(); 1010 1011 // always add "objectclass" to attributes to get, to find the right java 1012 // class 1013 String[] attrNames1 = null; 1014 if (attrNames != null) { 1015 attrNames1 = new String[attrNames.length + 1]; 1016 System.arraycopy(attrNames, 0, attrNames1, 0, attrNames.length); 1017 attrNames1[attrNames1.length - 1] = "objectclass"; 1018 } else { 1019 attrNames1 = new String[] { "objectclass" }; 1020 } 1021 1022 LDAPSearchResults ldapResults = null; 1023 1024 // if searchFilter is null, search for everything under the base 1025 if (searchFilter == null) { 1026 searchFilter = "(objectclass=*)"; 1027 } 1028 int errorCode = 0; 1029 1030 try { 1031 conn = getSearchConnection(principal, searchControl); 1032 // call readLDAPEntry() only in replica case, save one LDAP search 1033 // assume replica case when replicaRetryNum is not 0 1034 if (replicaRetryNum != 0) { 1035 readLDAPEntry(conn, id, null); 1036 } 1037 1038 int retry = 0; 1039 while (retry <= connNumRetry) { 1040 if (debug.messageEnabled()) { 1041 debug.message("DataLayer.search retry: " + retry); 1042 } 1043 1044 try { 1045 if (searchControl == null) { 1046 ldapResults = conn.search(id, scope, searchFilter, 1047 attrNames1, attrOnly); 1048 } else { 1049 if (searchControl.isGetAllReturnAttributesEnabled()) { 1050 /* 1051 * The array {"*"} is used, because LDAPv3 defines 1052 * "*" as a special string indicating all 1053 * attributes. This gets all the attributes. 1054 */ 1055 1056 attrNames1 = new String[] { "*" }; 1057 } 1058 1059 ldapResults = conn.search(id, scope, searchFilter, 1060 attrNames1, attrOnly, 1061 (LDAPSearchConstraints) searchControl 1062 .get("constraints")); 1063 } 1064 break; 1065 } catch (LDAPException e) { 1066 errorCode = e.getLDAPResultCode(); 1067 if (!retryErrorCodes.contains("" + e.getLDAPResultCode()) 1068 || retry == connNumRetry) { 1069 throw e; 1070 } 1071 retry++; 1072 try { 1073 Thread.sleep(connRetryInterval); 1074 } catch (InterruptedException ex) { 1075 } 1076 } 1077 } 1078 1079 // TODO: need review and see if conn is recorded properly for 1080 // subsequent use 1081 // 1082 SearchResults result = new SearchResults(ldapResults, conn, this); 1083 result.set(SearchResults.BASE_ID, id); 1084 result.set(SearchResults.SEARCH_FILTER, searchFilter); 1085 result.set(SearchResults.SEARCH_SCOPE, new Integer(scope)); 1086 1087 if ((searchControl != null) 1088 && (searchControl.contains(SearchControl.KeyVlvRange) 1089 || searchControl.contains(SearchControl.KeyVlvJumpTo))) { 1090 result.set(SearchResults.EXPECT_VLV_RESPONSE, Boolean.TRUE); 1091 1092 } 1093 1094 if (searchControl != null 1095 && searchControl.contains(SearchControl.KeySortKeys)) { 1096 SortKey[] sortKeys = searchControl.getSortKeys(); 1097 if (sortKeys != null && sortKeys.length > 0) { 1098 result.set(SearchResults.SORT_KEYS, sortKeys); 1099 } 1100 } 1101 1102 return result; 1103 1104 } catch (LDAPException e) { 1105 errorCode = e.getLDAPResultCode(); 1106 releaseConnection(conn, errorCode); 1107 if (debug.warningEnabled()) { 1108 debug.warning("Exception in DataLayer.search: ", e); 1109 } 1110 String msg = i18n.getString(IUMSConstants.SEARCH_FAILED); 1111 switch (errorCode) { 1112 case LDAPException.TIME_LIMIT_EXCEEDED: { 1113 int timeLimit = searchControl != null ? searchControl 1114 .getTimeOut() : 0; 1115 throw new TimeLimitExceededException(String.valueOf(timeLimit), 1116 e); 1117 } 1118 case LDAPException.SIZE_LIMIT_EXCEEDED: { 1119 int sizeLimit = searchControl != null ? searchControl 1120 .getMaxResults() : 0; 1121 throw new SizeLimitExceededException(String.valueOf(sizeLimit), 1122 e); 1123 } 1124 case LDAPException.PARAM_ERROR: 1125 case LDAPException.PROTOCOL_ERROR: 1126 throw new InvalidSearchFilterException(searchFilter, e); 1127 default: 1128 throw new UMSException(msg, e); 1129 } 1130 1131 } 1132 } 1133 1134 /** 1135 * Perform synchronous search based on specified ldap filter. This is low 1136 * level API which assumes caller knows how to construct a data store filer. 1137 * 1138 * @param principal Authenticated Principal. 1139 * @param guid Unique identifier for the entry 1140 * @param scope Scope can be either <code>SCOPE_ONE</code>, 1141 * <code>SCOPE_SUB</code>, <code>SCOBE_BASE</code> 1142 * @param searchFilter Search filter for this search. 1143 * @param searchControl Search Control. 1144 * @exception UMSException if failure. 1145 * @exception InvalidSearchFilterException if failure. 1146 * 1147 * @supported.api 1148 */ 1149 public SearchResults searchIDs( 1150 java.security.Principal principal, 1151 Guid guid, 1152 int scope, 1153 String searchFilter, 1154 SearchControl searchControl 1155 ) throws InvalidSearchFilterException, UMSException { 1156 // TODO: support LDAP referral 1157 String attrNames[] = { "objectclass" }; 1158 return search(principal, guid, scope, searchFilter, attrNames, false, 1159 searchControl); 1160 } 1161 1162 /** 1163 * Fetches the schema from the LDAP directory server. Retrieve the entire 1164 * schema from the root of a Directory Server. 1165 * 1166 * @return the schema in the LDAP directory server 1167 * @exception AccessRightsException 1168 * insufficient access 1169 * @exception UMSException 1170 * Fail to fetch the schema. 1171 * 1172 * @supported.api 1173 */ 1174 public LDAPSchema getSchema(java.security.Principal principal) 1175 throws AccessRightsException, UMSException { 1176 LDAPConnection conn = null; 1177 LDAPSchema dirSchema = new LDAPSchema(); 1178 int errorCode = 0; 1179 1180 try { 1181 LDAPSearchRequest request = LDAPRequestParser.parseReadRequest( 1182 "fake=fake"); 1183 conn = getConnection(principal); 1184 // disable the checking of attribute syntax quoting and the 1185 // read on "" 1186 conn.setProperty(DSConfigMgr.SCHEMA_BUG_PROPERTY, 1187 DSConfigMgr.VAL_STANDARD); 1188 int retry = 0; 1189 while (retry <= connNumRetry) { 1190 if (debug.messageEnabled()) { 1191 debug.message("DataLayer.getSchema retry: " + retry); 1192 } 1193 1194 try { 1195 // after connection is down, fetchSchema will not try to 1196 // reconnect. So use read to force it to reconnect 1197 if (retry > 0) { 1198 try { 1199 conn.read(request); 1200 } catch (Exception ex) { 1201 } 1202 } 1203 1204 dirSchema.fetchSchema(conn, "cn=schema"); 1205 return dirSchema; 1206 } catch (LDAPException e) { 1207 errorCode = e.getLDAPResultCode(); 1208 releaseConnection(conn, errorCode); 1209 conn = null; 1210 if (!retryErrorCodes.contains("" + e.getLDAPResultCode()) 1211 || retry == connNumRetry) { 1212 throw e; 1213 } 1214 retry++; 1215 try { 1216 Thread.sleep(connRetryInterval); 1217 } catch (InterruptedException ex) { 1218 } 1219 } 1220 } 1221 } catch (LDAPException e) { 1222 debug.error("Exception in DataLayer.getSchema: ", e); 1223 errorCode = e.getLDAPResultCode(); 1224 switch (errorCode) { 1225 case LDAPException.INSUFFICIENT_ACCESS_RIGHTS: 1226 throw new AccessRightsException(m_host, e); 1227 default: 1228 throw new UMSException(m_host, e); 1229 } 1230 } finally { 1231 if (conn != null) { 1232 releaseConnection(conn); 1233 } 1234 } 1235 1236 return dirSchema; 1237 } 1238 1239 /** 1240 * Adds schema element to the schema at the root DSE 1241 * 1242 * @param schemaElement 1243 * schema element to be added 1244 * @exception AccessRightsException 1245 * insufficient access 1246 * @exception SchemaElementAlreadyExistsException 1247 * if the element already exists 1248 * @exception UMSException 1249 * Fail to add schema element. 1250 * 1251 * @supported.api 1252 */ 1253 public void addSchema(java.security.Principal principal, 1254 LDAPSchemaElement schemaElement) throws AccessRightsException, 1255 SchemaElementAlreadyExistsException, UMSException { 1256 LDAPConnection conn = null; 1257 try { 1258 conn = getConnection(principal); 1259 // disable the checking of attribute syntax quoting and the 1260 // read on "" 1261 conn.setProperty("com.sun.identity.shared.ldap.schema.quoting", 1262 "standard"); 1263 schemaElement.add(conn, "cn=schema"); 1264 } catch (LDAPException e) { 1265 int errorCode = e.getLDAPResultCode(); 1266 releaseConnection(conn, errorCode); 1267 conn = null; 1268 debug.error("Exception in DataLayer.addSchema: ", e); 1269 switch (errorCode) { 1270 case LDAPException.ATTRIBUTE_OR_VALUE_EXISTS: 1271 throw new SchemaElementAlreadyExistsException(schemaElement 1272 .getName(), e); 1273 case LDAPException.INSUFFICIENT_ACCESS_RIGHTS: 1274 throw new AccessRightsException(schemaElement.getName(), e); 1275 default: 1276 throw new UMSException(schemaElement.getName(), e); 1277 } 1278 } finally { 1279 if (conn != null) { 1280 releaseConnection(conn); 1281 } 1282 } 1283 } 1284 1285 /** 1286 * Removes schema element from the schema 1287 * 1288 * @param schemaElement 1289 * schema element to be removed 1290 * @exception AccessRightsException 1291 * insufficient access 1292 * @exception UMSException 1293 * Fail to remove schema element. 1294 * 1295 * @supported.api 1296 */ 1297 public void removeSchema(java.security.Principal principal, 1298 LDAPSchemaElement schemaElement) throws AccessRightsException, 1299 UMSException { 1300 LDAPConnection conn = null; 1301 1302 try { 1303 conn = getConnection(principal); 1304 // disable the checking of attribute syntax quoting and the 1305 // read on "" 1306 conn.setProperty("com.sun.identity.shared.ldap.schema.quoting", 1307 "standard"); 1308 schemaElement.remove(conn, "cn=schema"); 1309 1310 } catch (LDAPException e) { 1311 int errorCode = e.getLDAPResultCode(); 1312 releaseConnection(conn, errorCode); 1313 conn = null; 1314 debug.error("Exception in DataLayer.removeSchema:", e); 1315 switch (errorCode) { 1316 case LDAPException.INSUFFICIENT_ACCESS_RIGHTS: 1317 throw new AccessRightsException(schemaElement.getName(), e); 1318 default: 1319 throw new UMSException(schemaElement.getName(), e); 1320 } 1321 } finally { 1322 if (conn != null) { 1323 releaseConnection(conn); 1324 } 1325 } 1326 } 1327 1328 private void initReplicaProperties() { 1329 String retries = SystemProperties 1330 .get("com.iplanet.am.replica.num.retries"); 1331 if (retries != null) { 1332 try { 1333 replicaRetryNum = Integer.parseInt(retries); 1334 if (replicaRetryNum < 0) { 1335 replicaRetryNum = 0; 1336 debug.warning("Invalid value for replica retry num, " + 1337 "set to 0"); 1338 } 1339 1340 } catch (NumberFormatException e) { 1341 debug.warning("Invalid value for replica retry num"); 1342 } 1343 } 1344 1345 String interval = SystemProperties 1346 .get("com.iplanet.am.replica.delay.between.retries"); 1347 if (interval != null) { 1348 try { 1349 replicaRetryInterval = Long.parseLong(interval); 1350 if (replicaRetryInterval < 0) { 1351 replicaRetryInterval = 0; 1352 debug.warning("Invalid value for replica interval, " + 1353 "set to 0"); 1354 } 1355 1356 } catch (NumberFormatException e) { 1357 debug.warning("Invalid value for replica interval"); 1358 } 1359 } 1360 } 1361 1362 public LDAPEntry readLDAPEntry(LDAPConnection ld, String dn, 1363 String[] attrnames) throws LDAPException { 1364 1365 LDAPException ldapEx = null; 1366 int retry = 0; 1367 int connRetry = 0; 1368 while (retry <= replicaRetryNum && connRetry <= connNumRetry) { 1369 if (debug.messageEnabled()) { 1370 debug.message("DataLayer.readLDAPEntry: connRetry: " 1371 + connRetry); 1372 debug.message("DataLayer.readLDAPEntry: retry: " + retry); 1373 } 1374 try { 1375 if (attrnames == null) { 1376 return ld.read(dn); 1377 } else { 1378 return ld.read(dn, attrnames); 1379 } 1380 } catch (LDAPException e) { 1381 int errorCode = e.getLDAPResultCode(); 1382 if (errorCode == LDAPException.NO_SUCH_OBJECT) { 1383 if (debug.messageEnabled()) { 1384 debug.message("Replica: entry not found: " + dn 1385 + " retry: " + retry); 1386 } 1387 if (retry == replicaRetryNum) { 1388 ldapEx = e; 1389 } else { 1390 try { 1391 Thread.sleep(replicaRetryInterval); 1392 } catch (Exception ex) { 1393 } 1394 } 1395 retry++; 1396 } else if (retryErrorCodes.contains("" + errorCode)) { 1397 if (connRetry == connNumRetry) { 1398 ldapEx = e; 1399 } else { 1400 try { 1401 Thread.sleep(connRetryInterval); 1402 } catch (Exception ex) { 1403 } 1404 } 1405 connRetry++; 1406 } else { 1407 throw e; 1408 } 1409 } 1410 } 1411 1412 throw ldapEx; 1413 } 1414 1415 public LDAPEntry readLDAPEntry(java.security.Principal principal, 1416 LDAPSearchRequest request) throws LDAPException { 1417 1418 LDAPException ldapEx = null; 1419 int retry = 0; 1420 int connRetry = 0; 1421 LDAPConnection ld = null; 1422 while (retry <= replicaRetryNum && connRetry <= connNumRetry) { 1423 if (debug.messageEnabled()) { 1424 debug.message("DataLayer.readLDAPEntry: connRetry: " 1425 + connRetry); 1426 debug.message("DataLayer.readLDAPEntry: retry: " + retry); 1427 } 1428 try { 1429 ld = getConnection(principal); 1430 return ld.read(request); 1431 } catch (LDAPException e) { 1432 int errorCode = e.getLDAPResultCode(); 1433 releaseConnection(ld, errorCode); 1434 ld = null; 1435 if (errorCode == LDAPException.NO_SUCH_OBJECT) { 1436 if (debug.messageEnabled()) { 1437 debug.message("Replica: entry not found: " + 1438 request.getBaseDN() + " retry: " + retry); 1439 } 1440 if (retry == replicaRetryNum) { 1441 ldapEx = e; 1442 } else { 1443 try { 1444 Thread.sleep(replicaRetryInterval); 1445 } catch (Exception ex) { 1446 } 1447 } 1448 retry++; 1449 } else if (retryErrorCodes.contains("" + errorCode)) { 1450 if (connRetry == connNumRetry) { 1451 ldapEx = e; 1452 } else { 1453 try { 1454 Thread.sleep(connRetryInterval); 1455 } catch (Exception ex) { 1456 } 1457 } 1458 connRetry++; 1459 } else { 1460 throw e; 1461 } 1462 } finally { 1463 if (ld != null) { 1464 releaseConnection(ld); 1465 } 1466 } 1467 } 1468 1469 throw ldapEx; 1470 } 1471 1472 1473 /** 1474 * Initialize the pool shared by all DataLayer object(s). 1475 */ 1476 private synchronized void initLdapPool() throws UMSException { 1477 // Don't do anything if pool is already initialized 1478 if (_ldapPool != null) 1479 return; 1480 1481 /* 1482 * Initialize the pool with minimum and maximum connections settings 1483 * retrieved from configuration 1484 */ 1485 ServerInstance svrCfg = null; 1486 String hostName = null; 1487 HashMap connOptions = new HashMap(); 1488 1489 try { 1490 DSConfigMgr dsCfg = DSConfigMgr.getDSConfigMgr(); 1491 hostName = dsCfg.getHostName("default"); 1492 1493 _trialConn = dsCfg.getNewProxyConnection(); 1494 1495 svrCfg = dsCfg.getServerInstance(LDAPUser.Type.AUTH_PROXY); 1496 } catch (LDAPServiceException ex) { 1497 debug.error("Error initializing connection pool " 1498 + ex.getMessage()); 1499 } 1500 1501 // Check if svrCfg was successfully obtained 1502 if ((svrCfg == null) || (_trialConn == null)) { 1503 debug.error("Error getting server config."); 1504 // throw exception 1505 String args[] = new String[1]; 1506 args[0] = (hostName == null) ? "default" : hostName; 1507 throw new UMSException(i18n.getString( 1508 IUMSConstants.NEW_INSTANCE_FAILED, args)); 1509 } 1510 1511 int poolMin = svrCfg.getMinConnections(); 1512 int poolMax = svrCfg.getMaxConnections(); 1513 int maxBackLog = svrCfg.getIntValue(LDAP_MAXBACKLOG, MAX_BACKLOG); 1514 m_releaseConnectionBeforeSearchCompletes = svrCfg.getBooleanValue( 1515 LDAP_RELEASECONNBEFORESEARCH, false); 1516 boolean referrals = svrCfg.getBooleanValue(LDAP_REFERRAL, true); 1517 String connDN = svrCfg.getAuthID(); 1518 String connPWD = svrCfg.getPasswd(); 1519 1520 if (debug.messageEnabled()) { 1521 debug.message("Creating ldap connection pool with :"); 1522 debug.message("poolMin : " + poolMin); 1523 debug.message("poolMax : " + poolMax); 1524 debug.message("maxBackLog : " + maxBackLog); 1525 } 1526 1527 try { 1528 // establish one good connection before the pool 1529 // _trialConn = new LDAPConnection(); 1530 1531 _trialConn.setOption(LDAPConnection.MAXBACKLOG, new Integer( 1532 maxBackLog)); 1533 _trialConn.setOption(LDAPConnection.REFERRALS, Boolean.valueOf( 1534 referrals)); 1535 1536 /* 1537 * Default rebind method is to provide the same authentication 1538 * in the rebind to the server being referred. 1539 */ 1540 LDAPBind defaultBinder = new LDAPBind() { 1541 public void bind(LDAPConnection ld) throws LDAPException { 1542 /* 1543 * There is possibly a bug in the ldapjdk that the passed in 1544 * ld is not carrying the original authentication dn and pwd 1545 * Hence, we have to kludge here using the one connection 1546 * that we know 1547 * about: the connection that we use to initialize the 1548 * connection 1549 * pool. 1550 * TODO: need to investigate 1551 */ 1552 String dn = _trialConn.getAuthenticationDN(); 1553 String pwd = _trialConn.getAuthenticationPassword(); 1554 String newhost = ld.getHost(); 1555 int newport = ld.getPort(); 1556 ld.connect(3, newhost, newport, dn, pwd); 1557 } 1558 }; 1559 _trialConn.setOption(LDAPConnection.BIND, defaultBinder); 1560 1561 // _trialConn.connect(3, m_host, m_port, m_proxyUser, 1562 // m_proxyPassword); 1563 1564 // remember the original search constraints 1565 _defaultSearchConstraints = _trialConn.getSearchConstraints(); 1566 1567 // Construct the pool by cloning the successful connection 1568 // Set the default options too for failover and fallback features. 1569 1570 connOptions.put("maxbacklog", new Integer(maxBackLog)); 1571 connOptions.put("referrals", Boolean.valueOf(referrals)); 1572 connOptions.put("searchconstraints", _defaultSearchConstraints); 1573 1574 ShutdownManager shutdownMan = com.sun.identity.common.ShutdownManager.getInstance(); 1575 1576 _ldapPool = new LDAPConnectionPool("DataLayer", poolMin, 1577 poolMax, hostName, 389, connDN, connPWD, _trialConn, 1578 connOptions); 1579 shutdownMan.addShutdownListener( 1580 new ShutdownListener() { 1581 public void shutdown() { 1582 if (_ldapPool != null) { 1583 _ldapPool.destroy(); 1584 } 1585 } 1586 } 1587 ); 1588 1589 } catch (LDAPException e) { 1590 debug.error("Exception in DataLayer.initLdapPool:", e); 1591 } 1592 } 1593 1594 public static int getConnNumRetry() { 1595 return connNumRetry; 1596 } 1597 1598 public static int getConnRetryInterval() { 1599 return connRetryInterval; 1600 } 1601 1602 public static HashSet getRetryErrorCodes() { 1603 return retryErrorCodes; 1604 } 1605 1606 private static void initializeEventService() { 1607 // Initialize event service. This is to make sure that EventService 1608 // thread is started. The other place where it is also tried to start 1609 // is: com.iplanet.am.sdk.ldap.AMEventManager which is 1610 // initialized in com.iplanet.am.sdk.ldap.DirectoryManager 1611 if (!EventService.isThreadStarted()) { 1612 // Use a separate thread to start the EventService thread. 1613 // This will prevent deadlocks associated in the system because 1614 // of EventService related dependencies. 1615 InitEventServiceThread th = new InitEventServiceThread(); 1616 Thread initEventServiceThread = new Thread(th, 1617 "InitEventServiceThread"); 1618 initEventServiceThread.setDaemon(true); 1619 initEventServiceThread.start(); 1620 } 1621 } 1622 1623 private static class InitEventServiceThread implements Runnable { 1624 public void run() { 1625 debug.message("InitEventServiceThread:initializeEventService() - " 1626 + "EventService thread getting initialized "); 1627 try { 1628 EventService es = EventService.getEventService(); 1629 if (!EventService.isThreadStarted()) { 1630 es.resetAllSearches(false); 1631 } 1632 } catch (Exception e) { 1633 // An Error occurred while intializing EventService 1634 debug.error("InitEventServiceThread:run() Unable to " 1635 + "start EventService!!", e); 1636 } 1637 } 1638 } 1639 1640 static private LDAPConnectionPool _ldapPool = null; 1641 1642 static private LDAPConnection _trialConn = null; 1643 1644 static private LDAPSearchConstraints _defaultSearchConstraints = null; 1645 1646 static private DataLayer m_instance = null; 1647 1648 private String m_host = null; 1649 1650 private int m_port; 1651 1652 private String m_proxyUser = ""; 1653 1654 private String m_proxyPassword = ""; 1655 1656 private boolean m_releaseConnectionBeforeSearchCompletes = false; 1657 1658 private static final String[] EMPTY_STRING_ARRAY = new String[0]; 1659 1660}
Copyright © 2010-2017, ForgeRock All Rights Reserved.