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: SMSException.java,v 1.7 2009/01/28 05:35:03 ww203982 Exp $ 026 * 027 */ 028 029/* 030 * Portions Copyrighted [2011] [ForgeRock AS] 031 */ 032package com.sun.identity.sm; 033 034import com.sun.identity.authentication.internal.InvalidAuthContextException; 035import com.sun.identity.shared.debug.Debug; 036import com.sun.identity.shared.locale.AMResourceBundleCache; 037import com.sun.identity.shared.locale.L10NMessage; 038import com.sun.identity.shared.locale.Locale; 039import com.iplanet.am.sdk.AMException; 040import com.iplanet.services.ldap.LDAPServiceException; 041import com.iplanet.services.ldap.event.EventException; 042import com.iplanet.services.util.XMLException; 043import com.iplanet.sso.SSOException; 044import com.iplanet.ums.IUMSConstants; 045import java.io.PrintWriter; 046import java.io.StringWriter; 047import java.text.MessageFormat; 048import java.util.ResourceBundle; 049import com.sun.identity.shared.ldap.LDAPException; 050 051/** 052 * The exception class whose instance is thrown if there is any error during the 053 * operation of objects of the <code>com.sun.identity.sms</code> package. This 054 * class maps the exception that occurred at a lower level to a high level 055 * error. Using the exception status code <code>getExceptionCode()</code> the 056 * errors are categorized as a <code>ABORT</code>, <code>RETRY</code>, 057 * <code>CONFIG_PROBLEM</code> or <code>LDAP_OP_FAILED</code> (typically a 058 * bug). 059 * 060 * @supported.all.api 061 */ 062public class SMSException extends Exception implements L10NMessage { 063 064 // Static variable 065 transient AMResourceBundleCache amCache = AMResourceBundleCache 066 .getInstance(); 067 068 transient Debug debug = Debug.getInstance(IUMSConstants.UMS_DEBUG); 069 070 // Instance variables 071 private int exceptionStatus = STATUS_NONE; 072 073 private Throwable rootCause; 074 075 private String message; 076 077 private String bundleName = IUMSConstants.UMS_BUNDLE_NAME; 078 079 private String errorCode; 080 081 private Object[] args; 082 083 /** 084 * Default constructor for <code> SMSException </code> 085 */ 086 public SMSException() { 087 super(); 088 exceptionStatus = STATUS_NONE; 089 } 090 091 /** 092 * @param status 093 * The exception status code. 094 * @param errorCode 095 * Key to resource bundle. 096 */ 097 public SMSException(int status, String errorCode) { 098 super(); 099 exceptionStatus = status; 100 this.errorCode = errorCode; 101 this.message = getL10NMessage(java.util.Locale.ENGLISH); 102 } 103 104 /** 105 * @param status 106 * The Exception status code. 107 * @param exMessage 108 * The message provided by the object which is throwing the 109 * exception 110 * @param errorCode 111 * Key to resource bundle. 112 */ 113 public SMSException(int status, String exMessage, String errorCode) { 114 exceptionStatus = status; 115 this.errorCode = errorCode; 116 this.message = exMessage + ": " + 117 getL10NMessage(java.util.Locale.ENGLISH); 118 } 119 120 /** 121 * @param msg 122 * The message provided by the object which is throwing the 123 * exception 124 */ 125 public SMSException(String msg) { 126 exceptionStatus = STATUS_NONE; 127 this.message = msg; 128 } 129 130 /** 131 * @param msg 132 * The message provided by the object which is throwing the 133 * exception 134 * @param errorCode 135 * Key to resource bundle. 136 */ 137 public SMSException(String msg, String errorCode) { 138 exceptionStatus = STATUS_NONE; 139 this.errorCode = errorCode; 140 this.message = msg + ": " + getL10NMessage(java.util.Locale.ENGLISH); 141 } 142 143 /** 144 * Constructs an <code>SMSException</code>. 145 * 146 * @param t 147 * The <code>Throwable</code> object provided by the object 148 * which is throwing the exception 149 * @param errorCode 150 * Key to resource bundle. 151 */ 152 public SMSException(Throwable t, String errorCode) { 153 // super(t); (can be used with JDK 1.4 and higher) 154 rootCause = t; 155 this.errorCode = errorCode; 156 this.message = getL10NMessage(java.util.Locale.ENGLISH); 157 exceptionMapper(); 158 } 159 160 /** 161 * Constructs an <code>SMSException</code>. 162 * 163 * @param message 164 * exception message. 165 * @param t 166 * The <code>Throwable</code> object provided by the object 167 * which is throwing the exception. 168 * @param errorCode 169 * Key to resource bundle. 170 */ 171 public SMSException(String message, Throwable t, String errorCode) { 172 // super(message, t); (can be used with JDK 1.4 and higher) 173 rootCause = t; 174 this.errorCode = errorCode; 175 this.message = message + ": " + 176 getL10NMessage(java.util.Locale.ENGLISH); 177 exceptionMapper(); 178 } 179 180 /** 181 * Constructs an <code>SMSException</code>. 182 * 183 * @param rbName 184 * Resource bundle Name to be used for getting localized error 185 * message. 186 * @param message 187 * exception message. 188 * @param t 189 * The <code>Throwable</code> object provided by the object 190 * which is throwing the exception. 191 * @param errorCode 192 * Key to resource bundle. 193 */ 194 public SMSException(String rbName, String message, Throwable t, 195 String errorCode) { 196 // super(message, t); (can be used with JDK 1.4 and higher) 197 rootCause = t; 198 this.errorCode = errorCode; 199 this.bundleName = rbName; 200 this.message = message + ": " + 201 getL10NMessage(java.util.Locale.ENGLISH); 202 if (rootCause != null && !(rootCause instanceof AMException)) { 203 exceptionMapper(); 204 } 205 } 206 207 /** 208 * This constructor is used to pass the localized error message At this 209 * level, the locale of the caller is not known and it is not possible to 210 * throw localized error message at this level. Instead this constructor 211 * provides Resource Bundle name and error code for correctly locating the 212 * error message. The default <code>getMessage()</code> will always return 213 * English messages only. This is in consistent with current JRE. 214 * 215 * @param rbName 216 * Resource bundle Name to be used for getting localized error 217 * message. 218 * @param errorCode 219 * Key to resource bundle. You can use <code>ResourceBundle rb = 220 * ResourceBunde.getBundle(rbName,locale); 221 * String localizedStr = rb.getString(errorCode)</code>. 222 * @param args 223 * arguments to message. If it is not present pass the as null. 224 */ 225 public SMSException(String rbName, String errorCode, Object[] args) { 226 exceptionStatus = STATUS_NONE; 227 this.bundleName = rbName; 228 this.errorCode = errorCode; 229 this.args = args; 230 this.message = getL10NMessage(java.util.Locale.ENGLISH); 231 } 232 233 /** 234 * Returns a localized error message 235 * 236 * @param locale 237 * Uses the locale object to create the appropriate localized 238 * error message 239 * @return localized error message. 240 * @see #SMSException(String, String, Object[]) 241 */ 242 public String getL10NMessage(java.util.Locale locale) { 243 String result = errorCode; 244 if (bundleName != null && locale != null) { 245 ResourceBundle bundle = amCache.getResBundle(bundleName, locale); 246 String mid = Locale.getString(bundle, errorCode, debug); 247 if (args == null || args.length == 0) { 248 result = mid; 249 } else { 250 result = MessageFormat.format(mid, args); 251 } 252 } 253 return result; 254 } 255 256 /** 257 * Returns <code>ResourceBundle</code> Name associated with this error 258 * message. 259 * 260 * @return <code>ResourceBundle</code> name associated with this error 261 * message. 262 * @see #SMSException(String, String, Object[]) 263 */ 264 public String getResourceBundleName() { 265 return bundleName; 266 } 267 268 /** 269 * Returns error code associated with this error message. 270 * 271 * @return Error code associated with this error message. 272 * @see #SMSException(String, String, Object[]) 273 */ 274 public String getErrorCode() { 275 return errorCode; 276 } 277 278 /** 279 * Returns arguments for formatting this error message. 280 * 281 * @return arguments for formatting this error message. You need to use 282 * <code>MessageFormat</code> class to format the message It can 283 * be null. 284 * @see #SMSException(String, String, Object[]) 285 */ 286 public Object[] getMessageArgs() { 287 return args; 288 } 289 290 /** 291 * Returns the status code for this exception. 292 * 293 * @return Integer representing the exception status code 294 */ 295 public int getExceptionCode() { 296 return exceptionStatus; 297 } 298 299 /** 300 * The this package can set the exception code. 301 * 302 * @param status 303 * The exception status code. 304 */ 305 void setExceptionCode(int status) { 306 exceptionStatus = status; 307 } 308 309 public String toString() { 310 StringBuilder buf = new StringBuilder(); 311 if (exceptionStatus != -1) { 312 buf.append("SMSException Exception Code:"); 313 buf.append(exceptionStatus); 314 buf.append('\n'); 315 } 316 String msg = message; 317 if (msg != null && msg.length() > 0) { 318 buf.append("Message:"); 319 buf.append(msg); 320 buf.append("\n"); 321 } 322 323 if (rootCause != null) { 324 buf.append("--------------------------------------------------\n"); 325 buf.append("The lower level exception message\n"); 326 buf.append(rootCause.getMessage()); 327 buf.append('\n'); 328 buf.append("The lower level exception:\n"); 329 StringWriter sw = new StringWriter(100); 330 rootCause.printStackTrace(new PrintWriter(sw)); 331 buf.append(sw.toString()); 332 buf.append('\n'); 333 } 334 return buf.toString(); 335 } 336 337 /** 338 * Returns the error message of this exception. 339 * 340 * @return String representing the error message 341 */ 342 public String getMessage() { 343 return message; 344 } 345 346 private String getString(String msgID) { 347 errorCode = msgID; 348 ResourceBundle bundle = null; 349 if (bundleName != null) { 350 bundle = amCache.getResBundle(bundleName, java.util.Locale.ENGLISH); 351 } 352 return (Locale.getString(bundle, msgID, debug)); 353 } 354 355 private void exceptionMapper() { 356 if (rootCause == null) { 357 return; 358 } 359 if (rootCause instanceof LDAPException) { 360 message = mapLDAPException(); 361 } else if (rootCause instanceof LDAPServiceException) { 362 // do nothing 363 } else if (rootCause instanceof EventException) { 364 exceptionStatus = STATUS_ABORT; 365 message = getString(IUMSConstants.SMS_EVENT_NOTIFICATION_FAILED); 366 } else if (rootCause instanceof XMLException) { 367 exceptionStatus = STATUS_ABORT; 368 message = getString(IUMSConstants.SMS_XML_PARSER_EXCEPTION); 369 } else if (rootCause instanceof InvalidAuthContextException) { 370 message = getString(IUMSConstants.SMS_AUTHENTICATION_ERROR); 371 exceptionStatus = STATUS_ABORT; 372 } else if (rootCause instanceof SSOException) { 373 message = getString(IUMSConstants.SMS_AUTHENTICATION_ERROR); 374 exceptionStatus = STATUS_ABORT; 375 } else { 376 message = getString(IUMSConstants.SMS_UNKNOWN_EXCEPTION_OCCURRED); 377 exceptionStatus = STATUS_UNKNOWN_EXCEPTION; 378 } 379 } 380 381 private String mapLDAPException() { 382 int resultCode = ((LDAPException) rootCause).getLDAPResultCode(); 383 384 String message = null; 385 386 switch (resultCode) { 387 // //////////////////////////////// 388 // Errors that need to be handled 389 // //////////////////////////////// 390 391 // Helpless errors 392 // All errors are helpless situations 393 // but some are more helpless than the others. 394 // These errors are either problems in connection 395 // or configuration. So, some can be retired and 396 // some are already busted. 397 case LDAPException.SERVER_DOWN: 398 case LDAPException.OTHER: 399 message = getString(IUMSConstants.SMS_SERVER_DOWN); 400 exceptionStatus = STATUS_RETRY; 401 break; 402 case LDAPException.LDAP_NOT_SUPPORTED: 403 message = getString(IUMSConstants.SMS_LDAP_NOT_SUPPORTED); 404 exceptionStatus = STATUS_ABORT; 405 break; 406 case LDAPException.BUSY: 407 message = getString(IUMSConstants.SMS_LDAP_SERVER_BUSY); 408 exceptionStatus = STATUS_RETRY; 409 break; 410 411 case LDAPException.INVALID_CREDENTIALS: 412 message = getString("INVALID_CREDENTIALS"); 413 exceptionStatus = STATUS_CONFIG_PROBLEM; 414 break; 415 416 // Application must show exactly what is happening 417 case LDAPException.NO_SUCH_OBJECT: 418 message = getString(IUMSConstants.SMS_NO_SUCH_OBJECT); 419 exceptionStatus = STATUS_LDAP_OP_FAILED; 420 break; 421 422 case LDAPException.INSUFFICIENT_ACCESS_RIGHTS: 423 message = getString(IUMSConstants.SMS_INSUFFICIENT_ACCESS_RIGHTS); 424 exceptionStatus = STATUS_NO_PERMISSION; 425 break; 426 427 case LDAPException.ADMIN_LIMIT_EXCEEDED: 428 message = getString(IUMSConstants.SMS_ADMIN_LIMIT_EXCEEDED); 429 exceptionStatus = STATUS_ABORT; 430 break; 431 432 case LDAPException.TIME_LIMIT_EXCEEDED: 433 message = getString(IUMSConstants.SMS_TIME_LIMIT_EXCEEDED); 434 exceptionStatus = STATUS_ABORT; 435 break; 436 437 case LDAPException.REFERRAL: 438 message = getString(IUMSConstants.SMS_LDAP_REFERRAL_EXCEPTION); 439 exceptionStatus = STATUS_CONFIG_PROBLEM; 440 break; 441 442 // We screwed up with something 443 case LDAPException.OBJECT_CLASS_VIOLATION: 444 case LDAPException.NAMING_VIOLATION: 445 case LDAPException.CONSTRAINT_VIOLATION: 446 case LDAPException.INVALID_DN_SYNTAX: 447 case LDAPException.ENTRY_ALREADY_EXISTS: 448 case LDAPException.ATTRIBUTE_OR_VALUE_EXISTS: 449 case LDAPException.PROTOCOL_ERROR: 450 case LDAPException.UNDEFINED_ATTRIBUTE_TYPE: 451 SMSEntry.debug.error(rootCause.toString()); 452 message = getString(IUMSConstants.SMS_LDAP_OPERATION_FAILED); 453 exceptionStatus = STATUS_LDAP_OP_FAILED; 454 break; 455 456 // Exception code that means logical operation. 457 case LDAPException.COMPARE_TRUE: 458 case LDAPException.COMPARE_FALSE: 459 case LDAPException.LDAP_PARTIAL_RESULTS: 460 exceptionStatus = STATUS_QUO_ANTE; 461 break; 462 463 default: 464 message = getString(IUMSConstants.SMS_UNEXPECTED_LDAP_EXCEPTION); 465 exceptionStatus = STATUS_UNKNOWN_EXCEPTION; 466 } 467 return message; 468 } 469 470 // Error codes 471 /** No status code is set */ 472 public static int STATUS_NONE = -1; 473 474 /** Retry connection to data store */ 475 public static int STATUS_RETRY = 0; 476 477 /** Repeated retry to data store failed */ 478 public static int STATUS_REPEATEDLY_FAILED = 0; 479 480 /** Status to abort operation */ 481 public static int STATUS_ABORT = 1; 482 483 /** 484 * If root LDAP cause is <code>LDAP_PARTIAL_RESULTS </code> then this 485 * status is set 486 */ 487 public static int STATUS_QUO_ANTE = 2; 488 489 /** 490 * If root LDAP cause is an LDAP exception with one of the following error 491 * codes then this status is set. 492 * <p> 493 * 494 * <PRE> 495 * 496 * NO_SUCH_OBJECT OBJECT_CLASS_VIOLATION NAMING_VIOLATION 497 * CONSTRAINT_VIOLATION INVALID_DN_SYNTAX ENTRY_ALREADY_EXISTS 498 * ATTRIBUTE_OR_VALUE_EXISTS PROTOCOL_ERROR UNDEFINED_ATTRIBUTE_TYPE 499 * 500 * </PRE> 501 */ 502 503 public static int STATUS_LDAP_OP_FAILED = 3; 504 505 /** 506 * If the root LDAP exception is <code> INVALID_CREDENTIALS </code> or 507 * <code> REFERRAL </code> then this status is set 508 */ 509 public static int STATUS_CONFIG_PROBLEM = 4; 510 511 /** If root cause is other than any of those listed in other status codes */ 512 public static int STATUS_UNKNOWN_EXCEPTION = 5; 513 514 /** If the root LDAP cause is <code> INSUFFICIENT_ACCESS_RIGHTS </code> */ 515 public static int STATUS_NO_PERMISSION = 8; 516 517 /** the operation is not allowed. */ 518 public static int STATUS_NOT_ALLOW = 9; 519 520}