org.eclipse.rdf4j.federated.exception.ExceptionUtil Maven / Gradle / Ivy
Show all versions of rdf4j-tools-federation Show documentation
/*******************************************************************************
* Copyright (c) 2019 Eclipse RDF4J contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
package org.eclipse.rdf4j.federated.exception;
import java.lang.reflect.Constructor;
import java.util.concurrent.TimeoutException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.rdf4j.federated.endpoint.Endpoint;
import org.eclipse.rdf4j.query.QueryEvaluationException;
import org.eclipse.rdf4j.query.QueryInterruptedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Convenience functions to handle exceptions.
*
* @author Andreas Schwarte
*
*/
public class ExceptionUtil {
protected static final Logger log = LoggerFactory.getLogger(ExceptionUtil.class);
/**
* Regex pattern to identify http error codes from the title of the returned document:
*
*
* Matcher m = httpErrorPattern.matcher("[..] 503 Service Unavailable [..]");
* if (m.matches()) {
* System.out.println("HTTP Error: " + m.group(1);
* }
*
*/
protected static final Pattern httpErrorPattern = Pattern.compile(".*(.*) .*", Pattern.DOTALL);
/**
* Trace the exception source within the exceptions to identify the originating endpoint. The message of the
* provided exception is adapted to "@ endpoint.getId() - %orginalMessage".
*
*
* Note that in addition HTTP error codes are extracted from the title, if the exception resulted from an HTTP
* error, such as for instance "503 Service unavailable"
*
* @param endpoint the the endpoint
* @param ex the exception
* @param additionalInfo additional information that might be helpful, e.g. the subquery
*
* @return a modified exception with endpoint source
*/
public static QueryEvaluationException traceExceptionSource(Endpoint endpoint, Throwable ex,
String additionalInfo) {
if (ex instanceof InterruptedException) {
Thread.currentThread().interrupt();
}
String eID;
if (endpoint == null) {
log.warn("No endpoint found for connection, probably changed from different thread.");
eID = "unknown";
} else {
eID = endpoint.getId();
}
// check for http error code (heuristic)
String message = ex.getMessage();
message = message == null ? "n/a" : message;
Matcher m = httpErrorPattern.matcher(message);
if (m.matches()) {
log.debug("HTTP error detected for endpoint " + eID + ":\n" + message);
message = "HTTP Error: " + m.group(1);
} else {
log.trace("No http error found");
}
if (!(ex instanceof QueryEvaluationException)) {
message += ". Original exception type: " + ex.getClass().getName();
}
QueryEvaluationException res = new QueryEvaluationException(
"@ " + eID + " - " + message + ". " + additionalInfo, ex.getCause());
res.setStackTrace(ex.getStackTrace());
return res;
}
/**
* Repair the connection and then trace the exception source.
*
* @param endpoint
* @param ex
* @return the exception
*/
public static QueryEvaluationException traceExceptionSourceAndRepair(Endpoint endpoint, Throwable ex,
String additionalInfo) {
return traceExceptionSource(endpoint, ex, additionalInfo);
}
/**
* Return the exception in a convenient representation, i.e. '%msg% (%CLASS%): %ex.getMessage()%'
*
* @param msg
* @param ex
*
* @return the exception in a convenient representation
*/
public static String getExceptionString(String msg, Throwable ex) {
return msg + " " + ex.getClass().getSimpleName() + ": " + ex.getMessage();
}
/**
* If possible change the message text of the specified exception. This is only possible if the provided exception
* has a public constructor with String and Throwable as argument. The new message is set to 'msgPrefix.
* ex.getMessage()', all other exception elements remain the same.
*
* @param
* @param msgPrefix
* @param ex
* @param exClazz
*
* @return the updated exception
*/
public static E changeExceptionMessage(String msgPrefix, E ex, Class exClazz) {
Constructor constructor;
try {
// try to find the constructor 'public Exception(String, Throwable)'
constructor = exClazz.getConstructor(new Class>[] { String.class, Throwable.class });
} catch (SecurityException e) {
log.warn("Cannot change the message of exception class " + exClazz.getCanonicalName()
+ " due to SecurityException: " + e.getMessage());
return ex;
} catch (NoSuchMethodException e) {
log.warn("Cannot change the message of exception class " + exClazz.getCanonicalName()
+ ": Constructor not found.");
return ex;
}
E newEx;
try {
newEx = constructor.newInstance(new Object[] { msgPrefix + "." + ex.getMessage(), ex.getCause() });
} catch (Exception e) {
log.warn("Cannot change the message of exception class " + exClazz.getCanonicalName() + " due to "
+ e.getClass().getSimpleName() + ": " + e.getMessage());
return ex;
}
newEx.setStackTrace(ex.getStackTrace());
return newEx;
}
/**
* Converts the {@link Throwable} to an {@link Exception}. If it is already of type exception, it is returned as is.
* Otherwise, we create a new {@link QueryEvaluationException}, and attach the stack trace and a meaningful message.
*
* @param t
* @return the {@link Exception}
*/
public static Exception toException(Throwable t) {
Exception e;
if (t instanceof QueryEvaluationException) {
return (QueryEvaluationException) t;
}
if (t instanceof TimeoutException) {
e = new QueryInterruptedException("Query evaluation has run into a timeout.", t);
} else if (t instanceof Exception) {
e = (Exception) t;
} else {
e = new QueryEvaluationException("" + t.getMessage() + ". Original type: " + t.getClass());
e.setStackTrace(t.getStackTrace());
}
return e;
}
/**
* Converts the given Throwable to a {@link QueryEvaluationException}. If it is already of type
* {@link QueryEvaluationException} no transformation is done, otherwise the throwable is wrapped.
*
* @param t
* @return the {@link QueryEvaluationException}
*/
public static QueryEvaluationException toQueryEvaluationException(Throwable t) {
Exception res = toException(t);
if (res instanceof QueryEvaluationException) {
return (QueryEvaluationException) res;
}
if (t instanceof InterruptedException) {
Thread.currentThread().interrupt();
}
return new QueryEvaluationException(res);
}
}