| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.solr.common; |
| |
| import java.io.CharArrayWriter; |
| import java.io.PrintWriter; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| import org.apache.solr.common.util.NamedList; |
| import org.slf4j.Logger; |
| import org.slf4j.MDC; |
| |
| /** |
| * |
| */ |
| public class SolrException extends RuntimeException { |
| |
| public static final String ROOT_ERROR_CLASS = "root-error-class"; |
| public static final String ERROR_CLASS = "error-class"; |
| final private Map<String, String> mdcContext; |
| |
| /** |
| * This list of valid HTTP Status error codes that Solr may return in |
| * the case of a "Server Side" error. |
| * |
| * @since solr 1.2 |
| */ |
| public enum ErrorCode { |
| BAD_REQUEST( 400 ), |
| UNAUTHORIZED( 401 ), |
| FORBIDDEN( 403 ), |
| NOT_FOUND( 404 ), |
| CONFLICT( 409 ), |
| UNSUPPORTED_MEDIA_TYPE( 415 ), |
| TOO_MANY_REQUESTS(429), |
| SERVER_ERROR( 500 ), |
| SERVICE_UNAVAILABLE( 503 ), |
| INVALID_STATE( 510 ), |
| UNKNOWN(0); |
| public final int code; |
| |
| private ErrorCode( int c ) |
| { |
| code = c; |
| } |
| public static ErrorCode getErrorCode(int c){ |
| for (ErrorCode err : values()) { |
| if(err.code == c) return err; |
| } |
| return UNKNOWN; |
| } |
| }; |
| |
| public SolrException(ErrorCode code, String msg) { |
| super(msg); |
| this.code = code.code; |
| this.mdcContext = MDC.getCopyOfContextMap(); |
| } |
| public SolrException(ErrorCode code, String msg, Throwable th) { |
| super(msg, th); |
| this.code = code.code; |
| this.mdcContext = MDC.getCopyOfContextMap(); |
| } |
| |
| public SolrException(ErrorCode code, Throwable th) { |
| super(th); |
| this.code = code.code; |
| this.mdcContext = MDC.getCopyOfContextMap(); |
| } |
| |
| /** |
| * Constructor that can set arbitrary http status code. Not for |
| * use in Solr, but may be used by clients in subclasses to capture |
| * errors returned by the servlet container or other HTTP proxies. |
| */ |
| protected SolrException(int code, String msg, Throwable th) { |
| super(msg, th); |
| this.code = code; |
| this.mdcContext = MDC.getCopyOfContextMap(); |
| } |
| |
| int code=0; |
| protected NamedList<String> metadata; |
| |
| /** |
| * The HTTP Status code associated with this Exception. For SolrExceptions |
| * thrown by Solr "Server Side", this should valid {@link ErrorCode}, |
| * however client side exceptions may contain an arbitrary error code based |
| * on the behavior of the Servlet Container hosting Solr, or any HTTP |
| * Proxies that may exist between the client and the server. |
| * |
| * @return The HTTP Status code associated with this Exception |
| */ |
| public int code() { return code; } |
| |
| public void setMetadata(NamedList<String> metadata) { |
| this.metadata = metadata; |
| } |
| |
| public NamedList<String> getMetadata() { |
| return metadata; |
| } |
| |
| public String getMetadata(String key) { |
| return (metadata != null && key != null) ? metadata.get(key) : null; |
| } |
| |
| public void setMetadata(String key, String value) { |
| if (key == null || value == null) |
| throw new IllegalArgumentException("Exception metadata cannot be null!"); |
| |
| if (metadata == null) |
| metadata = new NamedList<String>(); |
| metadata.add(key, value); |
| } |
| |
| public String getThrowable() { |
| return getMetadata(ERROR_CLASS); |
| } |
| |
| public String getRootThrowable() { |
| return getMetadata(ROOT_ERROR_CLASS); |
| } |
| |
| public void log(Logger log) { log(log,this); } |
| public static void log(Logger log, Throwable e) { |
| String stackTrace = toStr(e); |
| String ignore = doIgnore(e, stackTrace); |
| if (ignore != null) { |
| log.info(ignore); |
| return; |
| } |
| log.error(stackTrace); |
| |
| } |
| |
| public static void log(Logger log, String msg, Throwable e) { |
| String stackTrace = msg + ':' + toStr(e); |
| String ignore = doIgnore(e, stackTrace); |
| if (ignore != null) { |
| log.info(ignore); |
| return; |
| } |
| log.error(stackTrace); |
| } |
| |
| public static void log(Logger log, String msg) { |
| String ignore = doIgnore(null, msg); |
| if (ignore != null) { |
| log.info(ignore); |
| return; |
| } |
| log.error(msg); |
| } |
| |
| // public String toString() { return toStr(this); } // oops, inf loop |
| @Override |
| public String toString() { return super.toString(); } |
| |
| public static String toStr(Throwable e) { |
| CharArrayWriter cw = new CharArrayWriter(); |
| PrintWriter pw = new PrintWriter(cw); |
| e.printStackTrace(pw); |
| pw.flush(); |
| return cw.toString(); |
| |
| /** This doesn't work for some reason!!!!! |
| StringWriter sw = new StringWriter(); |
| PrintWriter pw = new PrintWriter(sw); |
| e.printStackTrace(pw); |
| pw.flush(); |
| System.out.println("The STRING:" + sw.toString()); |
| return sw.toString(); |
| **/ |
| } |
| |
| |
| /** |
| * For test code - do not log exceptions that match any of these regular expressions. |
| * A {@link java.util.concurrent.CopyOnWriteArraySet is recommended}. |
| */ |
| public static Set<String> ignorePatterns; |
| |
| /** Returns null if this exception does not match any ignore patterns, or a message string to use if it does. */ |
| public static String doIgnore(Throwable t, String m) { |
| Set<String> ignorePatterns = SolrException.ignorePatterns; // guard against races, albeit unlikely |
| if (ignorePatterns == null || m == null) return null; |
| if (t != null && t instanceof AssertionError) return null; |
| |
| for (String regex : ignorePatterns) { |
| Pattern pattern = Pattern.compile(regex); // TODO why do we compile late; why not up-front? |
| Matcher matcher = pattern.matcher(m); |
| |
| if (matcher.find()) return "Ignoring exception matching " + regex; |
| } |
| |
| return null; |
| } |
| |
| public static Throwable getRootCause(Throwable t) { |
| while (true) { |
| Throwable cause = t.getCause(); |
| if (cause!=null) { |
| t = cause; |
| } else { |
| break; |
| } |
| } |
| return t; |
| } |
| |
| @SuppressWarnings({"unchecked"}) |
| public void logInfoWithMdc(Logger logger, String msg) { |
| Map<String, String> previousMdcContext = MDC.getCopyOfContextMap(); |
| MDC.setContextMap(mdcContext); |
| try { |
| logger.info(msg); |
| } finally{ |
| MDC.setContextMap(previousMdcContext); |
| } |
| } |
| |
| public void logDebugWithMdc(Logger logger, String msg) { |
| Map<String, String> previousMdcContext = MDC.getCopyOfContextMap(); |
| MDC.setContextMap(mdcContext); |
| try { |
| logger.debug(msg); |
| } finally{ |
| MDC.setContextMap(previousMdcContext); |
| } |
| } |
| |
| public void logWarnWithMdc(Logger logger, String msg) { |
| Map<String, String> previousMdcContext = MDC.getCopyOfContextMap(); |
| MDC.setContextMap(mdcContext); |
| try { |
| logger.warn(msg); |
| } finally{ |
| MDC.setContextMap(previousMdcContext); |
| } |
| } |
| |
| } |