/*******************************************************************************
* Copyright (c) 2006-2010 eBay Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*******************************************************************************/
package org.ebayopensource.turmeric.runtime.common.exceptions;
import java.io.InputStream;
import java.net.SocketException;
import java.net.URL;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.ebayopensource.turmeric.common.v1.types.CommonErrorData;
import org.ebayopensource.turmeric.common.v1.types.ErrorParameter;
import org.ebayopensource.turmeric.runtime.common.errors.ErrorDataProvider.ErrorDataKey;
import org.ebayopensource.turmeric.runtime.common.utils.Preconditions;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* The utility class for exceptions.
*
*/
public class ExceptionUtils {
private static final String ERRORDATA_URL_PATTERN = "META-INF/errorlibrary/";
/**
* Find the nested exception inside Throwable t and of type clz.
*
* @param t the throwable to be searched by
* @param clz the class to be searched for
* @return the result, null if not found
*/
public static Throwable getCauseOfType(Throwable t, Class<?> clz) {
Throwable cause = t.getCause();
while (cause != null) {
if (clz.isAssignableFrom(cause.getClass())) {
return cause;
}
t = cause;
cause = t.getCause();
}
return null;
}
/**
*
* @param t the exception
* @return true if Throwable t is caused by client timeout exception.
*/
public static SocketException getClientTimeoutException(Throwable t) {
Throwable cause = ExceptionUtils.getRootCause(t);
if (!(cause instanceof SocketException)) {
return null;
}
SocketException socketExcption = (SocketException)cause;
String message = cause.getMessage();
if (null == message) {
return null;
}
if (message.contains("Software caused connection abort")) {
return socketExcption;
}
if (message.contains("Connection reset by peer")) {
return socketExcption;
}
return null;
}
/**
*
* @param t the exception
* @return Returns the non-null root cause of the given Throwable t.
*/
public static Throwable getRootCause(Throwable t) {
Throwable cause = t.getCause();
while (cause != null) {
t = cause;
cause = t.getCause();
}
return t;
}
/**
* Used by Property based EL.
*
* @param domainName domain name of the error data
* @return the URL of the ErrorData.xml resource
*/
public static URL getErrordataXMLURL(String domainName){
String errorDataUrl = ERRORDATA_URL_PATTERN + domainName + "/ErrorData.xml";
URL fileUrl = Thread.currentThread().getContextClassLoader().getResource(errorDataUrl);
return fileUrl;
}
/**
*
* Used by Property based EL. Though we have Util methods for DOM parsing,
* we need to make ErrorLibrary independent and hence we need to have this Util method
*
* @param inputStream the input stream to be parsed
* @return the packageName for the domain represented by the XML Stream
*/
public static String getPackageNameFromXML(InputStream inputStream) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
Document result = null;
String packagename = null;
try {
DocumentBuilder builder = factory.newDocumentBuilder();
result = builder.parse(inputStream);
Element docElement = result.getDocumentElement();
if (docElement != null)
packagename = docElement.getAttribute("packageName");
} catch (Exception e) {
}
return packagename;
}
/**
* Load a class by given class name and class loader.
*
* @param <T> The type of class to be loaded
* @param className the class name
* @param targetType the class type name, for error reporting purpose only
* @param cl the class loader
* @return the class
*/
public static <T> Class<T> loadClass(String className, Class<T> targetType, ClassLoader cl)
{
String targetTypeName;
if (targetType != null) {
targetTypeName = targetType.getName();
} else {
targetTypeName = "(unspecified assignment type)";
}
Class clazz = null;
try {
clazz = Class.forName(className, true, cl);
} catch (Exception exception) {
Object[] arguments = new Object[] {targetTypeName, className};
throwServiceRuntimeException(ErrorLibraryBaseErrors.el_errorcollection_not_available, arguments, exception);
}
@SuppressWarnings("unchecked")
Class<T> result = clazz;
return result;
}
/**
* Creates an instance of the given class.
* @param <T> the class type of the instance
* @param className the class name
* @param targetType the type of the class, for error reporting purpose only.
* @param cl the class loader
* @return the created instance
*/
public static <T> T createInstance(String className, Class<T> targetType, ClassLoader cl)
{
Class<T> clazz = loadClass(className, targetType, cl);
Object result = null;
try {
result = clazz.newInstance();
} catch (Exception exception) {
Object[] arguments = new Object[] {clazz.getName()};
throwServiceRuntimeException(ErrorLibraryBaseErrors.el_inst_exception, arguments, exception);
}
// type cast cannot be done, but we've checked isAssignableFrom
@SuppressWarnings("unchecked")
T result2 = (T)result;
return result2;
}
/**
* Throws a service runtime exception of given error data and arguments.
* The arguments will be inserted into the error data at the predefined places.
*
* @param errorData the error data
* @param arguments the arguments
*/
public static void throwServiceRuntimeException(CommonErrorData errorData, Object[] arguments){
throwServiceRuntimeException(errorData, arguments, null);
}
/**
* Throws a service runtime exception of given error data and arguments.
* The arguments will be inserted into the error data at the predefined places.
*
* @param errorData the error data
* @param arguments the arguments
* @param cause the root cause
*/
public static void throwServiceRuntimeException(CommonErrorData errorData, Object[] arguments, Throwable cause){
CommonErrorData commonErrorData = cloneErrorData(null, errorData, arguments);
ErrorLibraryBaseErrors.buildBootstrapErrorMessage(commonErrorData, arguments);
throw new ServiceRuntimeException(commonErrorData, cause);
}
/**
* Clone the error data.
*
* @param key the key of the error data, the cloned error name will be from the key
* @param errorData the error data to be copied
* @param params parameters to be set to the cloned the error data
* @return the cloned error data
*/
public static CommonErrorData cloneErrorData(ErrorDataKey key, CommonErrorData errorData,
Object[] params){
return cloneErrorData(key, errorData, params, null);
}
/**
* Clone the error data.
*
* @param key if not null, containing error name to be set to the cloned error data
* @param errorData the error data to be copied
* @param params parameters to be set to the cloned the error data
* @param errorName if key is null, this will be the error name of the cloned error data
* @return the cloned error data
*/
public static CommonErrorData cloneErrorData(ErrorDataKey key, CommonErrorData errorData, Object[] params,
String errorName){
Preconditions.checkNotNull(errorData);
CommonErrorData commonErrorData = new CommonErrorData();
commonErrorData.setErrorName(errorData.getErrorName());
if(commonErrorData.getErrorName() == null)
commonErrorData.setErrorName(errorName);
if(commonErrorData.getErrorName() == null && key != null)
commonErrorData.setErrorName(key.getErrorName());
commonErrorData.setCategory(errorData.getCategory());
commonErrorData.setDomain(errorData.getDomain());
commonErrorData.setErrorId(errorData.getErrorId());
commonErrorData.setExceptionId(errorData.getExceptionId());
commonErrorData.setSeverity(errorData.getSeverity());
commonErrorData.setSubdomain(errorData.getSubdomain());
commonErrorData.setOrganization(errorData.getOrganization());
setErrorParameters(commonErrorData, params);
if(errorData.getMessage() != null)
commonErrorData.setMessage(errorData.getMessage());
return commonErrorData;
}
private static void setErrorParameters(CommonErrorData commonErrorData, Object[] args ) {
if ( args != null && args.length > 0 ) {
for ( int i = 0; i < args.length; i++ ) {
if(args[i] != null){
ErrorParameter errorParameter = new ErrorParameter();
errorParameter.setName( "Param" + (i+1) );
errorParameter.setValue( args[i].toString() );
commonErrorData.getParameter().add( errorParameter );
}
}
}
}
}