remove last method from stacktrace before throwing exception - java

I have a utility method for timing and logging various queries all over the project.
The problem is, when looking at crashlytics now all unrelated crashes are joined together into one crash-instance.
Can I catch all exceptions on the utility method, and throw them after removing that method from the stack?
The environment is Android (Java)
UPDATE:
based on #Dhananjay's answer below, here's my code:
public static Cursor get(...) {
try {
// my utility code
} catch (RuntimeException e) {
throw cleanException(e);
}
}
private static RuntimeException cleanException(RuntimeException e) {
try {
StackTraceElement[] stackTrace = e.getStackTrace();
StackTraceElement[] subTrace = new StackTraceElement[stackTrace.length - 1];
System.arraycopy(stackTrace, 1, subTrace, 0, subTrace.length);
e.setStackTrace(subTrace);
return e;
} catch (Throwable ignored) {
return e;
}
}

This approach might solve your problem: Set the stacktrace of the exception in the utility logging method to exclude the utility method itself, and then throw the exception, here is a working example, you can modify it to eliminate any StackTraceElement you want to:
package test;
public class TestMain {
public static void main(String args[]) throws Exception {
try {
apiCall();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void apiCall() throws Exception {
logAndThrow();
}
public static void logAndThrow() throws Exception {
Exception e = new Exception();
StackTraceElement[] cleanedUpStackTrace = new StackTraceElement[e.getStackTrace().length -1];
// Eliminate this mehod i.e. logAndThrow's stack trace entry (i.e. the first one) in cleanedUpStackTrace
System.arraycopy(e.getStackTrace(), 1, cleanedUpStackTrace, 0, cleanedUpStackTrace.length);
for(StackTraceElement ste : cleanedUpStackTrace) {
System.out.println(ste.getMethodName());
}
e.setStackTrace(cleanedUpStackTrace);
throw e;
}
}
Here is the output of this program, the logAndThrow method is not present in stack trace now:
apiCall
main
java.lang.Exception
at test.TestMain.apiCall(TestMain.java:33)
at test.TestMain.main(TestMain.java:25)

Related

Using reflection to identify if the error thrown matches expected error

I need to write a simple code tester program, but I got stuck comparing the given error class with the test expected class. I am supposed to use reflection in this exercise.
I have my code testing class:
public class TestRunner {
private String result = "";
public void runTests(List<String> testClassNames) {
for (String testClassName : testClassNames) {
Class<?> clazz;
try {
clazz = Class.forName(testClassName);
} catch (ClassNotFoundException e) {
throw new IllegalStateException("No such class.");
}
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if (method.getAnnotation(MyTest.class) != null) {
if (testClassName.equals("reflection.tester.ExampleTests1")) {
result += method.getName() + "() - ";
ExampleTests1 instance = new ExampleTests1();
try {
// if null, result = OK
method.invoke(instance);
result += "OK\n";
} catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
// if error is caught result = FAILED
result += "FAILED\n";
}
} else {
// the second class. should only return "OK" if the error is implemented from the exception class
result += method.getName() + "() - ";
ExampleTests2 instance = new ExampleTests2();
try {
method.invoke(instance);
result += "FAILED\n";
} catch (RuntimeException e) {
Throwable original = e.getCause();
Object expected = method.getReturnType();
if (original.getClass().isAssignableFrom(expected.getClass())) {
result += "OK\n";
} else {
result += "FAILED\n";
}
} catch (InvocationTargetException | IllegalAccessException e) {
result += "ERROR\n";
}
}
}
}
}
}
}
Also have two test classes. In the first one there is only one rule, if the test won't throw an exception the test should pass, and it is working. The second class is more complicated. If the thrown error class is implemented or same to the expected error class then the test should pass and OK should be added to the result. Currently my code won't catch RunTimeException at all and moves to the last catch block. How can I fix this?
I will also add the test class for more information.
public class ExampleTests2 {
#MyTest(expected = RuntimeException.class)
public void test3() {
throw new IllegalStateException();
}
#MyTest(expected = IllegalStateException.class)
public void test4() {
throw new RuntimeException();
}
#MyTest(expected = IllegalStateException.class)
public void test5() {
throw new IllegalStateException();
}
#MyTest(expected = IllegalStateException.class)
public void test6() {
}
public void helperMethod() {
}
}
test3() and test5() should pass, test4() and test6() should fail, helperMethod() won't be checked because I only need to use the tests with #MyTest annotation.
JUnit has an assertThrows method that checks that an Exception is thrown. It has a method signature of
static <T extends Throwable> assertThrows​(Class<T> expectedType, Executable executable){}
Here's the documentation: https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/Assertions.html#assertThrows(java.lang.Class,org.junit.jupiter.api.function.Executable)
and here's how JUnit implements it:
https://github.com/junit-team/junit5/blob/main/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrows.java

Java try catch block not catching an exception

