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
019package org.apache.hadoop.lib.lang;
020
021import org.apache.hadoop.lib.util.Check;
022
023import java.text.MessageFormat;
024
025/**
026 * Generic exception that requires error codes and uses the a message
027 * template from the error code.
028 */
029public class XException extends Exception {
030
031  /**
032   * Interface to define error codes.
033   */
034  public static interface ERROR {
035
036    /**
037     * Returns the template for the error.
038     *
039     * @return the template for the error, the template must be in JDK
040     *         <code>MessageFormat</code> syntax (using {#} positional parameters).
041     */
042    public String getTemplate();
043
044  }
045
046  private ERROR error;
047
048  /**
049   * Private constructor used by the public constructors.
050   *
051   * @param error error code.
052   * @param message error message.
053   * @param cause exception cause if any.
054   */
055  private XException(ERROR error, String message, Throwable cause) {
056    super(message, cause);
057    this.error = error;
058  }
059
060  /**
061   * Creates an XException using another XException as cause.
062   * <p/>
063   * The error code and error message are extracted from the cause.
064   *
065   * @param cause exception cause.
066   */
067  public XException(XException cause) {
068    this(cause.getError(), cause.getMessage(), cause);
069  }
070
071  /**
072   * Creates an XException using the specified error code. The exception
073   * message is resolved using the error code template and the passed
074   * parameters.
075   *
076   * @param error error code for the XException.
077   * @param params parameters to use when creating the error message
078   * with the error code template.
079   */
080  @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"})
081  public XException(ERROR error, Object... params) {
082    this(Check.notNull(error, "error"), format(error, params), getCause(params));
083  }
084
085  /**
086   * Returns the error code of the exception.
087   *
088   * @return the error code of the exception.
089   */
090  public ERROR getError() {
091    return error;
092  }
093
094  /**
095   * Creates a message using a error message template and arguments.
096   * <p/>
097   * The template must be in JDK <code>MessageFormat</code> syntax
098   * (using {#} positional parameters).
099   *
100   * @param error error code, to get the template from.
101   * @param args arguments to use for creating the message.
102   *
103   * @return the resolved error message.
104   */
105  private static String format(ERROR error, Object... args) {
106    String template = error.getTemplate();
107    if (template == null) {
108      StringBuilder sb = new StringBuilder();
109      for (int i = 0; i < args.length; i++) {
110        sb.append(" {").append(i).append("}");
111      }
112      template = sb.deleteCharAt(0).toString();
113    }
114    return error + ": " + MessageFormat.format(template, args);
115  }
116
117  /**
118   * Returns the last parameter if it is an instance of <code>Throwable</code>
119   * returns it else it returns NULL.
120   *
121   * @param params parameters to look for a cause.
122   *
123   * @return the last parameter if it is an instance of <code>Throwable</code>
124   *         returns it else it returns NULL.
125   */
126  private static Throwable getCause(Object... params) {
127    Throwable throwable = null;
128    if (params != null && params.length > 0 && params[params.length - 1] instanceof Throwable) {
129      throwable = (Throwable) params[params.length - 1];
130    }
131    return throwable;
132  }
133
134}