I hope I can explain this correctly. I am using the Javolution library to do reading and writing of an XML config file. I'm using XMLStreamReader/Writer. During reading, I'm looking to read a tag and store its attributes in a LinkedHashMap. However, I'm having an exception being thrown, that to me looks to make no sense due to when it's thrown and what's currently going on in the code.
Using the Eclipse debugger, the exception is being thrown when an attribute's key and value is being added to my map.
public class Element {
private HashMap<String, String> attributes = new LinkedHashMap<String, String>();
...
public void setAttribute(String key, String value) {
...
attributes.put(key, value);
}
}
Straight after the key and value are added, this catches an exception from Javolution:
javolution.xml.stream.XMLStreamException: Local name cannot be null
Neither key or value are null. When they are being added to the map, I cannot step into the code further to see where the exception is being thrown, there is no stack trace, no file/line number shown anywhere to explain where or even how the exception is thrown.
From a quick google search of older implementation of Javolution, I can see that this particular exception is only thrown using a few methods of the XMLStreamWriterImpl type. I've set breakpoints at each use I have of these methods, but the debugger doesn't catch them being used until much later in the code (and my localName variable is initialised at declaration).
Would anyone have any advice for how I could determine why this exception is being thrown?
Stack Trace:
Java HotSpot(TM) 64-Bit Server VM[localhost:3999]
Thread [main] (Suspended)
XMLImplMain$Element.setAttribute(String, String) line: 827
XMLImplMain.translate(Element) line: 133
XMLImplMain.translate(Element) line: 140
XMLImplMain.translate(Element) line: 140
XMLImplMain.loadXML(String) line: 118
Bootstrap.main(String[]) line: 32
Thread [EventWriter] (Running)
It's possible that this Javolution library is not correctly compiled, without the argument -g when compiling via javac perhaps and this will make the output jar lack of debug information. You can find the corresponding source code of the jar you currently working on, and recompile it with sufficient arguments to make it debugable by yourself.
Or, there is a workaround which is way more complicated but no customizing compilation is needed, writing a BTrace script and catch the initialization of all the XMLStreamException and then print out the stacktrace, like this:
package com.sun.btrace.samples;
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
#BTrace
public class OnThrow {
#OnMethod(clazz = "javolution.xml.stream.XMLStreamException", method = "<init>", location = #Location(Kind.RETURN))
public static void endMethod(#Self Exception self) {
jstack();
}
}
and there is similar example here. You can dig a little deeper here.
Related
I have a little Java application that implements a RESTful API using Micronaut 2.0.0. Under the hood, it uses Redisson 3.13.1 to go to Redis. Redisson, in turn, uses Netty (4.1.49).
The application works fine in a 'classic' java (on HotSpot, both Java 8 and 11).
I'm trying to build a native image out of this application using GraalVM.
The command is approximately like this:
native-image --no-server --no-fallback -H:+TraceClassInitialization -H:+PrintClassInitialization --report-unsupported-elements-at-runtime --initialize-at-build-time=reactor.core.publisher.Flux,reactor.core.publisher.Mono -H:ConfigurationFileDirectories=target/config -cp target/app-1.0.0-SNAPSHOT.jar com.app.AppApplication target/app
Here is what I get:
Error: Unsupported features in 4 methods
Detailed message:
Error: No instances of java.net.Inet4Address are allowed in the image heap as this class should be initialized at image runtime. Object has been initialized without the native-image initialization instrumentation and the stack trace can't be tracked.
Trace: Object was reached by
reading field io.netty.channel.socket.InternetProtocolFamily.localHost of
constant io.netty.channel.socket.InternetProtocolFamily#593f1f62 reached by
scanning method io.netty.resolver.dns.DnsNameResolver.preferredAddressType(DnsNameResolver.java:481)
Call path from entry point to io.netty.resolver.dns.DnsNameResolver.preferredAddressType(ResolvedAddressTypes):
at io.netty.resolver.dns.DnsNameResolver.preferredAddressType(DnsNameResolver.java:478)
at io.netty.resolver.dns.DnsNameResolver.<init>(DnsNameResolver.java:436)
at io.netty.resolver.dns.DnsNameResolverBuilder.build(DnsNameResolverBuilder.java:473)
at io.netty.resolver.dns.DnsAddressResolverGroup.newNameResolver(DnsAddressResolverGroup.java:111)
at io.netty.resolver.dns.DnsAddressResolverGroup.newResolver(DnsAddressResolverGroup.java:91)
at io.netty.resolver.dns.DnsAddressResolverGroup.newResolver(DnsAddressResolverGroup.java:76)
at io.netty.resolver.AddressResolverGroup.getResolver(AddressResolverGroup.java:70)
at org.redisson.cluster.ClusterConnectionManager$1.run(ClusterConnectionManager.java:251)
at com.oracle.svm.core.jdk.RuntimeSupport.executeHooks(RuntimeSupport.java:125)
at com.oracle.svm.core.jdk.RuntimeSupport.executeStartupHooks(RuntimeSupport.java:75)
at com.oracle.svm.core.JavaMainWrapper.runCore(JavaMainWrapper.java:141)
at com.oracle.svm.core.JavaMainWrapper.run(JavaMainWrapper.java:184)
at com.oracle.svm.core.code.IsolateEnterStub.JavaMainWrapper_run_5087f5482cc9a6abc971913ece43acb471d2631b(generated:0)
That's just a part of the output, it also produces similar reports on other 3 errors.
I'm still struggling to understand the issue, but I suppose that, as java.net.InetAddress has native methods in it, neither it nor its subclass java.net.Inet4Address can be initialized at build time. This means that an instance of Inet4Address cannot be visible for a code that is initialized at build time (at initialization stage, in Java terms). And the native image builder found a way that kinda reaches a point where such an object is visible. It even shows the trace, but the thing is that ClusterConnectionManager$1 is a Runnable that is only submitted to an Executor at runtime (waaaay after the static initialization).
How do you debug such situations? Namely:
How do you find the culprit?
How do you fix it when the culprit is found?
PS. If I add --initialize-at-run-time=java.net.InetAddress, it fails differently:
Error: The class java.net.InetAddress has already been initialized; it is too late
to register java.net.InetAddress for build-time initialization (from the command
line). java.net.InetAddress has been initialized without the native-image
initialization instrumentation and the stack trace can't be tracked. Try avoiding
this conflict by avoiding to initialize the class that caused initialization of
java.net.InetAddress or by not marking java.net.InetAddress for build-time
initialization.
Java reports itself as build 25.252-b09-jvmci-20.1-b02, mixed mode.
PPS. I found this No instances of ... are allowed in the image heap as this class should be initialized at image runtime and it seems that the Quarkus issue was fixed. But I still do not understand how to fix the issue at hand. Any help would be appreciated.
TLDR; there is a small section with a summary at the end of the answer.
A bit of theory
In Java, every class must be initialized before usage. Initialization means executing static field initializers
and static initialization blocks. In a standard JVM (like HotSpot), this happens at runtime, of course.
But with a native image, you have two alternatives. A class may still be initialized at runtime, or its initialization
may be carried out at build time. The latter has an obvious benefit of avoiding this work at native image startup,
which makes the image start faster. But for some classes it does not make sense to initialize them at build time.
Such an example could be a class that makes some decisions in its initialization (create an instance of this or that
class, for example) based on environment (env variables, config files, etc).
There are some restrictions in regard to choosing between build/run time initialization alternatives:
If a class is initialized at build time, all of its superclasses must be initialized at build time
If a class is initialized at run time, all of its subclasses must be initialized at runtime
Some classes must always be initialized at runtime (see below)
An instance of a class that is initialized at runtime cannot exist in image heap (this means that no
build-time initialized class or its instance can (directly or indirectly) reference such a runtime-initialized
class instance)
InetAddress issue
Since version 19.3.0, native-image tool requires that java.net.InetAddress class is always initialized at runtime.
This (by restriction 2) means that its subclasses, java.net.Inet4Address and java.net.Inet6Address must also be
initialized at runtime, which in turn (by restriction 4) means that you cannot have any InetAddress referenced
by a build-time initialized class.
All the build failures we encounter here are caused by this same problem: either Inet4Address or Inet6Address
in image heap.
But why are Netty-related classes tried to be initialized at build time?
It turns out that netty-codec-http contains the following
Args = --initialize-at-build-time=io.netty \
in its native-image.properties below META-INF, and micronaut has netty-codec-http as a dependency, so all
io.netty classes are by default initialized at build time (as native-image tool respects such
native-image.properties files).
A model project
Here https://github.com/rpuch/netty-InetAddress-native-image-diagnosing is a project that models the problem and which
I further use to show how to solve the problem. Its main() method follows:
public static void main(String[] args) throws Exception {
NioEventLoopGroup group = new NioEventLoopGroup(1, new DefaultThreadFactory("netty"));
DnsAddressResolverGroup resolverGroup = new DnsAddressResolverGroup(NioDatagramChannel.class,
DnsServerAddressStreamProviders.platformDefault());
AddressResolver<InetSocketAddress> resolver = resolverGroup.getResolver(group.next());
System.out.println(resolver);
resolver.close();
group.shutdownGracefully().get();
}
It causes the same effects (regarding Netty) that the following code does:
Config config = new Config();
config.useSingleServer().setAddress(redisUri);
config.useSingleServer().setPassword(redisPassword);
return Redisson.createReactive(config);
This project also has --initialize-at-build-time=io.netty in its build script to emulate micronaut-based project
behavior.
So it is a useful substitute for the original project that brought this question to light.
GraalVM version
I'm using version 20.2.0 here (the most recent released version as of the moment of writing).
Diagnosing and fixing
1
Build fails with the following error:
Error: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException: No instances of java.net.Inet4Address are allowed in the image heap as this class should be initialized at image runtime. Object has been initialized without the native-image initialization instrumentation and the stack trace can't be tracked.
Trace:
at parsing io.netty.resolver.dns.DnsNameResolver.resolveHostsFileEntry(DnsNameResolver.java:659)
Call path from entry point to io.netty.resolver.dns.DnsNameResolver.resolveHostsFileEntry(String):
at io.netty.resolver.dns.DnsNameResolver.resolveHostsFileEntry(DnsNameResolver.java:651)
at io.netty.resolver.dns.DnsNameResolver.doResolve(DnsNameResolver.java:884)
at io.netty.resolver.dns.DnsNameResolver.doResolve(DnsNameResolver.java:733)
at io.netty.resolver.SimpleNameResolver.resolve(SimpleNameResolver.java:61)
at io.netty.resolver.SimpleNameResolver.resolve(SimpleNameResolver.java:53)
at io.netty.resolver.InetSocketAddressResolver.doResolve(InetSocketAddressResolver.java:55)
at io.netty.resolver.InetSocketAddressResolver.doResolve(InetSocketAddressResolver.java:31)
at io.netty.resolver.AbstractAddressResolver.resolve(AbstractAddressResolver.java:106)
at io.netty.bootstrap.Bootstrap.doResolveAndConnect0(Bootstrap.java:206)
at io.netty.bootstrap.Bootstrap.access$000(Bootstrap.java:46)
at io.netty.bootstrap.Bootstrap$1.operationComplete(Bootstrap.java:180)
DnsNameResolver:659 is
return LOCALHOST_ADDRESS;
and it references the static field named LOCALHOST_ADDRESS of type InetAddress. Let's avoid its initialization
at build time by adding the following to the native-image command`:
--initialize-at-run-time=io.netty.resolver.dns.DnsNameResolver
The error goes away.
2
Now there is another one:
Error: No instances of java.net.Inet6Address are allowed in the image heap as this class should be initialized at image runtime. Object has been initialized without the native-image initialization instrumentation and the stack trace can't be tracked.
Trace: Object was reached by
reading field java.util.HashMap$Node.value of
constant java.util.HashMap$Node#26eb0f30 reached by
indexing into array
constant java.util.HashMap$Node[]#63e95621 reached by
reading field java.util.HashMap.table of
constant java.util.HashMap#563992d1 reached by
reading field java.util.Collections$UnmodifiableMap.m of
constant java.util.Collections$UnmodifiableMap#38a9945c reached by
reading field io.netty.resolver.DefaultHostsFileEntriesResolver.inet6Entries of
constant io.netty.resolver.DefaultHostsFileEntriesResolver#7ef4ba7e reached by
scanning method io.netty.resolver.dns.DnsNameResolverBuilder.<init>(DnsNameResolverBuilder.java:56)
Call path from entry point to io.netty.resolver.dns.DnsNameResolverBuilder.<init>():
at io.netty.resolver.dns.DnsNameResolverBuilder.<init>(DnsNameResolverBuilder.java:68)
at io.netty.resolver.dns.DnsAddressResolverGroup.<init>(DnsAddressResolverGroup.java:54)
at Main.main(Main.java:18)
DnsNameResolverBuilder:56 is
private HostsFileEntriesResolver hostsFileEntriesResolver = HostsFileEntriesResolver.DEFAULT;
Let's postpone HostsFileEntriesResolver initialization:
--initialize-at-run-time=io.netty.resolver.HostsFileEntriesResolver
3
Now, there is another error:
Error: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException: No instances of java.net.Inet6Address are allowed in the image heap as this class should be initialized at image runtime. Object has been initialized without the native-image initialization instrumentation and the stack trace can't be tracked.
Trace:
at parsing io.netty.resolver.dns.DnsQueryContextManager.getOrCreateContextMap(DnsQueryContextManager.java:111)
Call path from entry point to io.netty.resolver.dns.DnsQueryContextManager.getOrCreateContextMap(InetSocketAddress):
at io.netty.resolver.dns.DnsQueryContextManager.getOrCreateContextMap(DnsQueryContextManager.java:96)
DnsQueryContextManager:111 references NetUtil.LOCALHOST6. NetUtil is initialized at build time, but its fields
LOCALHOST4 and LOCALHOST6 contain instances of Inet4Address and Inet6Address, respectively. This can be solved
by a substitution. We just add the following class to our project:
#TargetClass(NetUtil.class)
final class NetUtilSubstitutions {
#Alias
#InjectAccessors(NetUtilLocalhost4Accessor.class)
public static Inet4Address LOCALHOST4;
#Alias
#InjectAccessors(NetUtilLocalhost6Accessor.class)
public static Inet6Address LOCALHOST6;
private static class NetUtilLocalhost4Accessor {
static Inet4Address get() {
// using https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom
return NetUtilLocalhost4LazyHolder.LOCALHOST4;
}
static void set(Inet4Address ignored) {
// a no-op setter to avoid exceptions when NetUtil is initialized at run-time
}
}
private static class NetUtilLocalhost4LazyHolder {
private static final Inet4Address LOCALHOST4;
static {
byte[] LOCALHOST4_BYTES = {127, 0, 0, 1};
// Create IPv4 loopback address.
try {
LOCALHOST4 = (Inet4Address) InetAddress.getByAddress("localhost", LOCALHOST4_BYTES);
} catch (Exception e) {
// We should not get here as long as the length of the address is correct.
PlatformDependent.throwException(e);
throw new IllegalStateException("Should not reach here");
}
}
}
private static class NetUtilLocalhost6Accessor {
static Inet6Address get() {
// using https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom
return NetUtilLocalhost6LazyHolder.LOCALHOST6;
}
static void set(Inet6Address ignored) {
// a no-op setter to avoid exceptions when NetUtil is initialized at run-time
}
}
private static class NetUtilLocalhost6LazyHolder {
private static final Inet6Address LOCALHOST6;
static {
byte[] LOCALHOST6_BYTES = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
// Create IPv6 loopback address.
try {
LOCALHOST6 = (Inet6Address) InetAddress.getByAddress("localhost", LOCALHOST6_BYTES);
} catch (Exception e) {
// We should not get here as long as the length of the address is correct.
PlatformDependent.throwException(e);
throw new IllegalStateException("Should not reach here");
}
}
}
}
The idea is to replace loads of the problematic fields with method invocations controlled by us. This is achieved
via a substitution (note #TargetClass, #Alias and #InjectAccessors). As a result, the InetAddress values
are not stored in image heap anymore. The error goes away.
4
We now have another one:
Error: No instances of java.net.Inet6Address are allowed in the image heap as this class should be initialized at image runtime. Object has been initialized without the native-image initialization instrumentation and the stack trace can't be tracked.
Trace: Object was reached by
reading field io.netty.channel.socket.InternetProtocolFamily.localHost of
constant io.netty.channel.socket.InternetProtocolFamily#5dc39065 reached by
scanning method io.netty.resolver.dns.DnsNameResolver.preferredAddressType(DnsNameResolver.java:487)
Call path from entry point to io.netty.resolver.dns.DnsNameResolver.preferredAddressType(ResolvedAddressTypes):
at io.netty.resolver.dns.DnsNameResolver.preferredAddressType(DnsNameResolver.java:481)
As it can be seen from the code of InternetProtocolFamily, each enum constant stores an instance of InetAddress,
so if any class initialized at build time initializes InternetProtocolFamily, the image heap gets polluted with
InetAddress instance. This can also be solved with a substitution:
#TargetClass(InternetProtocolFamily.class)
final class InternetProtocolFamilySubstitutions {
#Alias
#InjectAccessors(InternetProtocolFamilyLocalhostAccessor.class)
private InetAddress localHost;
private static class InternetProtocolFamilyLocalhostAccessor {
static InetAddress get(InternetProtocolFamily family) {
switch (family) {
case IPv4:
return NetUtil.LOCALHOST4;
case IPv6:
return NetUtil.LOCALHOST6;
default:
throw new IllegalStateException("Unsupported internet protocol family: " + family);
}
}
static void set(InternetProtocolFamily family, InetAddress address) {
// storing nothing as the getter derives all it needs from its argument
}
}
}
The error goes away.
5
There is another one this time:
Error: No instances of java.net.Inet4Address are allowed in the image heap as this class should be initialized at image runtime. Object has been initialized without the native-image initialization instrumentation and the stack trace can't be tracked.
Detailed message:
Trace: Object was reached by
reading field java.net.InetSocketAddress$InetSocketAddressHolder.addr of
constant java.net.InetSocketAddress$InetSocketAddressHolder#34913c36 reached by
reading field java.net.InetSocketAddress.holder of
constant java.net.InetSocketAddress#ad1fe10 reached by
reading field io.netty.resolver.dns.SingletonDnsServerAddresses.address of
constant io.netty.resolver.dns.SingletonDnsServerAddresses#79fd599 reached by
scanning method io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.nameServerAddressStream(DefaultDnsServerAddressStreamProvider.java:115)
Call path from entry point to io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.nameServerAddressStream(String):
at io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.nameServerAddressStream(DefaultDnsServerAddressStreamProvider.java:115)
at io.netty.resolver.dns.DnsServerAddressStreamProviders$DefaultProviderHolder$1.nameServerAddressStream(DnsServerAddressStreamProviders.java:131)
at io.netty.resolver.dns.DnsNameResolver.doResolveAllUncached0(DnsNameResolver.java:1070)
First, let's move initialization of io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider to
run-time:
--initialize-at-run-time=io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider
Now, the error is similar, but still slightly different:
Error: No instances of java.net.Inet4Address are allowed in the image heap as this class should be initialized at image runtime. Object has been initialized without the native-image initialization instrumentation and the stack trace can't be tracked.
Trace: Object was reached by
reading field java.net.InetSocketAddress$InetSocketAddressHolder.addr of
constant java.net.InetSocketAddress$InetSocketAddressHolder#5537c5de reached by
reading field java.net.InetSocketAddress.holder of
constant java.net.InetSocketAddress#fb954f8 reached by
reading field io.netty.resolver.dns.SingletonDnsServerAddresses.address of
constant io.netty.resolver.dns.SingletonDnsServerAddresses#3ec9baab reached by
reading field io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider.defaultNameServerAddresses of
constant io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider#1b7f0339 reached by
reading field io.netty.resolver.dns.DnsServerAddressStreamProviders$DefaultProviderHolder$1.currentProvider of
constant io.netty.resolver.dns.DnsServerAddressStreamProviders$DefaultProviderHolder$1#2d249be7 reached by
scanning method io.netty.resolver.dns.DnsServerAddressStreamProviders.unixDefault(DnsServerAddressStreamProviders.java:104)
Call path from entry point to io.netty.resolver.dns.DnsServerAddressStreamProviders.unixDefault():
at io.netty.resolver.dns.DnsServerAddressStreamProviders.unixDefault(DnsServerAddressStreamProviders.java:104)
at io.netty.resolver.dns.DnsServerAddressStreamProviders.platformDefault(DnsServerAddressStreamProviders.java:100)
at Main.main(Main.java:18)
Ok, let's move initialization of io.netty.resolver.dns.DnsServerAddressStreamProviders$DefaultProviderHolder to
run-time as well:
'--initialize-at-run-time=io.netty.resolver.dns.DnsServerAddressStreamProviders$DefaultProviderHolder'
(note the single quotes: without them $ and characters following it will be interpreted by sh and replaced with an
empty string).
The error goes away.
Please note that the order turned out to be important here. When I first moved
io.netty.resolver.dns.DnsServerAddressStreamProviders$DefaultProviderHolder initialization to run-time but did not
touch io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider initialization, the error report did not change
a bit. So it takes a little patience and experimenting (or some knowledge which I do not have, alas).
Now we have this:
Error: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException: No instances of java.net.Inet6Address are allowed in the image heap as this class should be initialized at image runtime. Object has been initialized without the native-image initialization instrumentation and the stack trace can't be tracked.
Detailed message:
Trace:
at parsing io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.<clinit>(DefaultDnsServerAddressStreamProvider.java:87)
Call path from entry point to io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.<clinit>():
no path found from entry point to target method
Ok, it's NetUtil.LOCALHOST being referenced, so let's add a substitution for it as well (to NetUtilSubstitutions):
#Alias
#InjectAccessors(NetUtilLocalhostAccessor.class)
public static InetAddress LOCALHOST;
// NOTE: this is the simpliest implementation I could invent to just demonstrate the idea; it is probably not
// too efficient. An efficient implementation would only have getter and it would compute the InetAddress
// there; but the post is already very long, and NetUtil.LOCALHOST computation logic in Netty is rather cumbersome.
private static class NetUtilLocalhostAccessor {
private static volatile InetAddress ADDR;
static InetAddress get() {
return ADDR;
}
static void set(InetAddress addr) {
ADDR = addr;
}
}
This makes the final error go away.
Thanks to #NicolasFilotto for suggestions on item 5, I like his solution a lot more than the original, and actually
item 5 is an implementation of his ideas.
Summary of techniques
First of all, you could find a class that, being moved to runtime initialization phase, makes a failure to go away.
To do this, you could trace references in the provided stack trace. Best candidates to consider are static fields.
If item 1 does not help, you could try to create a substitution
There is also a theoretical possibility to go with a lighter variant: #RecomputeFieldValue, but it's more
restricted, and I could not make it work in this Netty-related task.
PS. Substitution-related code is inspired by https://github.com/quarkusio/quarkus/pull/5353/files
PPS. Item 5 solution is inspired by #NicolasFilotto
I have a cucumber scenario and the step uses assertEquals. My results report shows the stack trace which is not end user friendly. How can I suppress it
Scenario: Add two numbers
Given I have two inputs "3" and "2"
When I add them
Then the output should be "15"
You're correct in observing that the default XML output (assuming you're not outputting to JSON or text, but you didn't say) from a Junit tests show stack traces for failed steps. This isn't actually a Cucumber thing. CucumberOptions won't help you here.
You can:
Use a different or custom Runner for your test and then setup a tag that controls what is included in the output, or what will be read by the CI software of your choosing. For example the Confulence API API for doing this tells how "debugger"
Same type of deal for Ant Scripts to tweak the output, so that is doesn't show the output. A good Tutorial for learning how to use Any scripts to fire off your Cucumber JUnit Test is here.
Other have build a custom formatter for JUnit by implementing XMLJUnitResultFormatter API, explained more here - How do I configure JUnit Ant task to only produce output on failures?
Hope that gives you what you need.
I was also facing same issue with my Cucumber-Selenium-Java project. In the cucumber reports, it was generating around 40 lines of stacktrace. Due to this, it was impacting look and feel of the report. And the end user/client was little concerned about it. Because he/she was not really able to figure out the actual use of this stacktrace. So, I came up with below idea/approach. It's little bit tricky but, it's worthy.
Few notes before starting:
We cannot completely disable stacktrace in in all the cases. But we can modify the stacktrace and then, re-throw the new exception with useful and shortened stacktrace.
You need to be aware about frequently faced exceptions, errors. So that, we can create custom exception depending on the exceptions.
In the stacktrace it will generate few line of code from wrapper APIs, few lines from Junit/TestNg, few lines for java and selenium and there will be only one or two lines in the stacktrace, where actually our issue occurred.
Our test classes must be in unique package. So that, we can filter the stacktrace trace with package name and get the class name, line number and method name of actual issue and we can use this information in throwing custom exception. Hence, it will be easy to figure out the actual line of issue occurred. In my case all the classes were in package named "page". If you have more than one packages for your classes, then you can accordingly add string conditions in below code.
We need to wrap the test code in try-catch block. And while catching, we need to use Throwable class not exception class. Because, if there is any assertion failure, then Exception class won't be able to handle the issue as you know all the assertions come under Error class and Throwable is the parent of Error and Exception.
If we throw the new exception in catch block, then, it will change the line number in stacktrace, where actual issue occurred. So it will be difficult to figure out the actual line of issue. In order to avoid it, we need to get the class name, line number, method name of actual issue and store it in StackTraceElement class and use it in throwing new exception.
Some exceptions like "NoSuchElementException" provides lot of information in their cause and most of it is not really required, So we need to modify the content of it's message by using substring(), indexOf() and replaceAll() methods of String class in Java. And then, provide the modified information in new exception.
Few important Java method from Throwable java class and their description: (i) getStackTrace(): This method will return us array of StackTraceElement class. StackTraceElement class will provide us the class name, method name, line number at which issue is occurred. (ii) setStackTrace(): This method is used to provide a custom stacktrace to new Exception. (iii) getCause(): This method will provide the issue message from cause of exception. But sometimes, it might return null. Because for some exceptions "cause" might not be specified. So this needs be surround in try catch block and here we need to use getMessage() method for getting the actual error message. (iv) getClass(): This method will return the actual exception class name. We will use this method for figuring out the exception class name and then, we will use it for providing specific implementation for different different exception classes. Note: "getClass()" method is not from "Throwable" class. It is from Object class.
You need to create a common method for handling all the exceptions and reuse this method in all the required classes. e.g.: I have named the method as "processException" and placed it in "ReusableMethod" class.
Note that, I am using package name "page" in below method (line#8), because all my test classes are placed in this package. In your case you need to update the package name as per your need. Also, I have written custom cases for two exceptions only: NoSuchElementException & AssertionError. You might need to write more cases as per your need.
public void processException(Throwable e) throws Exception {
StackTraceElement[] arr = e.getStackTrace();
String className = "";
String methodName = "";
int lineNumber = 0;
for (int i = 0; i < arr.length; i++) {
String localClassName = arr[i].getClassName();
if (localClassName.startsWith("page")) {
className = localClassName;
methodName = arr[i].getMethodName();
lineNumber = arr[i].getLineNumber();
break;
}
}
String cause = "";
try {
cause = e.getCause().toString();
} catch (NullPointerException e1) {
cause = e.getMessage();
}
StackTraceElement st = new StackTraceElement(className, methodName, "Line", lineNumber);
StackTraceElement[] sArr = { st };
if (e.getClass().getName().contains("NoSuchElementException")) {
String processedCause = cause.substring(cause.indexOf("Unable to locate"), cause.indexOf("(Session info: "))
.replaceAll("\\n", "");
Exception ex = new Exception("org.openqa.selenium.NoSuchElementException: " + processedCause);
ex.setStackTrace(sArr);
throw ex;
} else if (e.getClass().getName().contains("AssertionError")) {
AssertionError ae = new AssertionError(cause);
ae.setStackTrace(sArr);
throw ae;
} else {
Exception ex = new Exception(e.getClass() + ": " + cause);
ex.setStackTrace(sArr);
throw ex;
}
}
Below is the sample Method to showcase the usages of above method in Test Class methods. We are calling the above created method by using the class reference, which is "reuseMethod" in my case. And we are passing the caught Throwable reference "e" to the above method in catch block:
public void user_Navigates_To_Home_Page() throws Exception {
try {
//Certain lines of code as per your tests
//element.click();
} catch (Throwable e) {
reuseMethod.processException(e);
}
}
Here are few screenshots for implementation of NoSuchElementException:
Before Implementing this approach:
After Implementing this approach:
EDIT2
#paradigmatic made a good point in suggesting to redirect rather than throw the exception; that solves the logging issue. The problem in Play 2 is that redirects need to occur within so-called Action scope, which is not always the case with date parser calls.
As a workaround, I went with Play's global interceptor, presumably the equivalent of a Java servlet filter.
val ymdMatcher = "\\d{8}".r // matcher for yyyyMMdd URI param
val ymdFormat = org.joda.time.format.DateTimeFormat.forPattern("yyyyMMdd")
def ymd2Date(ymd: String) = ymdFormat.parseDateTime(ymd)
override def onRouteRequest(r: RequestHeader): Option[Handler] = {
import play.api.i18n.Messages
ymdMatcher.findFirstIn(r.uri) map{ ymd=>
try { ymd2Date( ymd); super.onRouteRequest(r) }
catch { case e:Exception => // kick to "bad" action handler on invalid date
Some(controllers.Application.bad(Messages("bad.date.format")))
}
} getOrElse(super.onRouteRequest(r))
}
EDIT
Here 's a little context to work with:
// String "pimp": transforms ymdString.to_date call into JodaTime instance
class String2Date(ymd: String) {
def to_date = {
import play.api.i18n.Messages
try{ ymdFormat.parseDateTime(ymd) }
catch { case e:Exception => throw new NoTrace(Messages("bad.date.format")) }
}
val ymdFormat = org.joda.time.format.DateTimeFormat.forPattern("yyyyMMdd")
}
#inline implicit final def string2Date(ymd: String) = new String2Date(ymd)
and a test custom exception handler:
public class NoTrace extends Exception {
static final long serialVersionUID = -3387516993124229948L;
#Override
public Throwable fillInStackTrace() {
return null;
}
public NoTrace(String message) {
super(message);
}
}
Calling the date parser on an invalid yyyyMMdd string logs 30 line stack trace to the log (this occurs upstream by Play framework/Netty container, better than default 100 line trace):
"20120099".to_date
ORIGINAL
Have an issue where my application.log is getting filled with errors related to a uri date parser operation that should succeed given a valid yyyyMMdd uri date.
However, some users try to circumvent this by entering invalid dates in hopes of gaining free access to paid subscriber-only content. It's pointless, as it simply won't work, but regardless, I have MBs of these error traces in my application log.
Is there a way to throw a truly trimmed down Exception to the log? I found this SO answer, but in my application it looks like the container (Play framework on Netty) gets into the mix and logs its own 30 line stack trace to the log (30 lines is better than 100, but still 29 too many)
Similarly, I found this thread in regard to Java 7 and the new option to suppress stack trace; however, for some reason, despite being on Java 1.7, with Eclipse configured for Java 1.7, only the old 2 param method of Throwable is available (and I do see the 4 param method when I click through to the Throwable class; maybe a Scala 2.9.2 library issue?)
At any rate, ideally I can simply log a 1-line exception message and not the kitchen sink.
Simply override this method in your custom exception class:
#Override
public Throwable fillInStackTrace() {
return this;
}
after adding this method your trace method will not print
Your trouble is that although you can suppress the stacktrace of the exception your own code threw, there is nothing you can do about the exception it will be wrapped into by the framework. The only avenue I can see is not allowing the framework to catch your exception at all (doing your own top-level handling) or tweaking the logging configuration.
I think you have two options:
Control the logging to not save stack traces for some exceptions.
Write a post-processor that filters out the traces from the log file.
Unless you are in danger of running out of disk space, I think #2 is the better option, because if you do have a bug you can go back to the full log and have all the exception history.
The philosophy behind idea #2 is that disk space is cheap, but information can be precious during debug. Log a lot of data. Normally, use scripts to examine the log after it has been written to disk.
For example, if there is a type of log entry that you never expect to see, but that demands immediate action if it does appear, write a script that searches for it, and send you an e-mail if it finds one.
One of the most useful forms of script in this approach is one that drops stack trace lines. Usually, you only need to know what exceptions are happening, and the stack trace takes up a lot of screen space without telling you much. If you do need to investigate an exception, go back to the full log, find the exception line, and look at the stack trace and at what was happening immediately before the exception.
If there are too many of your date exceptions, have the script drop even the exception line. If you want to track how often they are happening, run a script that counts date exceptions per hour.
That sort of script typically costs a few minutes of programming in your favorite regex-capable script language.
I'm running into the strangest error in this program, which is confirmed when debugging it. I have the following code (boiled down to highlight the problem, of course):
BHFrame.java
public class BHFrame
{
private boolean uSS;
private StateSaver stateSaver;
public BHFrame(boolean useInternalStateSaver)
{
//Init code
uSS = useInternalStateSaver;
//More init code
System.out.println(uSS);
if (uSS)
{System.out.println("Entered 1");
stateSaver = new StateSaver(title, false);
stateSaver.addSaveable(getThis());
}
//More init code
System.out.println(uSS);
if (uSS)
{System.out.println("Entered 2");
try
{
stateSaver.loadState();
stateSaver.putState(getThis());
}
catch (IOException ex)
{
alertUserOfException(ex);
}
}
}
}
GUI.java
public class GUI extends BHFrame
{
public GUI(boolean useInternalStateSaver)
{
super(useInternalStateSaver);
}
}
Main.java
public class Main
{
public static void main(String[] args)
{
GUI gui = new GUI(false);
}
}
Output
false
false
Entered 2
Exception in thread "main" java.lang.NullPointerException
at bht.tools.comps.BHFrame.<init>(BHFrame.java:26)
at bhms.GUI.<init>(GUI.java:5)
at bhms.Main.main(Main.java:5)
The class BHFrame is extended and run from a child class that calls this constructor, but that really shouldn't affect this behavior. The problem is that, when false is passed to the constructor as useInternalStateSaver, the first if (uSS) is skipped, but the second is entered. Upon debugging, I found that uSS is false throughout runtime, including on the line of the second if statement, here. Why would Java enter an if statement when the condition returns false? Before you ask, I did delete the .class files and recompile it just in case there was some residual code messing with it, but I got the same result. And rest assured, all the references to the uSS variable are displayed here.
Solution
As it turns out, this appears to be a bug in NetBeans 7.1 Build 201109252201, wherein the IDE doesn't properly insert new code into the compiled .class files. The problem was fixed by compiling the files externally. A bug report has been submitted.
Whatever's throwing that exception is probably not in your posted code.
It's not being caught by your catch statement, which only catches IOException.
It's a NullPointerException and can occur anywhere.
You have shown no indication that the code inside your if block is actually executing. In your screenshot, there is absolutely know way of knowing if your if block is entered or not. There are no logging statements.
Add debugging messages at various points to see exactly what is happening. Or, you know, look at line 26 (wayyyyy before your posted code) to see why you're getting a NullPointerException.
I've seen crazy stuff like this when there is bad RAM on the machine. You might want to run memtest86.
You might also consider deleting all of your project class files, and then doing a build. Maybe you changed Main.java, but it was never recompiled. I hate that when that happens.
This is just a guess, because I can't see the code you are mentioning, but I reckon you have defined a local variable uSS in the second //More init code segment.
Once you define a local variable named the same as an instance variable, it 'hides' the instance variable. Better to qualify all instance variables with this.
So, try qualifying all above accesses of uSS with this. ... (this.uSS)
Even if this isn't the issue, it might be better to post the full code anyway.
HTH
I'm getting a NullPointerException in a Nokia S40.
I want to know what is causing this exception.
The device shows:
NullPointerException java/lang/NullPointerException
This error only occurs in the device, running in the emulator the application works fine.
I use microlog to debug my application. But the application works fine if the log is enabled.
Is there a way to get the stack trace when I get this NullPointerException? I don't need all details like the line number just which method cause this exception.
UPDATE: I installed the same application in another Nokia S40 and the same error didn't occur.
Nokia 2660 - error
Nokia 6131 - no error
UPDATE 2: Somehow I find what was causing the NullPointerException.
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;
public class OuterClass extends Canvas {
private Config config;
public OuterClass() {
this.config = new Config();
}
public void paint(Graphics graphics) {
HelperClass helper = new HelperClass(this.config);
helper.doStuff();
}
public void dispose() {
this.config = null;
}
public class Config implements IConfig {
public int getSomething() {
// ...
}
}
}
public class HelperClass {
private IConfig config;
public HelperClass(IConfig) {
this.config = config;
}
public doStuff() {
config.getSomething(); // Here is thrown NullPointerException
}
}
In some situations a thread is started and call the OuterClass.dispose() before the helper.doStuff() causing the NPE. I think when I enabled the log it made the thread slower and helper.doStuff() was called when I expected it to be called.
You are not going to find any way to save a Throwable stack trace on a Nokia Series40 handset.
The usual brute force way of debugging JavaME application on Series40 is to modify your code to create a stack trace yourself in memory.
What I'm talking about is:
Each Thread that you can identify (including system callback threads) needs its own Stack object, containing strings. Obviously, this increases the memory footprint of your application somewhat but keeping it in memory should limit the impact on race conditions.
When your code enters a method, it adds the method signature to the current Thread Stack. When the method exits (and you better only have one exit point per method) it pops the top of the Stack.
You can add aditional debug info on the stack, like values of variables in different places of the code.
You don't necessarily need to add this to every single method in your code.
You can add try{}catch(Throwable){} to the entry point of every thread you identified and either dump the stack in a file or on the screen (in a Form).
Obviously, this is not the kind of change you want to manually add in a lot of places in a large existing codebase. You can however make it part of your organisation coding standards for the future and write a source code parsing script to automatically add it to existing code.
I had some trouble in the past trying to print the stack trace to somewhere else than the standard output. The standard exception class doesn't provide the printStackTrace method that receives the output stream, therefore it only prints to the standard output stream.
It's possible, at least in Java SE, to redirect the java output stream to somewhere else by simply saying that System.out = . The PrintStream class receives an OutputStream, which means that you could create your own ByteArrayOutputStream, initialize a PrintStream, sets System.out to that stream, and then call ex.printStackTrace(). I don't have a J2ME environment here but I believe that, as long as it won't break when you try to set System.out to something else (nothing says that it's readonly in the docs), you should be able to do it.
After you do that, I would recommend writing it to a RecordStore that you have specifically for that, and then upload the records of that RecordStore to your server so you can get it.
I know it's not very easy but it may work. I would first test the System.out thing. If it works, everything else should work too.
My answer was wrong. As pointed out, the System.out and System.err fields are declared final. If you can't get the stack trace, and if you can't get the error when running the application on your emulator, try creating trace bullets on your code (alerts, logs, whatever you can) to isolate the piece of code where the problem is happening. It has to be something that could change between the emulator and the real device - for example, something related to retrieving/ saving records in a RecordStore, opening a Connection, etc... What did you try to do when you had the problem?
You could use Microlog to send an e-mail when the exception occurs.
Microlog
You could try catching the exception in some high-level try/catch block and then emailing the trace to yourself.