001 /*
002 * Copyright (c) 2011, Cloudera, Inc. All Rights Reserved.
003 *
004 * Cloudera, Inc. licenses this file to you under the Apache License,
005 * Version 2.0 (the "License"). You may not use this file except in
006 * compliance with the License. You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * This software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
011 * CONDITIONS OF ANY KIND, either express or implied. See the License for
012 * the specific language governing permissions and limitations under the
013 * License.
014 */
015 package com.cloudera.lib.lang;
016
017 import com.cloudera.lib.util.Check;
018
019 import java.text.MessageFormat;
020
021 /**
022 * Generic exception that requires error codes and uses the a message
023 * template from the error code.
024 */
025 public class XException extends Exception {
026
027 /**
028 * Interface to define error codes.
029 */
030 public static interface ERROR {
031
032 /**
033 * Returns the template for the error.
034 *
035 * @return the template for the error, the template must be in JDK
036 * <code>MessageFormat</code> syntax (using {#} positional parameters).
037 */
038 public String getTemplate();
039
040 }
041
042 private ERROR error;
043
044 /**
045 * Private constructor used by the public constructors.
046 * @param error error code.
047 * @param message error message.
048 * @param cause exception cause if any.
049 */
050 private XException(ERROR error, String message, Throwable cause) {
051 super(message, cause);
052 this.error = error;
053 }
054
055 /**
056 * Creates an XException using another XException as cause.
057 * <p/>
058 * The error code and error message are extracted from the cause.
059 * @param cause exception cause.
060 */
061 public XException(XException cause) {
062 this(cause.getError(), cause.getMessage(), cause);
063 }
064
065 /**
066 * Creates an XException using the specified error code. The exception
067 * message is resolved using the error code template and the passed
068 * parameters.
069 *
070 * @param error error code for the XException.
071 * @param params parameters to use when creating the error message
072 * with the error code template.
073 */
074 @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"})
075 public XException(ERROR error, Object... params) {
076 this(Check.notNull(error, "error"), format(error, params), getCause(params));
077 }
078
079 /**
080 * Returns the error code of the exception.
081 *
082 * @return the error code of the exception.
083 */
084 public ERROR getError() {
085 return error;
086 }
087
088 /**
089 * Creates a message using a error message template and arguments.
090 * <p/>
091 * The template must be in JDK <code>MessageFormat</code> syntax
092 * (using {#} positional parameters).
093 *
094 * @param error error code, to get the template from.
095 * @param args arguments to use for creating the message.
096 * @return the resolved error message.
097 */
098 private static String format(ERROR error, Object... args) {
099 String template = error.getTemplate();
100 if (template == null) {
101 StringBuilder sb = new StringBuilder();
102 for (int i = 0; i < args.length; i++) {
103 sb.append(" {").append(i).append("}");
104 }
105 template = sb.deleteCharAt(0).toString();
106 }
107 return error + ": " + MessageFormat.format(error.getTemplate(), args);
108 }
109
110 /**
111 * Returns the last parameter if it is an instance of <code>Throwable</code>
112 * returns it else it returns NULL.
113 *
114 * @param params parameters to look for a cause.
115 * @return the last parameter if it is an instance of <code>Throwable</code>
116 * returns it else it returns NULL.
117 */
118 private static Throwable getCause(Object... params) {
119 Throwable throwable = null;
120 if (params != null && params.length > 0 && params[params.length - 1] instanceof Throwable) {
121 throwable = (Throwable) params[params.length - 1];
122 }
123 return throwable;
124 }
125
126 }