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 }