From 74cdbc529cd7b826c76613355a09747be720d501 Mon Sep 17 00:00:00 2001 From: Taner Sener Date: Thu, 21 Jan 2021 19:55:22 +0000 Subject: [PATCH 1/5] update project logo --- README.md | 2 +- docs/assets/smart-exception-logo-v3.png | Bin 0 -> 7366 bytes docs/assets/smart-exception-logo-v4.png | Bin 0 -> 6600 bytes 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 docs/assets/smart-exception-logo-v3.png create mode 100644 docs/assets/smart-exception-logo-v4.png diff --git a/README.md b/README.md index a840e1b..e37bfa9 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Utilities to handle Throwable objects and stack trace elements - + ### 1. Features diff --git a/docs/assets/smart-exception-logo-v3.png b/docs/assets/smart-exception-logo-v3.png new file mode 100644 index 0000000000000000000000000000000000000000..3460e49c34d8e1ea3d70fb1fd9844762e8cae6ec GIT binary patch literal 7366 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 E0H%|)&Hw-a literal 0 HcmV?d00001 diff --git a/docs/assets/smart-exception-logo-v4.png b/docs/assets/smart-exception-logo-v4.png new file mode 100644 index 0000000000000000000000000000000000000000..abe681b70d210e5b17b1fee6da5daadb69850d19 GIT binary patch literal 6600 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 MXzFWJtJz2Y58K$ofdBvi literal 0 HcmV?d00001 From efc68796c80f106bc707d68102cb759409a989d2 Mon Sep 17 00:00:00 2001 From: Taner Sener Date: Sun, 7 Feb 2021 16:01:33 +0000 Subject: [PATCH 2/5] update release scripts --- build.gradle | 5 -- common/build.gradle | 109 ++++++++++++++++++++------------------------ java/build.gradle | 109 ++++++++++++++++++++------------------------ java9/build.gradle | 109 ++++++++++++++++++++------------------------ 4 files changed, 150 insertions(+), 182 deletions(-) diff --git a/build.gradle b/build.gradle index 65df42e..95ed9d8 100644 --- a/build.gradle +++ b/build.gradle @@ -5,15 +5,10 @@ buildscript { google() jcenter() } - dependencies { - classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5' - } } subprojects { apply plugin: 'java-library' - apply plugin: 'com.jfrog.bintray' - apply plugin: 'maven-publish' group = 'com.arthenica' version = '0.1.0' diff --git a/common/build.gradle b/common/build.gradle index d69bf66..7a9f7d3 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -21,8 +21,12 @@ artifacts { archives sourcesJar } +apply plugin: 'maven' +apply plugin: 'signing' + +archivesBaseName = "smart-exception-common" + ext { - bintrayName = "smart-exception-common" libraryName = "smart-exception-common" artifact = "smart-exception-common" libraryDescription = "smart-exception common package" @@ -31,72 +35,59 @@ ext { if (project.hasProperty('release')) { - def pomConfig = { - licenses { - license { - name licenseName - url licenseUrl - } - } - developers { - developer { - id developerId - name developerName - email developerEmail - } - } - - scm { - connection gitUrl - developerConnection gitUrl - url siteUrl + File propertiesFile = project.rootProject.file('local.properties') + if (propertiesFile.exists()) { + Properties properties = new Properties() + properties.load(new FileInputStream(propertiesFile)) + properties.each { name, value -> + ext[name] = value } } - publishing { - publications { - mavenJava(MavenPublication) { - from components.java - artifact sourcesJar - artifact javadocJar - groupId group - artifactId libraryName - version libraryVersion - pom.withXml { - def root = asNode() - root.appendNode('packaging', 'jar') - root.appendNode('name', libraryName) - root.appendNode('description', libraryDescription) - root.appendNode('url', siteUrl) - root.children().last() + pomConfig + uploadArchives { + repositories { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { + authentication(userName: ossrhUsername, password: ossrhPassword) } - } - } - } - Properties properties = new Properties() - properties.load(project.rootProject.file('local.properties').newDataInputStream()) + pom.project { + packaging 'jar' + name = libraryName + description = libraryDescription + url = siteUrl - bintray { - user = properties.getProperty("bintray.user") - key = properties.getProperty("bintray.apikey") - publications = ['mavenJava'] - pkg { - repo = bintrayRepo - name = bintrayName - desc = libraryDescription - websiteUrl = siteUrl - issueTrackerUrl = issueTrackerUrl - vcsUrl = gitUrl - licenses = allLicenses - dryRun = true - publish = false - override = false - publicDownloadNumbers = false - version { - desc = libraryDescription + licenses { + license { + name = licenseName + url = licenseUrl + } + } + developers { + developer { + id developerId + name developerName + email developerEmail + } + } + scm { + connection gitUrl + developerConnection gitUrl + url siteUrl + } + } } } } + signing { + sign configurations.archives + } + } diff --git a/java/build.gradle b/java/build.gradle index b641f88..84cb08e 100644 --- a/java/build.gradle +++ b/java/build.gradle @@ -25,8 +25,12 @@ artifacts { archives sourcesJar } +apply plugin: 'maven' +apply plugin: 'signing' + +archivesBaseName = "smart-exception-java" + ext { - bintrayName = "smart-exception-java" libraryName = "smart-exception-java" artifact = "smart-exception-java" libraryDescription = "smart-exception java and android package" @@ -35,72 +39,59 @@ ext { if (project.hasProperty('release')) { - def pomConfig = { - licenses { - license { - name licenseName - url licenseUrl - } - } - developers { - developer { - id developerId - name developerName - email developerEmail - } - } - - scm { - connection gitUrl - developerConnection gitUrl - url siteUrl + File propertiesFile = project.rootProject.file('local.properties') + if (propertiesFile.exists()) { + Properties properties = new Properties() + properties.load(new FileInputStream(propertiesFile)) + properties.each { name, value -> + ext[name] = value } } - publishing { - publications { - mavenJava(MavenPublication) { - from components.java - artifact sourcesJar - artifact javadocJar - groupId group - artifactId libraryName - version libraryVersion - pom.withXml { - def root = asNode() - root.appendNode('packaging', 'jar') - root.appendNode('name', libraryName) - root.appendNode('description', libraryDescription) - root.appendNode('url', siteUrl) - root.children().last() + pomConfig + uploadArchives { + repositories { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + pom.project { + packaging 'jar' + name = libraryName + description = libraryDescription + url = siteUrl + + licenses { + license { + name = licenseName + url = licenseUrl + } + } + developers { + developer { + id developerId + name developerName + email developerEmail + } + } + scm { + connection gitUrl + developerConnection gitUrl + url siteUrl + } } } } } - Properties properties = new Properties() - properties.load(project.rootProject.file('local.properties').newDataInputStream()) - - bintray { - user = properties.getProperty("bintray.user") - key = properties.getProperty("bintray.apikey") - publications = ['mavenJava'] - pkg { - repo = bintrayRepo - name = bintrayName - desc = libraryDescription - websiteUrl = siteUrl - issueTrackerUrl = issueTrackerUrl - vcsUrl = gitUrl - licenses = allLicenses - dryRun = true - publish = false - override = false - publicDownloadNumbers = false - version { - desc = libraryDescription - } - } + signing { + sign configurations.archives } } diff --git a/java9/build.gradle b/java9/build.gradle index b4a1e47..0f63dd7 100644 --- a/java9/build.gradle +++ b/java9/build.gradle @@ -25,8 +25,12 @@ artifacts { archives sourcesJar } +apply plugin: 'maven' +apply plugin: 'signing' + +archivesBaseName = "smart-exception-java9" + ext { - bintrayName = "smart-exception-java9" libraryName = "smart-exception-java9" artifact = "smart-exception-java9" libraryDescription = "smart-exception java9 package" @@ -35,72 +39,59 @@ ext { if (project.hasProperty('release')) { - def pomConfig = { - licenses { - license { - name licenseName - url licenseUrl - } - } - developers { - developer { - id developerId - name developerName - email developerEmail - } - } - - scm { - connection gitUrl - developerConnection gitUrl - url siteUrl + File propertiesFile = project.rootProject.file('local.properties') + if (propertiesFile.exists()) { + Properties properties = new Properties() + properties.load(new FileInputStream(propertiesFile)) + properties.each { name, value -> + ext[name] = value } } - publishing { - publications { - mavenJava(MavenPublication) { - from components.java - artifact sourcesJar - artifact javadocJar - groupId group - artifactId libraryName - version libraryVersion - pom.withXml { - def root = asNode() - root.appendNode('packaging', 'jar') - root.appendNode('name', libraryName) - root.appendNode('description', libraryDescription) - root.appendNode('url', siteUrl) - root.children().last() + pomConfig + uploadArchives { + repositories { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + pom.project { + packaging 'jar' + name = libraryName + description = libraryDescription + url = siteUrl + + licenses { + license { + name = licenseName + url = licenseUrl + } + } + developers { + developer { + id developerId + name developerName + email developerEmail + } + } + scm { + connection gitUrl + developerConnection gitUrl + url siteUrl + } } } } } - Properties properties = new Properties() - properties.load(project.rootProject.file('local.properties').newDataInputStream()) - - bintray { - user = properties.getProperty("bintray.user") - key = properties.getProperty("bintray.apikey") - publications = ['mavenJava'] - pkg { - repo = bintrayRepo - name = bintrayName - desc = libraryDescription - websiteUrl = siteUrl - issueTrackerUrl = issueTrackerUrl - vcsUrl = gitUrl - licenses = allLicenses - dryRun = true - publish = false - override = false - publicDownloadNumbers = false - version { - desc = libraryDescription - } - } + signing { + sign configurations.archives } } From b9736a2b9cbfb3b0e44a001b017ffaba3c2388b1 Mon Sep 17 00:00:00 2001 From: Taner Sener Date: Sun, 7 Feb 2021 18:56:09 +0000 Subject: [PATCH 3/5] Update maven repository links --- README.md | 6 +++--- build.gradle | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e37bfa9..c42f8ca 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# SmartException ![GitHub release](https://img.shields.io/badge/release-v0.1.0-blue.svg) ![Bintray](https://img.shields.io/badge/bintray-v0.1.0-blue.svg) [![Build Status](https://travis-ci.org/tanersener/smart-exception.svg?branch=master)](https://travis-ci.org/tanersener/smart-exception) +# SmartException ![GitHub release](https://img.shields.io/badge/release-v0.1.0-blue.svg) ![Maven Central](https://img.shields.io/maven-central/v/com.arthenica/smart-exception-java) [![Build Status](https://travis-ci.org/tanersener/smart-exception.svg?branch=master)](https://travis-ci.org/tanersener/smart-exception) Utilities to handle Throwable objects and stack trace elements @@ -20,7 +20,7 @@ Utilities to handle Throwable objects and stack trace elements ### 2. Using -Binaries are available at [Github](https://github.com/tanersener/mobile-ffmpeg/releases) and [JCenter](https://bintray.com/bintray/jcenter). +Binaries are available at [Github](https://github.com/tanersener/mobile-ffmpeg/releases) and [Maven Central](https://repo1.maven.org/maven2). #### 2.1. Gradle @@ -429,7 +429,7 @@ See the building status from the table below. - `common` includes shared classes and interfaces - `java` has Java 7/8 and Android specific implementation - `java9` has the implementation for Java 9 or later -- `test` includes test classes that use library jars published in `jcenter()` +- `test` includes test classes that use library jars published in `mavenCentral()` ### 7. License diff --git a/build.gradle b/build.gradle index 95ed9d8..4f649ad 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,6 @@ import java.text.SimpleDateFormat buildscript { repositories { google() - jcenter() } } @@ -93,7 +92,6 @@ subprojects { } repositories { - jcenter() mavenCentral() } From c4ee0fcc53623d5852ae4aa84adef5b069daf6f2 Mon Sep 17 00:00:00 2001 From: Taner Sener Date: Sun, 28 Nov 2021 19:14:41 +0000 Subject: [PATCH 4/5] add package information printing option, fixes #4 --- README.md | 201 +++++++++++++++--- .../smartexception/AbstractExceptions.java | 178 +++++++++++++--- .../arthenica/smartexception/ClassLoader.java | 39 ++++ .../smartexception/PackageLoader.java | 39 ++++ .../StackTraceElementSerializer.java | 14 +- docs/assets/smart-exception-logo-v5.png | Bin 0 -> 8112 bytes .../smartexception/java/Exceptions.java | 100 ++++++++- .../smartexception/java/JavaClassLoader.java | 64 ++++++ .../java/JavaPackageLoader.java | 45 ++++ .../smartexception/java/ApacheCxfTest.java | 32 +++ .../smartexception/java/ExceptionsTest.java | 78 ++++++- .../smartexception/java/SpringTest.java | 21 ++ .../smartexception/java9/Exceptions.java | 100 ++++++++- .../java9/Java9ClassLoader.java | 72 +++++++ .../java9/Java9PackageLoader.java | 45 ++++ .../smartexception/java9/ApacheCxfTest.java | 32 +++ .../smartexception/java9/ExceptionsTest.java | 79 ++++++- .../smartexception/java9/SpringTest.java | 21 ++ 18 files changed, 1100 insertions(+), 60 deletions(-) create mode 100644 common/src/main/java/com/arthenica/smartexception/ClassLoader.java create mode 100644 common/src/main/java/com/arthenica/smartexception/PackageLoader.java create mode 100644 docs/assets/smart-exception-logo-v5.png create mode 100644 java/src/main/java/com/arthenica/smartexception/java/JavaClassLoader.java create mode 100644 java/src/main/java/com/arthenica/smartexception/java/JavaPackageLoader.java create mode 100644 java9/src/main/java/com/arthenica/smartexception/java9/Java9ClassLoader.java create mode 100644 java9/src/main/java/com/arthenica/smartexception/java9/Java9PackageLoader.java diff --git a/README.md b/README.md index c42f8ca..c1eef44 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -# SmartException ![GitHub release](https://img.shields.io/badge/release-v0.1.0-blue.svg) ![Maven Central](https://img.shields.io/maven-central/v/com.arthenica/smart-exception-java) [![Build Status](https://travis-ci.org/tanersener/smart-exception.svg?branch=master)](https://travis-ci.org/tanersener/smart-exception) +# SmartException ![GitHub release](https://img.shields.io/badge/release-v0.1.1-blue.svg) ![Maven Central](https://img.shields.io/maven-central/v/com.arthenica/smart-exception-java) [![Build Status](https://travis-ci.org/tanersener/smart-exception.svg?branch=master)](https://travis-ci.org/tanersener/smart-exception) Utilities to handle Throwable objects and stack trace elements - + ### 1. Features -- Build shorter stack traces +- Build shorter stack traces - Start exception chain from a root package - Group stack trace elements from the same package - Ignore stack trace elements @@ -17,33 +17,37 @@ Utilities to handle Throwable objects and stack trace elements - Define how native methods and closed source stack trace elements will be printed - Search for a cause in an exception chain - Get the cause of a throwable +- Print package information (jar file and version) ### 2. Using -Binaries are available at [Github](https://github.com/tanersener/mobile-ffmpeg/releases) and [Maven Central](https://repo1.maven.org/maven2). +Binaries are available at [Github](https://github.com/tanersener/smart-exception/releases) +and [Maven Central](https://repo1.maven.org/maven2). #### 2.1. Gradle Add SmartException dependency to your `build.gradle`. - Java 9 or later + ``` dependencies { - implementation 'com.arthenica:smart-exception-java9:0.1.0' + implementation 'com.arthenica:smart-exception-java9:0.1.1' } ``` - Java 7/8 or Android + ``` dependencies { - implementation 'com.arthenica:smart-exception-java:0.1.0' + implementation 'com.arthenica:smart-exception-java:0.1.1' } ``` #### 2.2. Manual You can import SmartException jars into your IDE manually. Remember that both `smart-exception-java9` and -`smart-exception-java` jars depend on `smart-exception-common`. So, do not forget to import `smart-exception-common` +`smart-exception-java` jars depend on `smart-exception-common`. So, do not forget to import `smart-exception-common` too. #### 2.3. Import Exceptions class @@ -51,11 +55,13 @@ too. Import `Exceptions` class, which contains all utility methods, from the correct package. - Java 9 or later + ``` import com.arthenica.smartexception.java9.Exceptions; ``` - Java 7/8 or Android + ``` import com.arthenica.smartexception.java.Exceptions; ``` @@ -64,7 +70,7 @@ import com.arthenica.smartexception.java.Exceptions; Use `getStackTraceString` method to create a shorter stack trace. By default, `getStackTraceString` will generate the same long stack trace as a string for you. You need to define some rules for `getStackTraceString` to use. Those rules -define how the exception stack trace is processed and shortened. +define how the exception stack trace is processed and shortened. ``` Exceptions.getStackTraceString(e); @@ -72,9 +78,9 @@ Exceptions.getStackTraceString(e); ##### 2.4.1 Use `root` packages. -`root` package is the entry point in an exception chain. If you define a `root` package, then all stack trace elements -before that `root` package will be discarded, and you will get a cleaner, compact stack trace. A `root` package can be -defined in two different ways. +`root` package is the entry point in an exception chain. If you define a `root` package, then all stack trace elements +before that `root` package will be discarded, and you will get a cleaner, compact stack trace. A `root` package can be +defined in two different ways. - By registering a global `root` package. @@ -124,8 +130,8 @@ org.springframework.web.util.NestedServletException: Method not found. ##### 2.4.2 Use `group` packages. -`group` packages can be used to group stack trace elements from the same package into a single line. A `group` package -can be defined in two different ways. +`group` packages can be used to group stack trace elements from the same package into a single line. A `group` package +can be defined in two different ways. - By registering global `group` packages. @@ -208,6 +214,7 @@ Exceptions.getStackTraceString(e); ``` - By providing `ignore` packages while getting the stack trace. + ``` Exceptions.getStackTraceString(e, Collections.emptySet(), Collections.emptySet(), new HashSet(Arrays.asList("org.junit", "jdk.internal.reflect", "org.gradle"))); ``` @@ -267,7 +274,7 @@ Exceptions.setIgnoreAllCauses(true); ``` - Or you can specify that you want all causes ignored while getting the stack trace. - + ``` Exceptions.getStackTraceString(e, true); ``` @@ -297,6 +304,7 @@ Caused by: java.util.ConcurrentModificationException: java.lang.ArrayIndexOutOfB ##### 2.4.5 Define max number of stack trace elements that will be printed - Provide max depth while getting the stack trace. + ``` Exceptions.getStackTraceString(e, 5); ``` @@ -337,18 +345,162 @@ org.springframework.web.util.NestedServletException: Method not found. at java.base/java.lang.Thread.run(Thread.java:844) ``` +##### 2.4.6 Print package information + +Package information is the jar name and/or jar version of the class that is printed in a stack trace. Generating it is +expensive, so it is disabled by default. You can enable printing it in two ways. + +- By globally enabling it for all `getStackTraceString` calls. + +``` +Exceptions.setPrintPackageInformation(true); +``` + +- By providing `printPackageInformation` option while getting the stack trace. + +``` +Exceptions.getStackTraceString(e, Collections.singleton("com.arthenica"), Collections.singleton("org.apache.cxf"), new HashSet(Arrays.asList("org.junit", "jdk.internal.reflect", "org.gradle", "java.net", "sun.net")), false, true); +``` + +- You will have the following stack trace string. + +``` +javax.ws.rs.ProcessingException: java.net.ConnectException: ConnectException invoking http://localhost:12345/rs/service?param1=value1: Connection refused (Connection refused) + at org.apache.cxf ... 10 more [cxf-rt-rs-client-3.3.6.jar] + at com.arthenica.smartexception.java.ApacheCxfTest.accessAndPrintPackageInformation(ApacheCxfTest.java:88) +Caused by: java.net.ConnectException: ConnectException invoking http://localhost:12345/rs/service?param1=value1: Connection refused (Connection refused) + at java.lang.reflect.Constructor.newInstance(Constructor.java:488) + at org.apache.cxf ... 15 more [cxf-rt-transports-http-3.3.6.jar] + at com.arthenica.smartexception.java.ApacheCxfTest.accessAndPrintPackageInformation(ApacheCxfTest.java:88) +Caused by: java.net.ConnectException: Connection refused (Connection refused) + at org.apache.cxf ... 1 more [cxf-rt-transports-http-3.3.6.jar] + at java.security.AccessController.doPrivileged(Native Method) + at org.apache.cxf ... 18 more [cxf-rt-transports-http-3.3.6.jar] + at com.arthenica.smartexception.java.ApacheCxfTest.accessAndPrintPackageInformation(ApacheCxfTest.java:88) +``` + +- Instead of this one. + +``` +javax.ws.rs.ProcessingException: java.net.ConnectException: ConnectException invoking http://localhost:12345/rs/service?param1=value1: Connection refused (Connection refused) + at org.apache.cxf.jaxrs.client.AbstractClient.checkClientException(AbstractClient.java:629) + at org.apache.cxf.jaxrs.client.AbstractClient.preProcessResult(AbstractClient.java:605) + at org.apache.cxf.jaxrs.client.WebClient.doResponse(WebClient.java:1150) + at org.apache.cxf.jaxrs.client.WebClient.doChainedInvocation(WebClient.java:1087) + at org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:932) + at org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:901) + at org.apache.cxf.jaxrs.client.WebClient.invoke(WebClient.java:461) + at org.apache.cxf.jaxrs.client.SyncInvokerImpl.method(SyncInvokerImpl.java:135) + at org.apache.cxf.jaxrs.client.SyncInvokerImpl.method(SyncInvokerImpl.java:130) + at org.apache.cxf.jaxrs.client.SyncInvokerImpl.get(SyncInvokerImpl.java:50) + at org.apache.cxf.jaxrs.client.spec.InvocationBuilderImpl.get(InvocationBuilderImpl.java:88) + at com.arthenica.smartexception.java.ApacheCxfTest.accessAndPrintPackageInformation(ApacheCxfTest.java:88) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:564) + at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59) + at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) + at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56) + at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) + at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306) + at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100) + at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366) + at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103) + at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63) + at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331) + at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79) + at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329) + at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66) + at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293) + at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306) + at org.junit.runners.ParentRunner.run(ParentRunner.java:413) + at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110) + at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58) + at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38) + at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62) + at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:564) + at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36) + at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) + at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33) + at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94) + at com.sun.proxy.$Proxy2.processTestClass(Unknown Source) + at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:118) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:564) + at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36) + at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) + at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182) + at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164) + at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:412) + at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) + at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) + at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56) + at java.base/java.lang.Thread.run(Thread.java:844) +Caused by: java.net.ConnectException: ConnectException invoking http://localhost:12345/rs/service?param1=value1: Connection refused (Connection refused) + at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) + at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) + at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) + at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:488) + at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.mapException(HTTPConduit.java:1400) + at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1384) + at org.apache.cxf.io.AbstractWrappedOutputStream.close(AbstractWrappedOutputStream.java:77) + at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56) + at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:671) + at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:63) + at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308) + at org.apache.cxf.jaxrs.client.AbstractClient.doRunInterceptorChain(AbstractClient.java:701) + at org.apache.cxf.jaxrs.client.WebClient.doChainedInvocation(WebClient.java:1086) + ... 58 more +Caused by: java.net.ConnectException: Connection refused (Connection refused) + at java.base/java.net.PlainSocketImpl.socketConnect(Native Method) + at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:400) + at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:243) + at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:225) + at java.base/java.net.Socket.connect(Socket.java:591) + at java.base/sun.net.NetworkClient.doConnect(NetworkClient.java:177) + at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:474) + at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:569) + at java.base/sun.net.www.http.HttpClient.(HttpClient.java:242) + at java.base/sun.net.www.http.HttpClient.New(HttpClient.java:341) + at java.base/sun.net.www.http.HttpClient.New(HttpClient.java:362) + at java.base/sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1224) + at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1203) + at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1057) + at java.base/sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:991) + at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1563) + at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1491) + at java.base/java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:527) + at org.apache.cxf.transport.http.URLConnectionHTTPConduit$URLConnectionWrappedOutputStream$2.run(URLConnectionHTTPConduit.java:377) + at org.apache.cxf.transport.http.URLConnectionHTTPConduit$URLConnectionWrappedOutputStream$2.run(URLConnectionHTTPConduit.java:373) + at java.base/java.security.AccessController.doPrivileged(Native Method) + at org.apache.cxf.transport.http.URLConnectionHTTPConduit$URLConnectionWrappedOutputStream.getResponseCode(URLConnectionHTTPConduit.java:373) + at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.doProcessResponseCode(HTTPConduit.java:1597) + at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1625) + at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1570) + at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1371) + ... 65 more +``` + #### 2.5. Customise how a stack trace element will be printed -`com.arthenica.smartexception.StackTraceElementSerializer` interface includes several methods that you can use to -customise the stack trace string. Implement this interface and register your implementation using the following -method. +`com.arthenica.smartexception.StackTraceElementSerializer` interface includes several methods that you can use to +customise the stack trace string. Implement this interface and register your implementation using the following method. ``` Exceptions.setStackTraceElementSerializer(myImplementation); ``` -- `String toString(final StackTraceElement stackTraceElement)` method in this interface defines how single stack -trace element will be printed. +- `String toString(final StackTraceElement stackTraceElement)` method in this interface defines how single stack trace + element will be printed. For example, you can use the following method to print only the file name and the line number. @@ -373,11 +525,11 @@ java.lang.NumberFormatException: For input string: "ABC" at FrameworkMethod.java:59 ``` -- `String getNativeMethodDefinition()` method defines the text used for native methods. Java uses -`(Native Method)` text by default. +- `String getNativeMethodDefinition()` method defines the text used for native methods. Java uses + `(Native Method)` text by default. -- `String getUnknownSourceDefinition()` method specifies the text used for stack trace elements without a name -and line number. `(Unknown Source)` is used by default. +- `String getUnknownSourceDefinition()` method specifies the text used for stack trace elements without a name and line + number. `(Unknown Source)` is used by default. #### 2.6. Search for a cause @@ -403,7 +555,8 @@ if (Exceptions.containsCause(e, IllegalArgumentException.class)) | SmartException Version | Release Date | | :----: | :----: | -| [0.1.0](https://github.com/tanersener/smart-exception/releases/tag/v0.1.0) | April 12, 2020 | +| [0.1.1](https://github.com/tanersener/smart-exception/releases/tag/v0.1.1) | Nov 28, 2021 | +| [0.1.0](https://github.com/tanersener/smart-exception/releases/tag/v0.1.0) | Apr 12, 2020 | ### 5. Building diff --git a/common/src/main/java/com/arthenica/smartexception/AbstractExceptions.java b/common/src/main/java/com/arthenica/smartexception/AbstractExceptions.java index a0c478b..b55be77 100644 --- a/common/src/main/java/com/arthenica/smartexception/AbstractExceptions.java +++ b/common/src/main/java/com/arthenica/smartexception/AbstractExceptions.java @@ -1,7 +1,7 @@ /* * BSD 3-Clause License * - * Copyright (c) 2020, Taner Sener + * Copyright (c) 2020-2021, Taner Sener * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,6 +32,7 @@ package com.arthenica.smartexception; +import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -55,6 +56,11 @@ public abstract class AbstractExceptions { */ public static final boolean DEFAULT_IGNORE_ALL_CAUSES = false; + /** + *

