| /** |
| * 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.sqoop.json; |
| |
| import org.apache.sqoop.classification.InterfaceAudience; |
| import org.apache.sqoop.classification.InterfaceStability; |
| import org.apache.sqoop.common.SqoopException; |
| import org.apache.sqoop.utils.ClassUtils; |
| import org.json.simple.JSONArray; |
| import org.json.simple.JSONObject; |
| |
| import java.util.LinkedList; |
| import java.util.List; |
| |
| /** |
| * Transfer throwable instance as a throwable bean. |
| */ |
| @InterfaceAudience.Private |
| @InterfaceStability.Unstable |
| public class ThrowableBean implements JsonBean { |
| |
| public static final String MESSAGE = "message"; |
| public static final String STACK_TRACE = "stack-trace"; |
| public static final String CLASS = "class"; |
| public static final String METHOD = "method"; |
| public static final String FILE = "file"; |
| public static final String LINE = "line"; |
| public static final String CAUSE = "cause"; |
| public static final String ERROR_CODE = "error-code"; |
| public static final String ERROR_CODE_CLASS = "error-code-class"; |
| |
| private Throwable throwable; |
| |
| // For "extract" |
| public ThrowableBean(Throwable ex) { |
| throwable = ex; |
| } |
| |
| // For "restore" |
| public ThrowableBean() { |
| } |
| |
| public Throwable getThrowable() { |
| return throwable; |
| } |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public JSONObject extract(boolean skipSensitive) { |
| JSONObject result = new JSONObject(); |
| |
| result.put(MESSAGE, throwable.getMessage()); |
| result.put(CLASS, throwable.getClass().getName()); |
| |
| if(throwable instanceof SqoopException ) { |
| SqoopException sqoopException = (SqoopException) throwable; |
| result.put(ERROR_CODE, sqoopException.getErrorCode().getCode()); |
| result.put(ERROR_CODE_CLASS, sqoopException.getErrorCode().getClass().getName()); |
| // Override message with the original message |
| result.put(MESSAGE, sqoopException.getOriginalMessage()); |
| } |
| |
| JSONArray st = new JSONArray(); |
| for(StackTraceElement element : throwable.getStackTrace()) { |
| JSONObject obj = new JSONObject(); |
| |
| obj.put(CLASS, element.getClassName()); |
| obj.put(METHOD, element.getMethodName()); |
| obj.put(FILE, element.getFileName()); |
| obj.put(LINE, element.getLineNumber()); |
| |
| st.add(obj); |
| } |
| |
| result.put(STACK_TRACE, st); |
| |
| Throwable cause = throwable.getCause(); |
| if(cause != null) { |
| ThrowableBean causeBean = new ThrowableBean(cause); |
| result.put(CAUSE, causeBean.extract(skipSensitive)); |
| } |
| |
| return result; |
| } |
| |
| @Override |
| public void restore(JSONObject jsonObject) { |
| String exceptionClass = (String) jsonObject.get(CLASS); |
| String message = (String) jsonObject.get(MESSAGE); |
| if(message == null) { |
| message = ""; |
| } |
| |
| // Special handling for SqoopException as we need to transfer ERROR_CODE from the other side |
| if(jsonObject.containsKey(ERROR_CODE_CLASS)) { |
| Class e = ClassUtils.loadClass((String) jsonObject.get(ERROR_CODE_CLASS)); |
| |
| // Only if the error code class is known to this JVM, let's instantiate the real SqoopException |
| if( e != null) { |
| String errorCode = (String) jsonObject.get(ERROR_CODE); |
| Enum enumValue = Enum.valueOf(e, errorCode); |
| throwable = (Throwable) ClassUtils.instantiate(exceptionClass, enumValue, message); |
| } |
| } |
| |
| // Let's try to instantiate same class that was originally on remote side. |
| if(throwable == null) { |
| throwable = (Throwable) ClassUtils.instantiate(exceptionClass, message); |
| } |
| |
| // Fallback to generic Throwable in case that this particular exception is not known |
| // to this JVM (for example during server-client exchange). |
| if(throwable == null) { |
| throwable = new Throwable(message); |
| } |
| |
| List<StackTraceElement> st = new LinkedList<StackTraceElement>(); |
| for(Object object : (JSONArray)jsonObject.get(STACK_TRACE)) { |
| JSONObject json = (JSONObject)object; |
| StackTraceElement element = new StackTraceElement( |
| (String)json.get(CLASS), |
| (String)json.get(METHOD), |
| (String)json.get(FILE), |
| ((Long)json.get(LINE)).intValue() |
| ); |
| st.add(element); |
| } |
| |
| throwable.setStackTrace(st.toArray(new StackTraceElement[]{})); |
| |
| Object cause = jsonObject.get(CAUSE); |
| if(cause != null) { |
| JSONObject causeJson = (JSONObject)cause; |
| |
| ThrowableBean causeBean = new ThrowableBean(); |
| causeBean.restore(causeJson); |
| |
| throwable.initCause(causeBean.getThrowable()); |
| } |
| } |
| } |