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