I was making a command to clear messages using JDA.
I made this code
public class Main {
public static JDA jda;
public static void main(String[] args) throws LoginException {
jda = JDABuilder.createDefault("OTM0ODA4NTY1ODYzMDM5MDA3.Ye1eUg.JExQxPx8UUli8YQfN7TfdbzLHqI").build();
jda.addEventListener(new CommandExecutor());
} }
public class CommandExecutor extends ListenerAdapter {
public static final String prefix = "!-";
public void onMessageReceived(MessageReceivedEvent event) {
String[] args = event.getMessage().getContentRaw().split(" ");
if (args[0].equalsIgnoreCase(prefix + "clear"))
new Clear(event, args);
} }
public class Clear {
public Clear(MessageReceivedEvent event, String[] args) {
try {
int numberOfMessages = Integer.parseInt(args[1]);
List<Message> messages = event.getChannel().getHistory().retrievePast(numberOfMessages + 1).complete();
event.getChannel().purgeMessages(messages);
event.getChannel().sendMessage("Messages have been deleted!").queue(m -> m.delete().queueAfter(5, TimeUnit.SECONDS));
} catch (Exception e) {
e.printStackTrace();
} } }
The code inside the try block will clear the messages if a valid argument is passed. In case an invalid argument is passed, like a string, it should go to the catch block and print the details of the exception. However, this does not happen, and the error gets generated.
Here is the error generated
java.lang.NumberFormatException: For input string: "de"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
at java.base/java.lang.Integer.parseInt(Integer.java:668)
at java.base/java.lang.Integer.parseInt(Integer.java:786)
at JDA.TelevisionBot.Commands.Clear.<init>(Clear.java:24)
at JDA.TelevisionBot.CommandExecutor.onMessageReceived(CommandExecutor.java:26)
at net.dv8tion.jda.api.hooks.ListenerAdapter.onEvent(ListenerAdapter.java:359)
at net.dv8tion.jda.api.hooks.InterfacedEventManager.handle(InterfacedEventManager.java:96)
at net.dv8tion.jda.internal.hooks.EventManagerProxy.handleInternally(EventManagerProxy.java:88)
at net.dv8tion.jda.internal.hooks.EventManagerProxy.handle(EventManagerProxy.java:70)
at net.dv8tion.jda.internal.JDAImpl.handleEvent(JDAImpl.java:164)
at net.dv8tion.jda.internal.handle.MessageCreateHandler.handleInternally(MessageCreateHandler.java:121)
at net.dv8tion.jda.internal.handle.SocketHandler.handle(SocketHandler.java:36)
at net.dv8tion.jda.internal.requests.WebSocketClient.onDispatch(WebSocketClient.java:952)
at net.dv8tion.jda.internal.requests.WebSocketClient.onEvent(WebSocketClient.java:839)
at net.dv8tion.jda.internal.requests.WebSocketClient.handleEvent(WebSocketClient.java:817)
at net.dv8tion.jda.internal.requests.WebSocketClient.onBinaryMessage(WebSocketClient.java:991)
at com.neovisionaries.ws.client.ListenerManager.callOnBinaryMessage(ListenerManager.java:385)
at com.neovisionaries.ws.client.ReadingThread.callOnBinaryMessage(ReadingThread.java:276)
at com.neovisionaries.ws.client.ReadingThread.handleBinaryFrame(ReadingThread.java:996)
at com.neovisionaries.ws.client.ReadingThread.handleFrame(ReadingThread.java:755)
at com.neovisionaries.ws.client.ReadingThread.main(ReadingThread.java:108)
at com.neovisionaries.ws.client.ReadingThread.runMain(ReadingThread.java:64)
at com.neovisionaries.ws.client.WebSocketThread.run(WebSocketThread.java:45)
Thanks in advance
it is normal that you get everything you indicated since e.printStackTrace(),It's a method on Exception instances that prints the stack trace of the instance to System.err.
It's a very simple, but very useful tool for diagnosing an exceptions. It tells you what happened and where in the code this happened.
try {
throw new NullPointerException();
}
catch (NullPointerException e) {
System.out.println(e);
}
try {
throw new IOException();
}
catch (IOException e) {
e.printStackTrace();
}
System.exit(0);
Calling println(e):
java.lang.NullPointerException
Calling e.printStackTrace():
java.io.IOException
at package.Test.main(Test.java:74)

chain exception in catch block

I have the following java code :
public void someMethod(){
try{
// some code which generates Exception
}catch(Exception ex1) {
try{
// The code inside this method can also throw some Exception
myRollBackMethodForUndoingSomeChanges();
}catch(Exception ex2){
// I want to add inside `ex2` the history of `ex1` too
// Surely , I cannot set cause of `ex2` as `ex1` as `ex2`
// can be caused by it's own reasons.
// I dont want `ex1` details to be lost if I just throw `ex2` from my method
}
}
}
How to do it ?
EDIT : Actually this happens in my service layer and I have controller advice for logging. Hence I don't want to add 2 loggers here.
You can add ex1 to the supressed exceptions in ex2 via the method addSuppressed before rethrowing it.
Quick code example:
public static void main(final String[] args) {
try {
throw new IllegalArgumentException("Illegal Argument 1!");
} catch (final RuntimeException ex1) {
try {
throw new IllegalStateException("Illegal State 2!");
} catch (final RuntimeException ex2) {
ex2.addSuppressed(ex1);
throw ex2;
}
}
}
will produce the exception output:
Exception in thread "main" java.lang.IllegalStateException: Illegal State 2!
at package.main(Main.java:26)
Suppressed: java.lang.IllegalArgumentException: Illegal Argument 1!
at package.main(Main.java:20)

