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