Default value for printing package information when stack trace elements are printed or converted to string. + */ + public static final boolean DEFAULT_PRINT_PACKAGE_INFORMATION = false; + /** *

Stores global root package names. */ @@ -85,6 +91,11 @@ public abstract class AbstractExceptions { */ public static StackTraceElementSerializer stackTraceElementSerializer; + /** + *

Stores the global print package information option. + */ + public static boolean printPackageInformation = DEFAULT_PRINT_PACKAGE_INFORMATION; + /** *

Registers a new root package. * @@ -180,6 +191,31 @@ public static void setIgnoreAllCauses(final boolean ignoreAllCauses) { AbstractExceptions.ignoreAllCauses = ignoreAllCauses; } + /** + *

Returns the value of print package information option. + * + * @return the value of global print package information option. When this option is true, stack trace elements + * printed or converted to string will include the name of the jar file that includes the printed class and the version + * of the jar. If it is false, none of this information is printed + */ + public static boolean isPrintPackageInformation() { + return printPackageInformation; + } + + /** + *

Sets the value of print package information option. + * + *

When this option is true, stack trace elements printed or converted to string will include the name of the + * jar file that includes the printed class and the version of the jar. + * + *

Note that for some libraries extracting the jar file and the version may not be possible. + * + * @param printPackageInformation new print package information option. + */ + public static void setPrintPackageInformation(final boolean printPackageInformation) { + AbstractExceptions.printPackageInformation = printPackageInformation; + } + /** *

Returns the smart stack trace for the given throwable. * @@ -191,7 +227,7 @@ public static void setIgnoreAllCauses(final boolean ignoreAllCauses) { * @return a string containing the smart stack trace for the given throwable */ public static String getStackTraceString(final Throwable throwable) { - return getStackTraceString(throwable, false, rootPackageSet, groupPackageSet, ignorePackageSet, 0, ignoreAllCauses); + return getStackTraceString(throwable, false, rootPackageSet, groupPackageSet, ignorePackageSet, 0, ignoreAllCauses, printPackageInformation); } /** @@ -206,7 +242,7 @@ public static String getStackTraceString(final Throwable throwable) { * @return a string containing the smart stack trace for the given throwable */ public static String getStackTraceString(final Throwable throwable, final boolean ignoreAllCauses) { - return getStackTraceString(throwable, false, rootPackageSet, groupPackageSet, ignorePackageSet, 0, ignoreAllCauses); + return getStackTraceString(throwable, false, rootPackageSet, groupPackageSet, ignorePackageSet, 0, ignoreAllCauses, printPackageInformation); } /** @@ -219,7 +255,7 @@ public static String getStackTraceString(final Throwable throwable, final boolea * @return a string containing the smart stack trace for the given throwable */ public static String getStackTraceString(final Throwable throwable, final Set rootPackageSet, final Set groupPackageSet, final Set ignorePackageSet) { - return getStackTraceString(throwable, false, rootPackageSet, groupPackageSet, ignorePackageSet, 0, ignoreAllCauses); + return getStackTraceString(throwable, false, rootPackageSet, groupPackageSet, ignorePackageSet, 0, ignoreAllCauses, printPackageInformation); } /** @@ -233,7 +269,22 @@ public static String getStackTraceString(final Throwable throwable, final Setthrowable */ public static String getStackTraceString(final Throwable throwable, final Set rootPackageSet, final Set groupPackageSet, final Set ignorePackageSet, final boolean ignoreAllCauses) { - return getStackTraceString(throwable, false, rootPackageSet, groupPackageSet, ignorePackageSet, 0, ignoreAllCauses); + return getStackTraceString(throwable, false, rootPackageSet, groupPackageSet, ignorePackageSet, 0, ignoreAllCauses, printPackageInformation); + } + + /** + *

Returns the smart stack trace for the given throwable using packages provided. + * + * @param throwable parent throwable + * @param rootPackageSet root packages to use for building the stack trace + * @param groupPackageSet group packages to use for building the stack trace + * @param ignorePackageSet ignore packages to use for building the stack trace + * @param ignoreAllCauses ignore all causes in the exception chain + * @param printPackageInformation print package information + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable, final Set rootPackageSet, final Set groupPackageSet, final Set ignorePackageSet, final boolean ignoreAllCauses, final boolean printPackageInformation) { + return getStackTraceString(throwable, false, rootPackageSet, groupPackageSet, ignorePackageSet, 0, ignoreAllCauses, printPackageInformation); } /** @@ -244,7 +295,7 @@ public static String getStackTraceString(final Throwable throwable, final Setthrowable */ public static String getStackTraceString(final Throwable throwable, final String rootPackage) { - return getStackTraceString(throwable, false, Collections.singleton(rootPackage), new HashSet(), new HashSet(), 0, ignoreAllCauses); + return getStackTraceString(throwable, false, Collections.singleton(rootPackage), new HashSet(), new HashSet(), 0, ignoreAllCauses, printPackageInformation); } /** @@ -256,7 +307,7 @@ public static String getStackTraceString(final Throwable throwable, final String * @return a string containing the smart stack trace for the given throwable */ public static String getStackTraceString(final Throwable throwable, final String rootPackage, final String groupPackage) { - return getStackTraceString(throwable, false, Collections.singleton(rootPackage), Collections.singleton(groupPackage), new HashSet(), 0, ignoreAllCauses); + return getStackTraceString(throwable, false, Collections.singleton(rootPackage), Collections.singleton(groupPackage), new HashSet(), 0, ignoreAllCauses, printPackageInformation); } /** @@ -267,7 +318,7 @@ public static String getStackTraceString(final Throwable throwable, final String * @return a string containing the smart stack trace for the given throwable */ public static String getStackTraceString(final Throwable throwable, final int maxDepth) { - return getStackTraceString(throwable, false, new HashSet(), new HashSet(), new HashSet(), maxDepth, ignoreAllCauses); + return getStackTraceString(throwable, false, new HashSet(), new HashSet(), new HashSet(), maxDepth, ignoreAllCauses, printPackageInformation); } /** @@ -279,22 +330,36 @@ public static String getStackTraceString(final Throwable throwable, final int ma * @return a string containing the smart stack trace for the given throwable */ public static String getStackTraceString(final Throwable throwable, final int maxDepth, final boolean ignoreAllCauses) { - return getStackTraceString(throwable, false, new HashSet(), new HashSet(), new HashSet(), maxDepth, ignoreAllCauses); + return getStackTraceString(throwable, false, new HashSet(), new HashSet(), new HashSet(), maxDepth, ignoreAllCauses, printPackageInformation); + } + + /** + *

Returns the smart stack trace for the given throwable using elements found until the maxDepth. + * + * @param throwable parent throwable + * @param maxDepth max depth in exception chain that will be used + * @param ignoreAllCauses ignore all causes in the exception chain + * @param printPackageInformation print package information + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable, final int maxDepth, final boolean ignoreAllCauses, final boolean printPackageInformation) { + return getStackTraceString(throwable, false, new HashSet(), new HashSet(), new HashSet(), maxDepth, ignoreAllCauses, printPackageInformation); } /** *

Returns the smart stack trace for the given throwable using parameters provided. * - * @param throwable parent throwable - * @param isCause throwable is a cause or not - * @param rootPackageSet root packages to use for building the stack trace - * @param groupPackageSet group packages to use for building the stack trace - * @param ignorePackageSet ignore packages to use for building the stack trace - * @param maxDepth max depth in exception chain that will be used - * @param ignoreAllCauses ignore all causes in the exception chain + * @param throwable parent throwable + * @param isCause throwable is a cause or not + * @param rootPackageSet root packages to use for building the stack trace + * @param groupPackageSet group packages to use for building the stack trace + * @param ignorePackageSet ignore packages to use for building the stack trace + * @param maxDepth max depth in exception chain that will be used + * @param ignoreAllCauses ignore all causes in the exception chain + * @param printPackageInformation print package information * @return a string containing the smart stack trace for the given throwable */ - public static String getStackTraceString(final Throwable throwable, final boolean isCause, final Set rootPackageSet, final Set groupPackageSet, final Set ignorePackageSet, final int maxDepth, final boolean ignoreAllCauses) { + public static String getStackTraceString(final Throwable throwable, final boolean isCause, final Set rootPackageSet, final Set groupPackageSet, final Set ignorePackageSet, final int maxDepth, final boolean ignoreAllCauses, final boolean printPackageInformation) { final StringBuilder builder = new StringBuilder(); if (throwable == null) { @@ -342,7 +407,7 @@ public static String getStackTraceString(final Throwable throwable, final boolea if (groupPackageMatch != null) { if (!groupPackageMatch.equals(currentGroupPackage)) { - appendStackTraceGroupElement(builder, currentGroupPackage, currentGroupCount, firstStackTraceElementInTheGroup); + appendStackTraceGroupElement(builder, currentGroupPackage, currentGroupCount, firstStackTraceElementInTheGroup, printPackageInformation); builder.append(System.lineSeparator()); builder.append("\tat "); @@ -354,24 +419,24 @@ public static String getStackTraceString(final Throwable throwable, final boolea currentGroupCount++; } } else { - currentGroupCount = appendStackTraceGroupElement(builder, currentGroupPackage, currentGroupCount, firstStackTraceElementInTheGroup); + currentGroupCount = appendStackTraceGroupElement(builder, currentGroupPackage, currentGroupCount, firstStackTraceElementInTheGroup, printPackageInformation); builder.append(System.lineSeparator()); builder.append("\tat "); if (stackTraceElementSerializer == null) { throw new IllegalArgumentException("Stack trace element serializer not initialized."); } else { - builder.append(stackTraceElementSerializer.toString(traceElement)); + builder.append(stackTraceElementSerializer.toString(traceElement, printPackageInformation)); } currentGroupPackage = null; } } - appendStackTraceGroupElement(builder, currentGroupPackage, currentGroupCount, firstStackTraceElementInTheGroup); + appendStackTraceGroupElement(builder, currentGroupPackage, currentGroupCount, firstStackTraceElementInTheGroup, printPackageInformation); final Throwable cause = throwable.getCause(); if (cause != null && !containsPackage(className, ignoreCausePackageSet) && !ignoreAllCauses) { - builder.append(getStackTraceString(cause, true, rootPackageSet, groupPackageSet, ignorePackageSet, maxDepth, ignoreAllCauses)); + builder.append(getStackTraceString(cause, true, rootPackageSet, groupPackageSet, ignorePackageSet, maxDepth, ignoreAllCauses, printPackageInformation)); } return builder.toString(); @@ -384,14 +449,22 @@ public static String getStackTraceString(final Throwable throwable, final boolea * @param currentGroupPackage package name of the current group * @param numberOfElementsInTheCurrentGroup number of elements in the current group * @param firstStackTraceElementInTheGroup first stack trace element of this group + * @param printPackageInformation print package information * @return new value for the group element count */ - public static int appendStackTraceGroupElement(final StringBuilder stringBuilder, final String currentGroupPackage, final int numberOfElementsInTheCurrentGroup, final StackTraceElement firstStackTraceElementInTheGroup) { + public static int appendStackTraceGroupElement(final StringBuilder stringBuilder, final String currentGroupPackage, final int numberOfElementsInTheCurrentGroup, final StackTraceElement firstStackTraceElementInTheGroup, final boolean printPackageInformation) { if (numberOfElementsInTheCurrentGroup > 0) { if (stackTraceElementSerializer == null) { throw new IllegalArgumentException("Stack trace element serializer not initialized."); } else { - stringBuilder.append((numberOfElementsInTheCurrentGroup == 1) ? stackTraceElementSerializer.toString(firstStackTraceElementInTheGroup) : String.format("%s%s ... %d more", stackTraceElementSerializer.getModuleName(firstStackTraceElementInTheGroup), currentGroupPackage, (numberOfElementsInTheCurrentGroup - 1))); + if (numberOfElementsInTheCurrentGroup == 1) { + stringBuilder.append(stackTraceElementSerializer.toString(firstStackTraceElementInTheGroup, printPackageInformation)); + } else { + stringBuilder.append(String.format("%s%s ... %d more", stackTraceElementSerializer.getModuleName(firstStackTraceElementInTheGroup), currentGroupPackage, (numberOfElementsInTheCurrentGroup - 1))); + if (printPackageInformation) { + stringBuilder.append(stackTraceElementSerializer.getPackageInformation(firstStackTraceElementInTheGroup)); + } + } } } @@ -729,4 +802,61 @@ public static String packageName(final String className) { } } + /** + * Loads the implementation version for the given package. + * + * @param packageLoader package loader + * @param type class + * @param packageName package + * @return implementation version specified for the given package or null if a version is not specified + */ + public static String getVersion(final PackageLoader packageLoader, final Class type, final String packageName) { + try { + Package loadedPackage = type.getPackage(); + if (loadedPackage != null) { + return loadedPackage.getImplementationVersion(); + } + loadedPackage = packageLoader.getPackage(type.getClassLoader(), packageName); + if (loadedPackage != null) { + return loadedPackage.getImplementationVersion(); + } + } catch (Exception ignored) { + } + + return null; + } + + /** + * Returns the jar file that includes the given class. + * + * @param type class + * @return name of the jar file that includes the class or null if jar file information cannot be found + */ + public static String libraryName(final Class type) { + try { + if (type != null) { + URL resourceUrl = type.getClassLoader().getResource(type.getName().replace('.', '/') + ".class"); + if (resourceUrl != null) { + String resource = resourceUrl.toString(); + int index = resource.lastIndexOf('!'); + if (index > 0) { + resource = resource.substring(0, index); + index = resource.lastIndexOf('/'); + if (index > 0) { + resource = resource.substring(index + 1); + } + index = resource.lastIndexOf('\\'); + if (index > 0) { + resource = resource.substring(index + 1); + } + return resource; + } + } + } + } catch (Exception ignored) { + } + + return null; + } + } diff --git a/common/src/main/java/com/arthenica/smartexception/ClassLoader.java b/common/src/main/java/com/arthenica/smartexception/ClassLoader.java new file mode 100644 index 0000000..9a8e311 --- /dev/null +++ b/common/src/main/java/com/arthenica/smartexception/ClassLoader.java @@ -0,0 +1,39 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2021, Taner Sener + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.arthenica.smartexception; + +public interface ClassLoader { + + Class loadClass(final String className); + +} diff --git a/common/src/main/java/com/arthenica/smartexception/PackageLoader.java b/common/src/main/java/com/arthenica/smartexception/PackageLoader.java new file mode 100644 index 0000000..0e83a8c --- /dev/null +++ b/common/src/main/java/com/arthenica/smartexception/PackageLoader.java @@ -0,0 +1,39 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2021, Taner Sener + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.arthenica.smartexception; + +public interface PackageLoader { + + Package getPackage(final java.lang.ClassLoader classLoader, final String className); + +} diff --git a/common/src/main/java/com/arthenica/smartexception/StackTraceElementSerializer.java b/common/src/main/java/com/arthenica/smartexception/StackTraceElementSerializer.java index 440ef6b..6f5651f 100644 --- a/common/src/main/java/com/arthenica/smartexception/StackTraceElementSerializer.java +++ b/common/src/main/java/com/arthenica/smartexception/StackTraceElementSerializer.java @@ -43,10 +43,20 @@ public interface StackTraceElementSerializer { /** *

Converts the given stackTraceElement into a string. * - * @param stackTraceElement stack trace element to convert + * @param stackTraceElement stack trace element to convert + * @param printPackageInformation print package information * @return string representing the given stackTraceElement */ - String toString(final StackTraceElement stackTraceElement); + String toString(final StackTraceElement stackTraceElement, final boolean printPackageInformation); + + /** + *

Returns package information for the given stackTraceElement. + * + * @param stackTraceElement stack trace element to use + * @return package information of the given stackTraceElement or an empty string if implementation + * does not support getting package information + */ + String getPackageInformation(final StackTraceElement stackTraceElement); /** *

Returns module name for the given stackTraceElement. diff --git a/docs/assets/smart-exception-logo-v5.png b/docs/assets/smart-exception-logo-v5.png new file mode 100644 index 0000000000000000000000000000000000000000..c69f12a1618a8350ccb0b3a4e970ff84b4136667 GIT binary patch literal 8112 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 literal 0 HcmV?d00001 diff --git a/java/src/main/java/com/arthenica/smartexception/java/Exceptions.java b/java/src/main/java/com/arthenica/smartexception/java/Exceptions.java index 1df520c..e109f9e 100644 --- a/java/src/main/java/com/arthenica/smartexception/java/Exceptions.java +++ b/java/src/main/java/com/arthenica/smartexception/java/Exceptions.java @@ -1,7 +1,7 @@ /* * BSD 3-Clause License * - * Copyright (c) 2020, Taner Sener + * Copyright (c) 2020-2021, Taner Sener * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,6 +33,8 @@ package com.arthenica.smartexception.java; import com.arthenica.smartexception.AbstractExceptions; +import com.arthenica.smartexception.ClassLoader; +import com.arthenica.smartexception.PackageLoader; import com.arthenica.smartexception.StackTraceElementSerializer; import java.util.Set; @@ -47,10 +49,13 @@ public class Exceptions { static { + packageLoader = new JavaPackageLoader(); + classLoader = new JavaClassLoader(); + AbstractExceptions.setStackTraceElementSerializer(new StackTraceElementSerializer() { @Override - public String toString(StackTraceElement stackTraceElement) { + public String toString(final StackTraceElement stackTraceElement, final boolean printPackageInformation) { final StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(stackTraceElement.getClassName()); @@ -58,7 +63,7 @@ public String toString(StackTraceElement stackTraceElement) { stringBuilder.append(stackTraceElement.getMethodName()); if (stackTraceElement.isNativeMethod()) { - stringBuilder.append("(Native Method)"); + stringBuilder.append(getNativeMethodDefinition()); } else if ((stackTraceElement.getFileName() != null) && (stackTraceElement.getFileName().length() > 0)) { stringBuilder.append("("); stringBuilder.append(stackTraceElement.getFileName()); @@ -68,7 +73,37 @@ public String toString(StackTraceElement stackTraceElement) { } stringBuilder.append(")"); } else { - stringBuilder.append("(Unknown Source)"); + stringBuilder.append(getUnknownSourceDefinition()); + } + + if (printPackageInformation) { + stringBuilder.append(getPackageInformation(stackTraceElement)); + } + + return stringBuilder.toString(); + } + + @Override + public String getPackageInformation(final StackTraceElement stackTraceElement) { + final StringBuilder stringBuilder = new StringBuilder(); + + String className = stackTraceElement.getClassName(); + Class loadedClass = Exceptions.classLoader.loadClass(className); + if (loadedClass != null) { + final String libraryName = AbstractExceptions.libraryName(loadedClass); + final String version = AbstractExceptions.getVersion(Exceptions.packageLoader, loadedClass, AbstractExceptions.packageName(className)); + + if ((libraryName != null) || (version != null)) { + stringBuilder.append(" ["); + stringBuilder.append(libraryName); + if ((libraryName != null) && (version != null)) { + if (!libraryName.contains(version)) { + stringBuilder.append(":"); + stringBuilder.append(version); + } + } + stringBuilder.append("]"); + } } return stringBuilder.toString(); @@ -91,6 +126,10 @@ public String getUnknownSourceDefinition() { }); } + static PackageLoader packageLoader; + + static ClassLoader classLoader; + /** *

Registers a new root package. * @@ -182,6 +221,31 @@ public static void setIgnoreAllCauses(final boolean ignoreAllCauses) { AbstractExceptions.setIgnoreAllCauses(ignoreAllCauses); } + /** + *

Returns the value of print package information option. + * + * @return the value of global print package information option. When this option is true, stack trace elements + * printed or converted to string will include the name of the jar file that includes the printed class and the version + * of the jar. If it is false, none of this information is printed + */ + public static boolean isPrintPackageInformation() { + return AbstractExceptions.isPrintPackageInformation(); + } + + /** + *

Sets the value of print package information option. + * + *

When this option is true, stack trace elements printed or converted to string will include the name of the + * jar file that includes the printed class and the version of the jar. + * + *

Note that for some libraries extracting the jar file and the version may not be possible. + * + * @param printPackageInformation new print package information option. + */ + public static void setPrintPackageInformation(final boolean printPackageInformation) { + AbstractExceptions.setPrintPackageInformation(printPackageInformation); + } + /** *

Returns the smart stack trace for the given throwable. * @@ -238,6 +302,21 @@ public static String getStackTraceString(final Throwable throwable, final SetReturns the smart stack trace for the given throwable using packages provided. + * + * @param throwable parent throwable + * @param rootPackageSet root packages to use for building the stack trace + * @param groupPackageSet group packages to use for building the stack trace + * @param ignorePackageSet ignore packages to use for building the stack trace + * @param ignoreAllCauses ignore all causes in the exception chain + * @param printPackageInformation print package information + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable, final Set rootPackageSet, final Set groupPackageSet, final Set ignorePackageSet, final boolean ignoreAllCauses, final boolean printPackageInformation) { + return AbstractExceptions.getStackTraceString(throwable, rootPackageSet, groupPackageSet, ignorePackageSet, ignoreAllCauses, printPackageInformation); + } + /** *

Returns the smart stack trace for the given throwable using root package provided. * @@ -284,6 +363,19 @@ public static String getStackTraceString(final Throwable throwable, final int ma return AbstractExceptions.getStackTraceString(throwable, maxDepth, ignoreAllCauses); } + /** + *

Returns the smart stack trace for the given throwable using elements found until the maxDepth. + * + * @param throwable parent throwable + * @param maxDepth max depth in exception chain that will be used + * @param ignoreAllCauses ignore all causes in the exception chain + * @param printPackageInformation print package information + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable, final int maxDepth, final boolean ignoreAllCauses, final boolean printPackageInformation) { + return AbstractExceptions.getStackTraceString(throwable, maxDepth, ignoreAllCauses, printPackageInformation); + } + /** *

Returns all messages found in the exception chain of the throwable as a single string. * diff --git a/java/src/main/java/com/arthenica/smartexception/java/JavaClassLoader.java b/java/src/main/java/com/arthenica/smartexception/java/JavaClassLoader.java new file mode 100644 index 0000000..a01fc8c --- /dev/null +++ b/java/src/main/java/com/arthenica/smartexception/java/JavaClassLoader.java @@ -0,0 +1,64 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2021, Taner Sener + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.arthenica.smartexception.java; + +import com.arthenica.smartexception.ClassLoader; + +public class JavaClassLoader implements ClassLoader { + + @Override + public Class loadClass(final String className) { + try { + java.lang.ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + if (contextClassLoader != null) { + return contextClassLoader.loadClass(className); + } + } catch (ClassNotFoundException | SecurityException ignored) { + } + + try { + java.lang.ClassLoader systemClassLoader = java.lang.ClassLoader.getSystemClassLoader(); + if (systemClassLoader != null) { + return systemClassLoader.loadClass(className); + } + } catch (ClassNotFoundException | SecurityException | IllegalStateException | Error ignored) { + } + + try { + return Class.forName(className); + } catch (ClassNotFoundException | LinkageError ignored) { + return null; + } + } + +} diff --git a/java/src/main/java/com/arthenica/smartexception/java/JavaPackageLoader.java b/java/src/main/java/com/arthenica/smartexception/java/JavaPackageLoader.java new file mode 100644 index 0000000..7ba7624 --- /dev/null +++ b/java/src/main/java/com/arthenica/smartexception/java/JavaPackageLoader.java @@ -0,0 +1,45 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2021, Taner Sener + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.arthenica.smartexception.java; + +import com.arthenica.smartexception.AbstractExceptions; +import com.arthenica.smartexception.PackageLoader; + +public class JavaPackageLoader implements PackageLoader { + + @Override + public Package getPackage(final java.lang.ClassLoader classLoader, final String className) { + return Package.getPackage(AbstractExceptions.packageName(className)); + } + +} diff --git a/java/src/test/java/com/arthenica/smartexception/java/ApacheCxfTest.java b/java/src/test/java/com/arthenica/smartexception/java/ApacheCxfTest.java index 92aeb90..1443ebd 100644 --- a/java/src/test/java/com/arthenica/smartexception/java/ApacheCxfTest.java +++ b/java/src/test/java/com/arthenica/smartexception/java/ApacheCxfTest.java @@ -77,4 +77,36 @@ public void access() { } } + @Test + public void accessAndPrintPackageInformation() { + Client client = ClientBuilder.newBuilder().build(); + WebTarget target = client.target("http://localhost:12345/rs"); + target = target.path("service").queryParam("param1", "value1"); + + Invocation.Builder builder = target.request(); + try { + builder.get(); + } catch (Exception e) { + String expectedStackTrace = "\n" + + "javax.ws.rs.ProcessingException: java.net.ConnectException: ConnectException invoking http://localhost:12345/rs/service?param1=value1: Connection refused (Connection refused)\n" + + "\tat org.apache.cxf ... 10 more [cxf-rt-rs-client-3.3.6.jar]\n" + + "\tat com.arthenica.smartexception.java.ApacheCxfTest.accessAndPrintPackageInformation(ApacheCxfTest.java:88)\n" + + "Caused by: java.net.ConnectException: ConnectException invoking http://localhost:12345/rs/service?param1=value1: Connection refused (Connection refused)\n" + + "\tat jdk.internal.reflect ... 2 more\n" + + "\tat java.lang.reflect.Constructor.newInstance(Constructor.java:488)\n" + + "\tat org.apache.cxf ... 15 more [cxf-rt-transports-http-3.3.6.jar]\n" + + "\tat com.arthenica.smartexception.java.ApacheCxfTest.accessAndPrintPackageInformation(ApacheCxfTest.java:88)\n" + + "Caused by: java.net.ConnectException: Connection refused (Connection refused)\n" + + "\tat java.net ... 4 more\n" + + "\tat sun.net ... 11 more\n" + + "\tat java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:527)\n" + + "\tat org.apache.cxf ... 1 more [cxf-rt-transports-http-3.3.6.jar]\n" + + "\tat java.security.AccessController.doPrivileged(Native Method)\n" + + "\tat org.apache.cxf ... 18 more [cxf-rt-transports-http-3.3.6.jar]\n" + + "\tat com.arthenica.smartexception.java.ApacheCxfTest.accessAndPrintPackageInformation(ApacheCxfTest.java:88)"; + + Assert.assertEquals(ExceptionsTest.trimDynamicParts(expectedStackTrace), ExceptionsTest.trimDynamicParts(Exceptions.getStackTraceString(e, Collections.singleton("com.arthenica"), new HashSet<>(Arrays.asList("org.apache.cxf", "java.net", "java.security", "sun.net", "jdk.internal.reflect")), new HashSet(), false, true))); + } + } + } diff --git a/java/src/test/java/com/arthenica/smartexception/java/ExceptionsTest.java b/java/src/test/java/com/arthenica/smartexception/java/ExceptionsTest.java index c18cd3e..e74d7c9 100644 --- a/java/src/test/java/com/arthenica/smartexception/java/ExceptionsTest.java +++ b/java/src/test/java/com/arthenica/smartexception/java/ExceptionsTest.java @@ -1,7 +1,7 @@ /* * BSD 3-Clause License * - * Copyright (c) 2020, Taner Sener + * Copyright (c) 2020-2021, Taner Sener * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,6 +32,13 @@ package com.arthenica.smartexception.java; +import com.arthenica.smartexception.AbstractExceptions; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Hex; +import org.json.JSONArray; +import org.json.JSONException; import org.junit.Assert; import org.junit.Test; @@ -40,6 +47,7 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.security.DigestException; +import java.util.Arrays; import java.util.Collections; import java.util.ConcurrentModificationException; import java.util.HashSet; @@ -519,4 +527,72 @@ public String call() throws Exception { } } + @Test + public void packageInformationJunit() { + RuntimeException runtimeException = new RuntimeException("Fail!"); + + StackTraceElement stackTraceElement = Arrays.asList(AbstractExceptions.getStackTrace(runtimeException, 6)).get(5); + Assert.assertEquals("junit-4.13.jar", libraryName(stackTraceElement)); + Assert.assertEquals("4.13", version(stackTraceElement)); + } + + @Test + public void packageInformationApacheCommons() { + try { + Hex.decodeHex("12345".toCharArray()); + } catch (DecoderException exception) { + StackTraceElement[] stackTrace = AbstractExceptions.getStackTrace(exception, 10); + + StackTraceElement stackTraceElement = Arrays.asList(stackTrace).get(0); + Assert.assertEquals("commons-codec-1.10.jar", libraryName(stackTraceElement)); + Assert.assertEquals("1.10", version(stackTraceElement)); + } + } + + @Test + public void packageInformationJson() { + try { + new JSONArray(""); + } catch (JSONException exception) { + StackTraceElement[] stackTrace = AbstractExceptions.getStackTrace(exception, 10); + + StackTraceElement stackTraceElement = Arrays.asList(stackTrace).get(0); + Assert.assertEquals("android-json-0.0.20131108.vaadin1.jar", libraryName(stackTraceElement)); + Assert.assertEquals("0.0.20131108.vaadin1", version(stackTraceElement)); + } + } + + @Test + public void packageInformationJackson() { + try { + new ObjectMapper().readValue("", Long.class); + } catch (JsonProcessingException exception) { + StackTraceElement[] stackTrace = AbstractExceptions.getStackTrace(exception, 10); + + StackTraceElement stackTraceElement = Arrays.asList(stackTrace).get(0); + Assert.assertEquals("jackson-databind-2.10.3.jar", libraryName(stackTraceElement)); + Assert.assertEquals("2.10.3", version(stackTraceElement)); + } + } + + String libraryName(final StackTraceElement stackTraceElement) { + String className = stackTraceElement.getClassName(); + Class loadedClass = Exceptions.classLoader.loadClass(className); + if (loadedClass != null) { + return AbstractExceptions.libraryName(loadedClass); + } else { + return null; + } + } + + String version(final StackTraceElement stackTraceElement) { + String className = stackTraceElement.getClassName(); + Class loadedClass = Exceptions.classLoader.loadClass(className); + if (loadedClass != null) { + return AbstractExceptions.getVersion(Exceptions.packageLoader, loadedClass, AbstractExceptions.packageName(className)); + } else { + return null; + } + } + } diff --git a/java/src/test/java/com/arthenica/smartexception/java/SpringTest.java b/java/src/test/java/com/arthenica/smartexception/java/SpringTest.java index 58940d4..a1aefd3 100644 --- a/java/src/test/java/com/arthenica/smartexception/java/SpringTest.java +++ b/java/src/test/java/com/arthenica/smartexception/java/SpringTest.java @@ -115,4 +115,25 @@ public void putUserTest() { } } + @Test + public void getStackTraceWithMaxDepthAndPrintPackageInformation() { + try { + mvc.perform(MockMvcRequestBuilders.post("/user").content("Body")).andExpect(status().isOk()); + } catch (Exception e) { + String expectedStackTrace = "\n" + + "org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NoSuchMethodException: Method not found.\n" + + "\tat org.springframework ... 6 more [spring-webmvc-5.2.5.RELEASE.jar]\n" + + "\tat com.arthenica.smartexception.java.SpringTest.getStackTraceWithMaxDepthAndPrintPackageInformation(SpringTest.java:121)\n" + + "Caused by: java.lang.NoSuchMethodException: Method not found.\n" + + "\tat com.arthenica.smartexception.java.spring.RestController.update(RestController.java:56)\n" + + "\tat jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n" + + "\tat jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n" + + "\tat jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n" + + "\tat org.springframework ... 14 more [spring-web-5.2.5.RELEASE.jar]\n" + + "\tat com.arthenica.smartexception.java.SpringTest.getStackTraceWithMaxDepthAndPrintPackageInformation(SpringTest.java:121)"; + + Assert.assertEquals(ExceptionsTest.trimDynamicParts(expectedStackTrace), ExceptionsTest.trimDynamicParts(Exceptions.getStackTraceString(e, Collections.singleton("com.arthenica"), new HashSet(Collections.singleton("org.springframework")), new HashSet<>(Arrays.asList("sun.net", "sun.security", "sun.reflect", "java.lang.reflect", "javax.servlet.http")), false, true))); + } + } + } diff --git a/java9/src/main/java/com/arthenica/smartexception/java9/Exceptions.java b/java9/src/main/java/com/arthenica/smartexception/java9/Exceptions.java index 5e2a0ab..f19ecb7 100644 --- a/java9/src/main/java/com/arthenica/smartexception/java9/Exceptions.java +++ b/java9/src/main/java/com/arthenica/smartexception/java9/Exceptions.java @@ -1,7 +1,7 @@ /* * BSD 3-Clause License * - * Copyright (c) 2020, Taner Sener + * Copyright (c) 2020-2021, Taner Sener * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,6 +33,8 @@ package com.arthenica.smartexception.java9; import com.arthenica.smartexception.AbstractExceptions; +import com.arthenica.smartexception.ClassLoader; +import com.arthenica.smartexception.PackageLoader; import com.arthenica.smartexception.StackTraceElementSerializer; import java.util.Set; @@ -59,10 +61,13 @@ public class Exceptions { protected static boolean ignoreModuleName = DEFAULT_IGNORE_MODULE_NAME; static { + packageLoader = new Java9PackageLoader(); + classLoader = new Java9ClassLoader(); + AbstractExceptions.setStackTraceElementSerializer(new StackTraceElementSerializer() { @Override - public String toString(StackTraceElement stackTraceElement) { + public String toString(final StackTraceElement stackTraceElement, final boolean printPackageInformation) { final StringBuilder stringBuilder = new StringBuilder(); if (!ignoreModuleName && !AbstractExceptions.isEmpty(stackTraceElement.getModuleName())) { @@ -74,7 +79,7 @@ public String toString(StackTraceElement stackTraceElement) { stringBuilder.append(stackTraceElement.getMethodName()); if (stackTraceElement.isNativeMethod()) { - stringBuilder.append("(Native Method)"); + stringBuilder.append(getNativeMethodDefinition()); } else if (!AbstractExceptions.isEmpty(stackTraceElement.getFileName())) { stringBuilder.append("("); stringBuilder.append(stackTraceElement.getFileName()); @@ -84,7 +89,37 @@ public String toString(StackTraceElement stackTraceElement) { } stringBuilder.append(")"); } else { - stringBuilder.append("(Unknown Source)"); + stringBuilder.append(getUnknownSourceDefinition()); + } + + if (printPackageInformation) { + stringBuilder.append(getPackageInformation(stackTraceElement)); + } + + return stringBuilder.toString(); + } + + @Override + public String getPackageInformation(final StackTraceElement stackTraceElement) { + final StringBuilder stringBuilder = new StringBuilder(); + + String className = stackTraceElement.getClassName(); + Class loadedClass = Exceptions.classLoader.loadClass(className); + if (loadedClass != null) { + final String libraryName = AbstractExceptions.libraryName(loadedClass); + final String version = AbstractExceptions.getVersion(Exceptions.packageLoader, loadedClass, AbstractExceptions.packageName(className)); + + if ((libraryName != null) || (version != null)) { + stringBuilder.append(" ["); + stringBuilder.append(libraryName); + if ((libraryName != null) && (version != null)) { + if (!libraryName.contains(version)) { + stringBuilder.append(":"); + stringBuilder.append(version); + } + } + stringBuilder.append("]"); + } } return stringBuilder.toString(); @@ -107,6 +142,10 @@ public String getUnknownSourceDefinition() { }); } + static PackageLoader packageLoader; + + static ClassLoader classLoader; + /** *

Returns the value of ignore module name option. * @@ -216,6 +255,31 @@ public static void setIgnoreAllCauses(final boolean ignoreAllCauses) { AbstractExceptions.setIgnoreAllCauses(ignoreAllCauses); } + /** + *

Returns the value of print package information option. + * + * @return the value of global print package information option. When this option is true, stack trace elements + * printed or converted to string will include the name of the jar file that includes the printed class and the version + * of the jar. If it is false, none of this information is printed + */ + public static boolean isPrintPackageInformation() { + return AbstractExceptions.isPrintPackageInformation(); + } + + /** + *

Sets the value of print package information option. + * + *

When this option is true, stack trace elements printed or converted to string will include the name of the + * jar file that includes the printed class and the version of the jar. + * + *

Note that for some libraries extracting the jar file and the version may not be possible. + * + * @param printPackageInformation new print package information option. + */ + public static void setPrintPackageInformation(final boolean printPackageInformation) { + AbstractExceptions.setPrintPackageInformation(printPackageInformation); + } + /** *

Returns the smart stack trace for the given throwable. * @@ -272,6 +336,21 @@ public static String getStackTraceString(final Throwable throwable, final SetReturns the smart stack trace for the given throwable using packages provided. + * + * @param throwable parent throwable + * @param rootPackageSet root packages to use for building the stack trace + * @param groupPackageSet group packages to use for building the stack trace + * @param ignorePackageSet ignore packages to use for building the stack trace + * @param ignoreAllCauses ignore all causes in the exception chain + * @param printPackageInformation print package information + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable, final Set rootPackageSet, final Set groupPackageSet, final Set ignorePackageSet, final boolean ignoreAllCauses, final boolean printPackageInformation) { + return AbstractExceptions.getStackTraceString(throwable, rootPackageSet, groupPackageSet, ignorePackageSet, ignoreAllCauses, printPackageInformation); + } + /** *

Returns the smart stack trace for the given throwable using root package provided. * @@ -318,6 +397,19 @@ public static String getStackTraceString(final Throwable throwable, final int ma return AbstractExceptions.getStackTraceString(throwable, maxDepth, ignoreAllCauses); } + /** + *

Returns the smart stack trace for the given throwable using elements found until the maxDepth. + * + * @param throwable parent throwable + * @param maxDepth max depth in exception chain that will be used + * @param ignoreAllCauses ignore all causes in the exception chain + * @param printPackageInformation print package information + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable, final int maxDepth, final boolean ignoreAllCauses, final boolean printPackageInformation) { + return AbstractExceptions.getStackTraceString(throwable, maxDepth, ignoreAllCauses, printPackageInformation); + } + /** *

Returns all messages found in the exception chain of the throwable as a single string. * diff --git a/java9/src/main/java/com/arthenica/smartexception/java9/Java9ClassLoader.java b/java9/src/main/java/com/arthenica/smartexception/java9/Java9ClassLoader.java new file mode 100644 index 0000000..6f5cd7c --- /dev/null +++ b/java9/src/main/java/com/arthenica/smartexception/java9/Java9ClassLoader.java @@ -0,0 +1,72 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2021, Taner Sener + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.arthenica.smartexception.java9; + +import com.arthenica.smartexception.ClassLoader; + +public class Java9ClassLoader implements ClassLoader { + + @Override + public Class loadClass(final String className) { + try { + java.lang.ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + if (contextClassLoader != null) { + return contextClassLoader.loadClass(className); + } + } catch (ClassNotFoundException | SecurityException ignored) { + } + + try { + java.lang.ClassLoader platformClassLoader = java.lang.ClassLoader.getPlatformClassLoader(); + if (platformClassLoader != null) { + return platformClassLoader.loadClass(className); + } + } catch (ClassNotFoundException | SecurityException ignored) { + } + + try { + java.lang.ClassLoader systemClassLoader = java.lang.ClassLoader.getSystemClassLoader(); + if (systemClassLoader != null) { + return systemClassLoader.loadClass(className); + } + } catch (ClassNotFoundException | SecurityException | IllegalStateException | Error ignored) { + } + + try { + return Class.forName(className); + } catch (ClassNotFoundException | LinkageError ignored) { + return null; + } + } + +} diff --git a/java9/src/main/java/com/arthenica/smartexception/java9/Java9PackageLoader.java b/java9/src/main/java/com/arthenica/smartexception/java9/Java9PackageLoader.java new file mode 100644 index 0000000..77fadda --- /dev/null +++ b/java9/src/main/java/com/arthenica/smartexception/java9/Java9PackageLoader.java @@ -0,0 +1,45 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2021, Taner Sener + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.arthenica.smartexception.java9; + +import com.arthenica.smartexception.AbstractExceptions; +import com.arthenica.smartexception.PackageLoader; + +public class Java9PackageLoader implements PackageLoader { + + @Override + public Package getPackage(final java.lang.ClassLoader classLoader, final String className) { + return classLoader.getDefinedPackage(AbstractExceptions.packageName(className)); + } + +} diff --git a/java9/src/test/java/com/arthenica/smartexception/java9/ApacheCxfTest.java b/java9/src/test/java/com/arthenica/smartexception/java9/ApacheCxfTest.java index e4d94f1..d91c9f6 100644 --- a/java9/src/test/java/com/arthenica/smartexception/java9/ApacheCxfTest.java +++ b/java9/src/test/java/com/arthenica/smartexception/java9/ApacheCxfTest.java @@ -77,4 +77,36 @@ public void access() { } } + @Test + public void accessAndPrintPackageInformation() { + Client client = ClientBuilder.newBuilder().build(); + WebTarget target = client.target("http://localhost:12345/rs"); + target = target.path("service").queryParam("param1", "value1"); + + Invocation.Builder builder = target.request(); + try { + builder.get(); + } catch (Exception e) { + String expectedStackTrace = "\n" + + "javax.ws.rs.ProcessingException: java.net.ConnectException: ConnectException invoking http://localhost:12345/rs/service?param1=value1: Connection refused (Connection refused)\n" + + "\tat org.apache.cxf ... 10 more [cxf-rt-rs-client-3.3.6.jar]\n" + + "\tat com.arthenica.smartexception.java9.ApacheCxfTest.accessAndPrintPackageInformation(ApacheCxfTest.java:88)\n" + + "Caused by: java.net.ConnectException: ConnectException invoking http://localhost:12345/rs/service?param1=value1: Connection refused (Connection refused)\n" + + "\tat jdk.internal.reflect ... 2 more\n" + + "\tat java.lang.reflect.Constructor.newInstance(Constructor.java:488)\n" + + "\tat org.apache.cxf ... 15 more [cxf-rt-transports-http-3.3.6.jar]\n" + + "\tat com.arthenica.smartexception.java9.ApacheCxfTest.accessAndPrintPackageInformation(ApacheCxfTest.java:88)\n" + + "Caused by: java.net.ConnectException: Connection refused (Connection refused)\n" + + "\tat java.net ... 4 more\n" + + "\tat sun.net ... 11 more\n" + + "\tat java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:527)\n" + + "\tat org.apache.cxf ... 1 more [cxf-rt-transports-http-3.3.6.jar]\n" + + "\tat java.security.AccessController.doPrivileged(Native Method)\n" + + "\tat org.apache.cxf ... 18 more [cxf-rt-transports-http-3.3.6.jar]\n" + + "\tat com.arthenica.smartexception.java9.ApacheCxfTest.accessAndPrintPackageInformation(ApacheCxfTest.java:88)"; + + Assert.assertEquals(ExceptionsTest.trimDynamicParts(expectedStackTrace), ExceptionsTest.trimDynamicParts(Exceptions.getStackTraceString(e, Collections.singleton("com.arthenica"), new HashSet<>(Arrays.asList("org.apache.cxf", "java.net", "java.security", "sun.net", "jdk.internal.reflect")), new HashSet(), false, true))); + } + } + } diff --git a/java9/src/test/java/com/arthenica/smartexception/java9/ExceptionsTest.java b/java9/src/test/java/com/arthenica/smartexception/java9/ExceptionsTest.java index 0e6cb69..0b694c4 100644 --- a/java9/src/test/java/com/arthenica/smartexception/java9/ExceptionsTest.java +++ b/java9/src/test/java/com/arthenica/smartexception/java9/ExceptionsTest.java @@ -1,7 +1,7 @@ /* * BSD 3-Clause License * - * Copyright (c) 2020, Taner Sener + * Copyright (c) 2020-2021, Taner Sener * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,6 +32,13 @@ package com.arthenica.smartexception.java9; +import com.arthenica.smartexception.AbstractExceptions; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Hex; +import org.json.JSONArray; +import org.json.JSONException; import org.junit.Assert; import org.junit.Test; @@ -40,6 +47,7 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.security.DigestException; +import java.util.Arrays; import java.util.Collections; import java.util.ConcurrentModificationException; import java.util.concurrent.Callable; @@ -473,4 +481,73 @@ public void searchCauseWithCauseClassAndCauseMessageAndMaxDepth() { } } + + @Test + public void packageInformationJunit() { + RuntimeException runtimeException = new RuntimeException("Fail!"); + + StackTraceElement stackTraceElement = Arrays.asList(AbstractExceptions.getStackTrace(runtimeException, 6)).get(5); + Assert.assertEquals("junit-4.13.jar", libraryName(stackTraceElement)); + Assert.assertEquals("4.13", version(stackTraceElement)); + } + + @Test + public void packageInformationApacheCommons() { + try { + Hex.decodeHex("12345".toCharArray()); + } catch (DecoderException exception) { + StackTraceElement[] stackTrace = AbstractExceptions.getStackTrace(exception, 10); + + StackTraceElement stackTraceElement = Arrays.asList(stackTrace).get(0); + Assert.assertEquals("commons-codec-1.10.jar", libraryName(stackTraceElement)); + Assert.assertEquals("1.10", version(stackTraceElement)); + } + } + + @Test + public void packageInformationJson() { + try { + new JSONArray(""); + } catch (JSONException exception) { + StackTraceElement[] stackTrace = AbstractExceptions.getStackTrace(exception, 10); + + StackTraceElement stackTraceElement = Arrays.asList(stackTrace).get(0); + Assert.assertEquals("android-json-0.0.20131108.vaadin1.jar", libraryName(stackTraceElement)); + Assert.assertEquals("0.0.20131108.vaadin1", version(stackTraceElement)); + } + } + + @Test + public void packageInformationJackson() { + try { + new ObjectMapper().readValue("", Long.class); + } catch (JsonProcessingException exception) { + StackTraceElement[] stackTrace = AbstractExceptions.getStackTrace(exception, 10); + + StackTraceElement stackTraceElement = Arrays.asList(stackTrace).get(0); + Assert.assertEquals("jackson-databind-2.10.3.jar", libraryName(stackTraceElement)); + Assert.assertEquals("2.10.3", version(stackTraceElement)); + } + } + + String libraryName(final StackTraceElement stackTraceElement) { + String className = stackTraceElement.getClassName(); + Class loadedClass = Exceptions.classLoader.loadClass(className); + if (loadedClass != null) { + return AbstractExceptions.libraryName(loadedClass); + } else { + return null; + } + } + + String version(final StackTraceElement stackTraceElement) { + String className = stackTraceElement.getClassName(); + Class loadedClass = Exceptions.classLoader.loadClass(className); + if (loadedClass != null) { + return AbstractExceptions.getVersion(Exceptions.packageLoader, loadedClass, AbstractExceptions.packageName(className)); + } else { + return null; + } + } + } diff --git a/java9/src/test/java/com/arthenica/smartexception/java9/SpringTest.java b/java9/src/test/java/com/arthenica/smartexception/java9/SpringTest.java index 101287a..94f82f7 100644 --- a/java9/src/test/java/com/arthenica/smartexception/java9/SpringTest.java +++ b/java9/src/test/java/com/arthenica/smartexception/java9/SpringTest.java @@ -118,4 +118,25 @@ public void putUserTest() { } } + @Test + public void getStackTraceWithMaxDepthAndPrintPackageInformation() { + try { + mvc.perform(MockMvcRequestBuilders.post("/user").content("Body")).andExpect(status().isOk()); + } catch (Exception e) { + String expectedStackTrace = "\n" + + "org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NoSuchMethodException: Method not found.\n" + + "\tat org.springframework ... 6 more [spring-webmvc-5.2.5.RELEASE.jar]\n" + + "\tat com.arthenica.smartexception.java9.SpringTest.getStackTraceWithMaxDepthAndPrintPackageInformation(SpringTest.java:121)\n" + + "Caused by: java.lang.NoSuchMethodException: Method not found.\n" + + "\tat com.arthenica.smartexception.java9.spring.RestController.update(RestController.java:56)\n" + + "\tat jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n" + + "\tat jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n" + + "\tat jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n" + + "\tat org.springframework ... 14 more [spring-web-5.2.5.RELEASE.jar]\n" + + "\tat com.arthenica.smartexception.java9.SpringTest.getStackTraceWithMaxDepthAndPrintPackageInformation(SpringTest.java:121)"; + + Assert.assertEquals(ExceptionsTest.trimDynamicParts(expectedStackTrace), ExceptionsTest.trimDynamicParts(Exceptions.getStackTraceString(e, Collections.singleton("com.arthenica"), new HashSet(Collections.singleton("org.springframework")), new HashSet<>(Arrays.asList("sun.net", "sun.security", "sun.reflect", "java.lang.reflect", "javax.servlet.http")), false, true))); + } + } + } From 4f0734a63cb6318c3bda3b24813997186d2c1180 Mon Sep 17 00:00:00 2001 From: Taner Sener Date: Sun, 28 Nov 2021 23:56:57 +0000 Subject: [PATCH 5/5] update version information --- build.gradle | 8 +++++--- java/build.gradle | 3 ++- java9/build.gradle | 3 ++- test/java/build.gradle | 2 +- test/java9/build.gradle | 2 +- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 4f649ad..8f78426 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ subprojects { apply plugin: 'java-library' group = 'com.arthenica' - version = '0.1.0' + version = '0.1.1' task sourcesJar(type: Jar) { archiveClassifier.set('sources') @@ -20,7 +20,8 @@ subprojects { 'Built-By': System.getProperty('user.name'), 'Build-Timestamp': new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ").format(new Date()), 'Created-By': "Gradle ${gradle.gradleVersion}", - 'Build-Jdk': System.getProperty('java.version') + 'Build-Jdk': System.getProperty('java.version'), + 'Implementation-Version': version ) } } @@ -32,7 +33,8 @@ subprojects { 'Built-By': System.getProperty('user.name'), 'Build-Timestamp': new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ").format(new Date()), 'Created-By': "Gradle ${gradle.gradleVersion}", - 'Build-Jdk': System.getProperty('java.version') + 'Build-Jdk': System.getProperty('java.version'), + 'Implementation-Version': version ) } } diff --git a/java/build.gradle b/java/build.gradle index 84cb08e..67a099c 100644 --- a/java/build.gradle +++ b/java/build.gradle @@ -15,7 +15,8 @@ task javadocJar(type: Jar, dependsOn: javadoc) { 'Built-By': System.getProperty('user.name'), 'Build-Timestamp': new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ").format(new Date()), 'Created-By': "Gradle ${gradle.gradleVersion}", - 'Build-Jdk': System.getProperty('java.version') + 'Build-Jdk': System.getProperty('java.version'), + 'Implementation-Version': version ) } } diff --git a/java9/build.gradle b/java9/build.gradle index 0f63dd7..e2d4830 100644 --- a/java9/build.gradle +++ b/java9/build.gradle @@ -15,7 +15,8 @@ task javadocJar(type: Jar, dependsOn: javadoc) { 'Built-By': System.getProperty('user.name'), 'Build-Timestamp': new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ").format(new Date()), 'Created-By': "Gradle ${gradle.gradleVersion}", - 'Build-Jdk': System.getProperty('java.version') + 'Build-Jdk': System.getProperty('java.version'), + 'Implementation-Version': version ) } } diff --git a/test/java/build.gradle b/test/java/build.gradle index 6703ff3..6c78304 100644 --- a/test/java/build.gradle +++ b/test/java/build.gradle @@ -2,5 +2,5 @@ sourceCompatibility = JavaVersion.VERSION_1_7 targetCompatibility = JavaVersion.VERSION_1_7 dependencies { - implementation 'com.arthenica:smart-exception-java:0.1.0' + implementation 'com.arthenica:smart-exception-java:0.1.1' } diff --git a/test/java9/build.gradle b/test/java9/build.gradle index 18809d3..e61f6ce 100644 --- a/test/java9/build.gradle +++ b/test/java9/build.gradle @@ -2,5 +2,5 @@ sourceCompatibility = JavaVersion.VERSION_1_9 targetCompatibility = JavaVersion.VERSION_1_9 dependencies { - implementation 'com.arthenica:smart-exception-java9:0.1.0' + implementation 'com.arthenica:smart-exception-java9:0.1.1' }