I have trouble using the try-catch method for a exception in java

I do not know how to successfully try and catch the exception. As you can see I already started the try-catch statement but do not know how to finish it. I get the error " tractorException.java:83: error: unreported exception tractorException; must be caught or declared to be thrown
setVehicleID(0); "
import java.io.*;
import java.util.*;
import javax.swing.*;
public class tractorException extends Exception {
protected int VehicleID;
public int setVehicleID(int VehicleID) throws tractorException {
if (VehicleID <= 0 || VehicleID > 100000) {
throw new tractorException();
} else {
this.VehicleID = VehicleID;
return this.VehicleID;
}
}
public int getVehicleID() {
return this.VehicleID;
}
tractorException() {
setVehicleID(0);
}
public static void main (String[] args) {
try {
throw new Exception("Something went wrong!!");
} catch (Exception e) {
}
Change your main method to:
public static void main (String[] args) {
try {
throw new tractorException(); // infinite loop ensues
} catch (Exception e) {
// this catch doesn't matter
}
}
The infinite loop occurs because tractorException's constructor calls setVehicleID(0), which in turn calls throw new tractorException(), which, as you guessed, calls setVehicleID(0) ... to infinity and beyond.
A function that throws exception must be caught or declared to be thrown. The issue you have with your code is in the line setVehicleID(0); as stated in the error log you posted.
Since setVehicleID() method throws exception, any time you call this function, it must be caught or re-throw. To fix your error, you need to surround this call with try catch:
tractorException()
{
try{
setVehicleID(0);
}
catch( tractorException e ) {
// Do something with error
}
}
try enter this
you cannot call Directly setVehicleID method because it is risky Method
tractorException() {
try{
setVehicleID(0);
}catch(Exception e){
}
}

Why is the main method not covered?

main method:
public static void main(String[] args) throws Exception
{
if (args.length != EXPECTED_NUMBER_OF_ARGUMENTS)
{
System.err.println("Usage - java XFRCompiler ConfigXML PackageXML XFR");
}
String configXML = args[0];
String packageXML = args[1];
String xfr = args[2];
AutoConfigCompiler compiler = new AutoConfigCompiler();
compiler.setConfigDocument(loadDocument(configXML));
compiler.setPackageInfoDoc(loadDocument(packageXML));
// compiler.setVisiblityDoc(loadDocument("VisibilityFilter.xml"));
compiler.compileModel(xfr);
}
private static Document loadDocument(String fileName) throws Exception
{
TXDOMParser parser = (TXDOMParser) ParserFactory.makeParser(TXDOMParser.class.getName());
InputSource source = new InputSource(new FileInputStream(fileName));
parser.parse(source);
return parser.getDocument();
}
testcase:
#Test
public void testCompileModel() throws Exception
{
// construct parameters
URL configFile = Thread.currentThread().getContextClassLoader().getResource("Ford_2008_Mustang_Config.xml");
URL packageFile = Thread.currentThread().getContextClassLoader().getResource("Ford_2008_Mustang_Package.xml");
File tmpFile = new File("Ford_2008_Mustang_tmp.xfr");
if(!tmpFile.exists()) {
tmpFile.createNewFile();
}
String[] args = new String[]{configFile.getPath(),packageFile.getPath(),tmpFile.getPath()};
try {
// test main method
XFRCompiler.main(args);
} catch (Exception e) {
assertTrue(true);
}
try {
// test args length is less than 3
XFRCompiler.main(new String[]{"",""});
} catch (Exception e) {
//ignore
}
tmpFile.delete();
}
Coverage outputs displayed as the lines from String configXML = args[0]; in main method
are not covered.
assertTrue(true); is a pointless no-op
Remove the try/catch around the call to XFRCompiler.main(args);, since all it does is swallow excpetions and make debugging harder; most likely you will then see an exception that tells you where the problem is.
There should be a call to fail() after the call to XFRCompiler.main(new String[]{"",""}); since you expect it to throw an exception
Put the two calls in separate test methods.
I'm worried about all those assertTrue(true). If there can't be an exception, then the assert is not necessary. If there is an unexpected exception, then this code will swallow it and you will get the behavior you see right now.
Then, if you expect an exception, you should code like this:
try {
... code that will throw an exception ...
fail("No exception was thrown");
} catch (SpecficTypeOfException e) {
assertEquals("message", e.getMessage());
}
That way, wrong types of exception and the exception message will be checked.
PS: Don't post questions with "urgent". We already help as fast as we can.

Categories