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