How to read value of java annotation with JDK8 and JDK11?
import io.cucumber.java.en.When;
public class Sof {
private static final Logger log = LoggerFactory.getLogger(Sof.class);
#When(value = "I update text {string} with {string}(\\?)")
public static void main(String[] args) {
Class c = Sof.class;
Method[] methods = c.getMethods();
Method method = null;
for (Method m : methods) {
if (m.getName().equals("main")) {
method = m;
}
}
Annotation stepAnnotation = method.getAnnotation(When.class);
Object as[] = { "a", "b" };
Matcher matcher = Pattern.compile("value=(.*)\\)").matcher(stepAnnotation.toString());
if (matcher.find()) {
log.info("---> " + stepAnnotation.annotationType().getSimpleName() + " " + String.format(matcher.group(1).replaceAll("\\{\\S+\\}", "{%s}").replace("(\\?)", ""), as));
} else {
System.err.println("error");
}
}
}
/!\ in reality, I do not know the type of annotation #When. this can be any of the interfaces in the io.cucumber.java package
result JDK8:
---> When I update text {a} with {b}
result JDK11 (extra quote): (stepAnnotation.toString() is different!)
---> When "I update text {a} with {b}"
EDIT openjdk11 and oraclejdk11 do not respect javadoc:
/**
* Returns a string representation of this annotation. The details
* of the representation are implementation-dependent, but the following
* may be regarded as typical:
* <pre>
* #com.acme.util.Name(first=Alfred, middle=E., last=Neuman)
* </pre>
*
* #return a string representation of this annotation
*/
String toString();
You should not depend on the toString() implementation which is normally for debugging/logging only.
See Is it possible to read the value of a annotation in java? for more details on how to read the value of an annotation.
UPDATE:
To do everything via reflection, you can so something like this:
import org.springframework.transaction.annotation.Transactional;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
public class AnnotationTest {
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException {
Method[] methods = AnnotationTest.class.getMethods();
System.out.println("methods = " + Arrays.toString(methods));
for (Method method : methods) {
System.out.println("method = " + method);
Annotation[] annotations = method.getAnnotations();
System.out.println("annotations = " + Arrays.toString(annotations));
for (Annotation annotation : annotations) {
System.out.println("annotation = " + annotation);
Class<? extends Annotation> annotationClass = annotation.annotationType();
System.out.println("annotationClass = " + annotationClass);
Method[] annotationMethods = annotationClass.getMethods();
System.out.println("annotation methods = " + Arrays.toString(annotationMethods));
for (Method annotationMethod : annotationMethods) {
if (Modifier.isPublic(annotationMethod.getModifiers())) {
String name = annotationMethod.getName();
Object o = annotationMethod.invoke(annotation);
System.out.println(name + ": " + o);
}
}
}
}
}
#Transactional("bla")
public void test() {
}
}
(I used one of Spring's annotations here since that is what I happen to have on my classpath)
UPDATE (with end of solution):
#When(value = "I update text {string} with {string}(\\?)")
public static void main(String[] args) {
Object as[] = { "a", "b" };
Class c = Sof.class;
Method[] methods = c.getMethods();
Method method = null;
for (Method m : methods) {
if (m.getName().equals("main")) {
method = m;
}
}
Annotation stepAnnotation = method.getAnnotation(When.class);
Class<? extends Annotation> annotationClass = stepAnnotation.annotationType();
try {
Method valueMethods = annotationClass.getDeclaredMethod("value");
if (Modifier.isPublic(valueMethods.getModifiers())) {
log.info("---> {} " + String.format(valueMethods.invoke(stepAnnotation).toString().replaceAll("\\{\\S+\\}", "{%s}").replace("(\\?)", ""), as),
stepAnnotation.annotationType().getSimpleName());
}
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e1) {
e1.printStackTrace();
}
}
Related
I want to build a Java agent with Byte Buddy, which intercepts JDBC connection creation and closing event. I want to support data sources such as HikariCP, DBCP, etc, also plain JDBC with RDBMS drivers.
My code:
Instrumention setup
new AgentBuilder.Default()
.type(startMatcher.and(isSubTypeOf(java.sql.Connection.class).and(not(isAbstract())))
.transform(constructorTransformer).transform(methodsTransformer).with(listener).installOn(inst);
Intercepting code
package org.wxt.xtools.agents.jdbcmon;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import org.wxt.xtools.agents.utils.StringUtils;
import org.wxt.xtools.agents.utils.TopN;
import ch.qos.logback.classic.Logger;
import io.prometheus.client.Histogram;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.This;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
/**
*
* #author ggfan
*
*/
public class JDBCAPIInterceptor {
// ***ATTENTION*** fields below must be public, as we will access them from the
// intercepting methods
public static Map<Integer, ConnectionInfo> conns = new HashMap<Integer, ConnectionInfo>();
public static Map<String, String> stacks = new HashMap<String, String>();
public static Map<String, Integer> counstsByStack = new HashMap<String, Integer>();
public static TopN<StatementExecutionInfo> slowStatements = new TopN<StatementExecutionInfo>(10, new Comparator<StatementExecutionInfo>() {
#Override
public int compare(StatementExecutionInfo o1, StatementExecutionInfo o2) {
long d1 = o1.getExecutionEndTime() - o1.getExecutionStartTime();
long d2 = o2.getExecutionEndTime() - o2.getExecutionStartTime();
return Long.compare(d1, d2);
}
});
public static Logger log = JDBCMonitor.log;
public static JDBCStatsReport getInfo() {
JDBCStatsReport report = new JDBCStatsReport();
List<ConnectionInfo> data = new ArrayList<ConnectionInfo>();
for (ConnectionInfo ci : conns.values()) {
ConnectionInfo copy = new ConnectionInfo();
copy.setHash(ci.getHash());
copy.setStackHash(ci.getStackHash() + "(" + counstsByStack.get(ci.getStackHash()) + ")");
copy.setCreationTime(ci.getCreationTime());
copy.setLastActiveTime(ci.getLastActiveTime());
copy.setLastMethod(ci.getLastMethod());
copy.setCurrentStatement(ci.getCurrentStatement());
data.add(copy);
}
report.setConnectionInfo(data);
report.setSlowStatementTopN(slowStatements);
return report;
}
#RuntimeType
public static Object interceptor(#Origin Class<?> clazz, #Origin Method method, #SuperCall Callable<?> callable,
#net.bytebuddy.implementation.bind.annotation.This Object inst, #AllArguments Object[] args)
throws Exception {
// this is equal to original method, will not cause inner calls to other
// matching methods get intercepted.
// Object o = callable.call();
log.debug("intercepting {}#{}", clazz.getName(), method.getName());
int hashCode = System.identityHashCode(inst);
if (java.sql.Connection.class.isAssignableFrom(clazz)) {
ConnectionInfo ci = conns.get(hashCode);
// TODO fix bug here!!!
if (ci == null) {
log.debug("connection#{} is not in records anymore, maybe called #{} after close, ignored", hashCode,
method.getName());
}
return interceptConnectionMethods(method, hashCode, ci, callable, args);
} else if (java.sql.Statement.class.isAssignableFrom(clazz)) {
return interceptStatementMethods(method, hashCode, callable, args);
} else if (java.sql.PreparedStatement.class.isAssignableFrom(clazz)) {
return interceptStatementMethods(method, hashCode, callable, args);
} else if (java.sql.CallableStatement.class.isAssignableFrom(clazz)) {
return interceptStatementMethods(method, hashCode, callable, args);
}
return null;
}
private static Object interceptConnectionMethods(Method method, int hashCode, ConnectionInfo ci,
Callable<?> callable, Object[] args) throws Exception {
Object o = callable.call();
log.debug("connection#{} used by {}", hashCode, method.getName());
ci.setLastActiveTime(System.currentTimeMillis());
ci.setLastMethod(method.getName());
int resultHash = System.identityHashCode(o);
if (method.getName().equals("close")) {
log.info("connection#{} released", hashCode);
String stackHash = ci.getStackHash();
Integer scount = counstsByStack.get(stackHash);
if (scount != null && scount > 0) {
int newscount = scount - 1;
log.info("set connection count to {} by stack hash {}", newscount, stackHash);
if (newscount == 0) {
counstsByStack.remove(stackHash);
stacks.remove(stackHash);
} else {
counstsByStack.put(stackHash, newscount);
}
} else {
log.error("connection count by stack hash {} is not supposed to be null or less than zero", stackHash);
}
conns.remove(hashCode);
} else if (method.getName().equals("createStatement")) {
StatementExecutionInfo stmt = new StatementExecutionInfo();
stmt.setType(StatementType.NORMAL);
stmt.setHash(resultHash);
conns.get(hashCode).setCurrentStatement(stmt);
log.info("statement#{} created, type {}, attached to connection#{}", resultHash, StatementType.NORMAL, hashCode);
} else if (method.getName().equals("prepareStatement")) {
StatementExecutionInfo stmt = new StatementExecutionInfo();
stmt.setType(StatementType.PREPARED);
stmt.setSqlText((String) args[0]);
stmt.setHash(resultHash);
conns.get(hashCode).setCurrentStatement(stmt);
log.info("statement#{} created, type {}, attached to connection#{}", resultHash, StatementType.PREPARED, hashCode);
} else if (method.getName().equals("prepareCall")) {
StatementExecutionInfo stmt = new StatementExecutionInfo();
stmt.setType(StatementType.CALLABLE);
stmt.setSqlText((String) args[0]);
stmt.setHash(resultHash);
conns.get(hashCode).setCurrentStatement(stmt);
log.info("statement#{} created, type {}, attached to connection#{}", resultHash, StatementType.CALLABLE, hashCode);
}
return o;
}
private static Object interceptStatementMethods(Method method, int hashCode, Callable<?> callable, Object[] args)
throws Exception {
log.info("intercepting statement method {}", method.getName());
Object o = null;
ConnectionInfo containingCI = conns.values().stream().filter(c -> c.getCurrentStatement().getHash() == hashCode)
.findFirst().orElse(null);
if (containingCI == null) {
log.warn("unexpected situation happened: statement can't found containing connection!");
}
if (method.getName().equals("close")) {
o = callable.call();
// TODO statement close method not intercepted ??
if (containingCI != null) {
containingCI.setCurrentStatement(null);
log.info("statement#{} closed, detached from connection#{}", hashCode, containingCI.getHash());
}
}
// all statement execution method trigger
else if (method.getName().startsWith("execute")) {
String stack = getCurrentThreadStackTrace();
String stackHash = StringUtils.md5(stack);
stacks.put(stackHash, stack);
log.info("statement#{} {} started by {}", hashCode, method.getName(), stackHash);
long est = System.currentTimeMillis();
if (containingCI != null) {
containingCI.getCurrentStatement().setExecutionStartTime(est);
containingCI.getCurrentStatement().setStackHash(stackHash);
containingCI.getCurrentStatement().setExecutionCallStack(stack);
if (args.length > 0) {
containingCI.getCurrentStatement().setSqlText((String) args[0]);
}
}
Histogram.Timer timer = JDBCMetrics.SQL_EXECUTION_TIME.startTimer();
try {
o = callable.call();
} finally {
timer.observeDuration();
}
long eet = System.currentTimeMillis();
log.info("statement#{} {} finished, duration is {}", hashCode, method.getName(), (eet - est));
if (containingCI != null) {
containingCI.getCurrentStatement().setExecutionEndTime(eet);
slowStatements.add(containingCI.getCurrentStatement());
}
}
return o;
}
public static String getCurrentThreadStackTrace() throws IOException {
StringWriter sw = new StringWriter();
Throwable t = new Throwable("");
t.printStackTrace(new PrintWriter(sw));
String stackTrace = sw.toString();
sw.close();
return stackTrace;
}
#Advice.OnMethodExit
public static void intercept(#net.bytebuddy.asm.Advice.Origin Constructor<?> m, #This Object inst)
throws Exception {
log.debug("----------------- constructor intercept -------------------------");
if (java.sql.Connection.class.isAssignableFrom(m.getDeclaringClass())) {
// some CP library override hashCode method
int hashCode = System.identityHashCode(inst);
ConnectionInfo ci = new ConnectionInfo();
ci.setHash(hashCode);
ci.setCreationTime(System.currentTimeMillis());
String stackTrace = getCurrentThreadStackTrace();
String shash = StringUtils.md5(stackTrace);
stacks.put(shash, stackTrace);
// ci.setStack(stackTrace);
ci.setStackHash(shash);
log.info("connection#{} acquired by stack#{}", hashCode, shash);
log.debug(stackTrace);
conns.put(hashCode, ci);
Integer scount = counstsByStack.get(ci.getStackHash());
if (scount == null) {
counstsByStack.put(ci.getStackHash(), 1);
} else {
counstsByStack.put(ci.getStackHash(), scount + 1);
}
}
}
}
However, this will not work for some cases. Take HikariCP for example:
HikariCP's implementation has an abstract class ProxyConnection, which has the close method implemented, then a class HikariProxyConnection extends ProxyConnection, which overides some methods, except close. If I setup instrumentation on HikariProxyConnection, the close method will not be intercepted. If I change my code to:
new AgentBuilder.Default()
.type(startMatcher.and(isSubTypeOf(java.sql.Connection.class).and(isAbstract()))
.transform(constructorTransformer).transform(methodsTransformer).with(listener).installOn(inst);
it will work for HikariCP, but not for other connection pool implementations.
For my use case, is there a unified way? No matter what connection pool is used, and even with plain JDBC.
Your matcher:
isSubTypeOf(java.sql.Connection.class).and(not(isAbstract())
explicitly excludes abstract classes. You would need to instrument all methods to achieve what you wanted if you are using Advice.
Also, it seems like you are blending concepts:
import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.This;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
The latter annotations belong to MethodDelegation, not to Advice. Refer to the javadoc to see how advice should be applied.
I'd like to find the methods which changed between two arbitrary Java files.
What I've Tried
I've tried using diff (GNU diffutils 3.3) to find changes to lines in the file and diff --show-c-function connect those lines to the changed method. Unfortunately, in Java this lists the class, not the function.
I also tried git diff which seems to properly be able to find the changed function (at least as displayed on GitHub), but it doesn't always list the full signature and my files are not in the same Git repository (https://github.com/apache/commons-math/commit/34adc606601cb578486d4a019b4655c5aff607b5).
Desired Results
Input:
~/repos/d4jBugs/Math_54_buggy/src/main/java/org/apache/commons/math/dfp/Dfp.java
~/repos/d4jBugs/Math_54_fixed/src/main/java/org/apache/commons/math/dfp/Dfp.java
State of Files:
The changed methods between those two files are public double toDouble() and protected Dfp(final DfpField field, double x)
Output: (fully qualified names)
org.apache.commons.math.dfp.Dfp.toDouble()
org.apache.commons.math.dfp.Dfp(final DfpField field, double x)
Summary
Can I find the modified methods with the GNU diffutils tool or git diff and if yes, how would I do that? (Note: I'm not bound to those tools and am happy to install something else if needed.)
I used JavaParser 3.4.4, but it probably works but has not been tested with other versions.
It can be imported in Gradle with:
compile group: 'com.github.javaparser', name: 'javaparser-core', version: '3.4.4'
You can use my class like:
HashSet<String> changedMethods = MethodDiff.methodDiffInClass(
oldFileNameWithPath,
newFileNameWithPath
);
MethodDiff Source:
import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.CallableDeclaration;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.ConstructorDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.comments.Comment;
import com.github.javaparser.printer.PrettyPrinterConfiguration;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
/**
* Created by Loren Klingman on 10/19/17.
* Finds Changes Between Methods of Two Java Source Files
*/
public class MethodDiff {
private static PrettyPrinterConfiguration ppc = null;
class ClassPair {
final ClassOrInterfaceDeclaration clazz;
final String name;
ClassPair(ClassOrInterfaceDeclaration c, String n) {
clazz = c;
name = n;
}
}
public static PrettyPrinterConfiguration getPPC() {
if (ppc != null) {
return ppc;
}
PrettyPrinterConfiguration localPpc = new PrettyPrinterConfiguration();
localPpc.setColumnAlignFirstMethodChain(false);
localPpc.setColumnAlignParameters(false);
localPpc.setEndOfLineCharacter("");
localPpc.setIndent("");
localPpc.setPrintComments(false);
localPpc.setPrintJavadoc(false);
ppc = localPpc;
return ppc;
}
public static <N extends Node> List<N> getChildNodesNotInClass(Node n, Class<N> clazz) {
List<N> nodes = new ArrayList<>();
for (Node child : n.getChildNodes()) {
if (child instanceof ClassOrInterfaceDeclaration) {
// Don't go into a nested class
continue;
}
if (clazz.isInstance(child)) {
nodes.add(clazz.cast(child));
}
nodes.addAll(getChildNodesNotInClass(child, clazz));
}
return nodes;
}
private List<ClassPair> getClasses(Node n, String parents, boolean inMethod) {
List<ClassPair> pairList = new ArrayList<>();
for (Node child : n.getChildNodes()) {
if (child instanceof ClassOrInterfaceDeclaration) {
ClassOrInterfaceDeclaration c = (ClassOrInterfaceDeclaration)child;
String cName = parents+c.getNameAsString();
if (inMethod) {
System.out.println(
"WARNING: Class "+cName+" is located inside a method. We cannot predict its name at"
+ " compile time so it will not be diffed."
);
} else {
pairList.add(new ClassPair(c, cName));
pairList.addAll(getClasses(c, cName + "$", inMethod));
}
} else if (child instanceof MethodDeclaration || child instanceof ConstructorDeclaration) {
pairList.addAll(getClasses(child, parents, true));
} else {
pairList.addAll(getClasses(child, parents, inMethod));
}
}
return pairList;
}
private List<ClassPair> getClasses(String file) {
try {
CompilationUnit cu = JavaParser.parse(new File(file));
return getClasses(cu, "", false);
} catch (FileNotFoundException f) {
throw new RuntimeException("EXCEPTION: Could not find file: "+file);
}
}
public static String getSignature(String className, CallableDeclaration m) {
return className+"."+m.getSignature().asString();
}
public static HashSet<String> methodDiffInClass(String file1, String file2) {
HashSet<String> changedMethods = new HashSet<>();
HashMap<String, String> methods = new HashMap<>();
MethodDiff md = new MethodDiff();
// Load all the method and constructor values into a Hashmap from File1
List<ClassPair> cList = md.getClasses(file1);
for (ClassPair c : cList) {
List<ConstructorDeclaration> conList = getChildNodesNotInClass(c.clazz, ConstructorDeclaration.class);
List<MethodDeclaration> mList = getChildNodesNotInClass(c.clazz, MethodDeclaration.class);
for (MethodDeclaration m : mList) {
String methodSignature = getSignature(c.name, m);
if (m.getBody().isPresent()) {
methods.put(methodSignature, m.getBody().get().toString(getPPC()));
} else {
System.out.println("Warning: No Body for "+file1+" "+methodSignature);
}
}
for (ConstructorDeclaration con : conList) {
String methodSignature = getSignature(c.name, con);
methods.put(methodSignature, con.getBody().toString(getPPC()));
}
}
// Compare everything in file2 to what is in file1 and log any differences
cList = md.getClasses(file2);
for (ClassPair c : cList) {
List<ConstructorDeclaration> conList = getChildNodesNotInClass(c.clazz, ConstructorDeclaration.class);
List<MethodDeclaration> mList = getChildNodesNotInClass(c.clazz, MethodDeclaration.class);
for (MethodDeclaration m : mList) {
String methodSignature = getSignature(c.name, m);
if (m.getBody().isPresent()) {
String body1 = methods.remove(methodSignature);
String body2 = m.getBody().get().toString(getPPC());
if (body1 == null || !body1.equals(body2)) {
// Javassist doesn't add spaces for methods with 2+ parameters...
changedMethods.add(methodSignature.replace(" ", ""));
}
} else {
System.out.println("Warning: No Body for "+file2+" "+methodSignature);
}
}
for (ConstructorDeclaration con : conList) {
String methodSignature = getSignature(c.name, con);
String body1 = methods.remove(methodSignature);
String body2 = con.getBody().toString(getPPC());
if (body1 == null || !body1.equals(body2)) {
// Javassist doesn't add spaces for methods with 2+ parameters...
changedMethods.add(methodSignature.replace(" ", ""));
}
}
// Anything left in methods was only in the first set and so is "changed"
for (String method : methods.keySet()) {
// Javassist doesn't add spaces for methods with 2+ parameters...
changedMethods.add(method.replace(" ", ""));
}
}
return changedMethods;
}
private static void removeComments(Node node) {
for (Comment child : node.getAllContainedComments()) {
child.remove();
}
}
}
I'm having this weird issue where a class from some transitive dependency keeps showing up at runtime, shadowing a newer version of the class from the (correct) first level dependency, even though I thought I made sure that I excluded the older version from all other dependencies I declare (this is in a Maven/IntelliJ setup)
More specifically, at runtime the app fails with a NoClassDefFoundError, since during class loading a wrong version of the owning class is loaded, which has a field of a type that does not exist in newer versions of the library that class is defined in. To illustrate:
// lib.jar:wrong-version
class Owner {
private SomeType f;
}
// lib.jar:new-version
class Owner {
private OtherType f;
}
At runtime, the class loader finds a reference to the symbol Owner and attempts to load the version that has SomeType, which in return does not exist anymore. This is even though I excluded wrong-version where ever I could spot it.
I also ran mvn dependency:tree to see if the old version is still being pulled in somewhere, but it's not!
In order to further debug this, I was wondering if there is a way to find out where a class loader was reading a specific class from, i.e. which file? Is that possible? Or even better, build a list of origins where a certain symbol is defined, in case it's defined more than once?
Sorry if this is vague, but the problem is rather nebulous.
The following code will search the whole classpath for a particular class. With no arguments it will dump every class it finds and then you can pipe to grep or redirect to a file. It looks inside jars...
Usage: WhichClass or WhichClass package.name (note no .class)
Apologies for the lack of comments ...
import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public final class WhichClass {
private WhichClass() {
}
static Vector<String> scratchVector;
public static void main(final String[] argv) {
Vector v;
if ((argv.length == 0) || "-all".equals(argv[0])) {
v = findClass(null);
} else {
v = findClass(argv[0]);
}
for (int i = 0; i < v.size(); i++) {
System.out.println(v.elementAt(i));
}
}
static String className(final String classFile) {
return classFile.replace('/', '.').substring(0, classFile.length() - ".class".length());
}
static Vector findClass(final String className) {
if (className != null) {
scratchVector = new Vector<String>(5);
} else {
scratchVector = new Vector<String>(5000);
}
findClassInPath(className, setupBootClassPath());
findClassInPath(className, setupClassPath());
return scratchVector;
}
static void findClassInPath(final String className, final StringTokenizer path) {
while (path.hasMoreTokens()) {
String pathElement = path.nextToken();
File pathFile = new File(pathElement);
if (pathFile.isDirectory()) {
try {
if (className != null) {
String pathName = className.replace('.', System.getProperty("file.separator").charAt(0)) + ".class";
findClassInPathElement(pathName, pathElement, pathFile);
} else {
findClassInPathElement(className, pathElement, pathFile);
}
} catch (IOException e) {
e.printStackTrace();
}
} else if (pathFile.exists()) {
try {
if (className != null) {
String pathName = className.replace('.', '/') + ".class";
ZipFile zipFile = new ZipFile(pathFile);
ZipEntry zipEntry = zipFile.getEntry(pathName);
if (zipEntry != null) {
scratchVector.addElement(pathFile + "(" + zipEntry + ")");
}
} else {
ZipFile zipFile = new ZipFile(pathFile);
Enumeration entries = zipFile.entries();
while (entries.hasMoreElements()) {
String entry = entries.nextElement().toString();
if (entry.endsWith(".class")) {
String name = className(entry);
scratchVector.addElement(pathFile + "(" + entry + ")");
}
}
}
} catch (IOException e) {
System.err.println(e + " while working on " + pathFile);
}
}
}
}
static void findClassInPathElement(final String pathName, final String pathElement, final File pathFile)
throws IOException {
String[] list = pathFile.list();
for (int i = 0; i < list.length; i++) {
File file = new File(pathFile, list[i]);
if (file.isDirectory()) {
findClassInPathElement(pathName, pathElement, file);
} else if (file.exists() && (file.length() != 0) && list[i].endsWith(".class")) {
String classFile = file.toString().substring(pathElement.length() + 1);
String name = className(classFile);
if (pathName != null) {
if (classFile.equals(pathName)) {
scratchVector.addElement(file.toString());
}
} else {
scratchVector.addElement(file.toString());
}
}
}
}
static StringTokenizer setupBootClassPath() {
String classPath = System.getProperty("sun.boot.class.path");
String separator = System.getProperty("path.separator");
return new StringTokenizer(classPath, separator);
}
static StringTokenizer setupClassPath() {
String classPath = System.getProperty("java.class.path");
String separator = System.getProperty("path.separator");
return new StringTokenizer(classPath, separator);
}
}
If you know the fully qualified name of the class, say somelib.Owner, you can try calling the following in your code:
public void foo() {
URL url = somelib.Owner.class.getClassLoader().getResource("somelib/Owner.class");
System.out.println(url);
}
I am using ErrorCollector in my Junit test case with the aim of printing out the errors and not stopping at the erroneous locations.
I successfully tried the ErrorCollector by using methods without parameters. However to reduce code duplicacy (i wrote the same method 6 times
without the parameter ; since i am using 6 files for comparison as seen in my code),
I would like to have a generic method that can be used to achieve the same purpose of printing out errors and continuing with the check.
When i tried using the method with a parameter i got the exception "Method should have no parameters"..
The following is my code.
import org.gdal172.ogr.ogr;
import org.hamcrest.CoreMatchers;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
public class DGNTester {
private ReadDGN readDgn = new ReadDGN();
private LinkedHashMap<String, Integer> layerMapCountForCompare = new LinkedHashMap<String, Integer>();
#Rule
public ErrorCollector collector = new ErrorCollector();
private File output = null;
static {
// perform OGR format registration once
if (ogr.GetDriverCount() == 0)
ogr.RegisterAll();
}
/**
* #param args
*/
public static void main(String[] args) {
DGNTester dTest = new DGNTester();
String dgnFileName_43k10 = "input\\43k10.dgn";
String dgnFileName_43k11 = "input\\43k11.dgn";
String dgnFileName_43k12 = "input\\43k12.dgn";
//The six files iam using as input.
dTest.test(dgnFileName_43k10, "dvd");
dTest.test(dgnFileName_43k10, "all");
dTest.test(dgnFileName_43k11, "dvd");
dTest.test(dgnFileName_43k11, "all");
dTest.test(dgnFileName_43k12, "dvd");
dTest.test(dgnFileName_43k12, "all");
}
#Test
public void test(String fileName, String inputType) {
System.out.println("FOR FILE -->" + fileName);
System.out
.println("---------------------------------------------------------------------------------------------------");
String fileIdentifier = fileName.substring(6, 11);
String dstFilePath = null;
String outputName = null;
if (layerMapCountForCompare != null)
layerMapCountForCompare.clear();
if (inputType.equals("dvd")) {
dstFilePath = "F:\\eclipse_helios_3.6.1_64_bit_with_jre_and_add-ons\\eclipse\\Resources\\DST\\dvd.dst";
outputName = "output\\outputfile_" + fileIdentifier
+ "_dvd.dst.txt";
}
if (inputType.equals("all")) {
dstFilePath = "F:\\eclipse_helios_3.6.1_64_bit_with_jre_and_add-ons\\eclipse\\Resources\\DST\\AllLayers.dst";
outputName = "output\\outputfile_" + fileIdentifier + ".txt";
}
layerMapCountForCompare = readDgn.getLayerFeatureCount(fileName,
dstFilePath);
// Read the text output file and Compare with the map. These are the six out put files against each input file
output = new File(outputName);
if (output.exists()) {
try {
Set keys = layerMapCountForCompare.keySet();
Iterator itr = keys.iterator();
String key = "";
Integer val;
String line;
BufferedReader br = new BufferedReader(new FileReader(output));
while ((line = br.readLine()) != null && itr.hasNext()) {
key = (String) itr.next();
val = layerMapCountForCompare.get(key);
String compare = key + "=" + val;
compare.trim();
line.trim();
//When i print this out in a positive scenario; i am able to see the values of 'compare' and 'line' as same
/*System.out.println("COMPARE >>> " + compare
+ " --------------- AND --------- Line " + line);*/
assertEquals("Comparing input and output", line, compare);
}
br.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
System.out.println("Output file does not exist");
}
}
public void assertEquals(String msg, Object expected, Object actual) {
collector.checkThat(actual, CoreMatchers.equalTo(expected));
}
}
In my previous example; where i did not use parameters;
Result result = JUnitCore.runClasses(DGNTester.class);
for (Failure failure : result.getFailures()) {
System.out.println(failure.toString());
}
if (result.wasSuccessful()) {
System.out.println("Tests finished successfully...");
}
This code helped in triggering the test methods and print the appropriate methods.
Can you please guide me as to how I can use my generic method thet takes two parameters to utilize the ErrorCollector.
How to utlilize Error Collector for junit testing methods with parameters
The #Test annotation doesn't support test methods with parameters in the signature.
Example:
Trying to run the method brokenTest an exception will be thrown. A correct test method in JUnit should look like correctTest:
/** This method uses parameters in the signature. It will not work! */
#Test
public void brokenTest(String fileName) {...}
/** This correct test method has no parameters in its signature. */
#Test
public void correctTest() {...}
Parameterized tests with JUnit
To support parameterized testing you can use the #RunWith(Parameterized.class) annotation (class-level).
The test class needs a static method that returns the parameters in an Iterable (like a List object). Annotate this method with #Parameters.
Furthermore you need a public(!) member variable for each parameter used, each one annotated with #Parameter(0), #Parameter(1), and so on.
Thus JUnit will run testWithParameters() for each test case produced with the createParameters() method. It will automatically assign the correct parameters to the #Parameter(N) fields (firstParameter/secondParameter).
You can produce as many parameters as you need.
Use these parameters in your test methods as needed by refering to their field names.
Example with an excerpt of the class you provided:
#RunWith(Parameterized.class)
public class DGNTester {
#Rule
public ErrorCollector collector = new ErrorCollector();
/**
* Method that generates the parameters.
* Each testValues.add(...) line produces a new test case.
*
* #return Array with test values.
*/
#Parameters
public static Iterable<Object[]> createParameters() {
List<Object[]> testValues = new ArrayList<>();
try {
testValues.add(new Object[]{"pre-Case1-Value1", "Case1-Value2"});
testValues.add(new Object[]{"Case2-Param1", "Case2-Value2"});
testValues.add(new Object[]{"pre-Case3-Value1", "Case3-Value2"});
}
return testValues;
}
/** The first parameter. */
#Parameter(0)
public String firstParameter;
/** The second parameter. */
#Parameter(1)
public String secondParameter;
/** Test using the parameters generated by createParameters().
* In this example we check, if the first parameter is equal to the
* concatenation of the String "pre-" and the second parameter */
#Test
public void testWithParameters() {
assertThat("Wrong parameter values", firstParameter,
is("pre-" + secondParameter));
}
...
}
I have a class with information about a Person that looks something like this:
public class Contact {
private String name;
private String location;
private String address;
private String email;
private String phone;
private String fax;
public String toString() {
// Something here
}
// Getters and setters.
}
I want toString() to return this.name +" - "+ this.locations + ... for all variables. I was trying to implement it using reflection as shown from this question but I can't manage to print instance variables.
What is the correct way to solve this?
From Implementing toString:
public String toString() {
StringBuilder result = new StringBuilder();
String newLine = System.getProperty("line.separator");
result.append( this.getClass().getName() );
result.append( " Object {" );
result.append(newLine);
//determine fields declared in this class only (no fields of superclass)
Field[] fields = this.getClass().getDeclaredFields();
//print field names paired with their values
for ( Field field : fields ) {
result.append(" ");
try {
result.append( field.getName() );
result.append(": ");
//requires access to private field:
result.append( field.get(this) );
} catch ( IllegalAccessException ex ) {
System.out.println(ex);
}
result.append(newLine);
}
result.append("}");
return result.toString();
}
Why do you want to reinvent the wheel when there are opensource that are already doing the job pretty nicely.
Both apache common-langs and spring support some very flexible builder pattern
For apache, here is how you do it reflectively
#Override
public String toString()
{
return ToStringBuilder.reflectionToString(this);
}
Here is how you do it if you only want to print fields that you care about.
#Override
public String toString()
{
return new ToStringBuilder(this)
.append("name", name)
.append("location", location)
.append("address", address)
.toString();
}
You can go as far as "styling" your print output with non-default ToStringStyle or even customizing it with your own style.
I didn't personally try spring ToStringCreator api, but it looks very similar.
If you are using Eclipse, this should be easy:
1.Press Alt+Shift+S
2.Choose "Generate toString()..."
Enjoy! You can have any template of toString()s.
This also works with getter/setters.
Generic toString() one-liner, using reflection and style customization:
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
...
public String toString()
{
return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
When accessing the field value, pass the instance rather than null.
Why not use code generation here? Eclipse, for example, will generate a reasoble toString implementation for you.
Another simple approach is to let Lombok generate the toString method for you.
For this:
Simply add Lombok to your project
Add the annotation #ToString to the definition of your class
Compile your class/project, and it is done
So for example in your case, your class would look like this:
#ToString
public class Contact {
private String name;
private String location;
private String address;
private String email;
private String phone;
private String fax;
// Getters and setters.
}
Example of output in this case:
Contact(name=John, location=USA, address=SF, email=foo#bar.com, phone=99999, fax=88888)
More details about how to use the annotation #ToString.
NB: You can also let Lombok generate the getters and setters for you, here is the full feature list.
If the output from ReflectionToStringBuilder.toString() is not enough readable for you, here is code that:
1) sorts field names alphabetically
2) flags non-null fields with asterisks in the beginning of the line
public static Collection<Field> getAllFields(Class<?> type) {
TreeSet<Field> fields = new TreeSet<Field>(
new Comparator<Field>() {
#Override
public int compare(Field o1, Field o2) {
int res = o1.getName().compareTo(o2.getName());
if (0 != res) {
return res;
}
res = o1.getDeclaringClass().getSimpleName().compareTo(o2.getDeclaringClass().getSimpleName());
if (0 != res) {
return res;
}
res = o1.getDeclaringClass().getName().compareTo(o2.getDeclaringClass().getName());
return res;
}
});
for (Class<?> c = type; c != null; c = c.getSuperclass()) {
fields.addAll(Arrays.asList(c.getDeclaredFields()));
}
return fields;
}
public static void printAllFields(Object obj) {
for (Field field : getAllFields(obj.getClass())) {
field.setAccessible(true);
String name = field.getName();
Object value = null;
try {
value = field.get(obj);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
System.out.printf("%s %s.%s = %s;\n", value==null?" ":"*", field.getDeclaringClass().getSimpleName(), name, value);
}
}
test harness:
public static void main(String[] args) {
A a = new A();
a.x = 1;
B b = new B();
b.x=10;
b.y=20;
System.out.println("=======");
printAllFields(a);
System.out.println("=======");
printAllFields(b);
System.out.println("=======");
}
class A {
int x;
String z = "z";
Integer b;
}
class B extends A {
int y;
private double z = 12345.6;
public int a = 55;
}
Addition with #cletus answer, You have to fetch all model fields(upper hierarchy) and set field.setAccessible(true) to access private members. Here is the full snippet:
#Override
public String toString() {
StringBuilder result = new StringBuilder();
String newLine = System.getProperty("line.separator");
result.append(getClass().getSimpleName());
result.append( " {" );
result.append(newLine);
List<Field> fields = getAllModelFields(getClass());
for (Field field : fields) {
result.append(" ");
try {
result.append(field.getName());
result.append(": ");
field.setAccessible(true);
result.append(field.get(this));
} catch ( IllegalAccessException ex ) {
// System.err.println(ex);
}
result.append(newLine);
}
result.append("}");
result.append(newLine);
return result.toString();
}
private List<Field> getAllModelFields(Class aClass) {
List<Field> fields = new ArrayList<>();
do {
Collections.addAll(fields, aClass.getDeclaredFields());
aClass = aClass.getSuperclass();
} while (aClass != null);
return fields;
}
i will get my answer as follow:
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class findclass {
public static void main(String[] args) throws Exception, IllegalAccessException {
new findclass().findclass(new Object(), "objectName");
new findclass().findclass(1213, "int");
new findclass().findclass("ssdfs", "String");
}
public Map<String, String>map=new HashMap<String, String>();
public void findclass(Object c,String name) throws IllegalArgumentException, IllegalAccessException {
if(map.containsKey(c.getClass().getName() + "#" + Integer.toHexString(c.hashCode()))){
System.out.println(c.getClass().getSimpleName()+" "+name+" = "+map.get(c.getClass().getName() + "#" + Integer.toHexString(c.hashCode()))+" = "+c);
return;}
map.put(c.getClass().getName() + "#" + Integer.toHexString(c.hashCode()), name);
Class te=c.getClass();
if(te.equals(Integer.class)||te.equals(Double.class)||te.equals(Float.class)||te.equals(Boolean.class)||te.equals(Byte.class)||te.equals(Long.class)||te.equals(String.class)||te.equals(Character.class)){
System.out.println(c.getClass().getSimpleName()+" "+name+" = "+c);
return;
}
if(te.isArray()){
if(te==int[].class||te==char[].class||te==double[].class||te==float[].class||te==byte[].class||te==long[].class||te==boolean[].class){
boolean dotflag=true;
for (int i = 0; i < Array.getLength(c); i++) {
System.out.println(Array.get(c, i).getClass().getSimpleName()+" "+name+"["+i+"] = "+Array.get(c, i));
}
return;
}
Object[]arr=(Object[])c;
for (Object object : arr) {
if(object==null)
System.out.println(c.getClass().getSimpleName()+" "+name+" = null");
else {
findclass(object, name+"."+object.getClass().getSimpleName());
}
}
}
Field[] fields=c.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
if(field.get(c)==null){
System.out.println(field.getType().getSimpleName()+" "+name+"."+field.getName()+" = null");
continue;
}
findclass(field.get(c),name+"."+field.getName());
}
if(te.getSuperclass()==Number.class||te.getSuperclass()==Object.class||te.getSuperclass()==null)
return;
Field[]faFields=c.getClass().getSuperclass().getDeclaredFields();
for (Field field : faFields) {
field.setAccessible(true);
if(field.get(c)==null){
System.out.println(field.getType().getSimpleName()+" "+name+"<"+c.getClass().getSuperclass().getSimpleName()+"."+field.getName()+" = null");
continue;
}
Object check=field.get(c);
findclass(field.get(c),name+"<"+c.getClass().getSuperclass().getSimpleName()+"."+field.getName());
}
}
public void findclass(Object c,String name,Writer writer) throws IllegalArgumentException, IllegalAccessException, IOException {
if(map.containsKey(c.getClass().getName() + "#" + Integer.toHexString(c.hashCode()))){
writer.append(c.getClass().getSimpleName()+" "+name+" = "+map.get(c.getClass().getName() + "#" + Integer.toHexString(c.hashCode()))+" = "+c+"\n");
return;}
map.put(c.getClass().getName() + "#" + Integer.toHexString(c.hashCode()), name);
Class te=c.getClass();
if(te.equals(Integer.class)||te.equals(Double.class)||te.equals(Float.class)||te.equals(Boolean.class)||te.equals(Byte.class)||te.equals(Long.class)||te.equals(String.class)||te.equals(Character.class)){
writer.append(c.getClass().getSimpleName()+" "+name+" = "+c+"\n");
return;
}
if(te.isArray()){
if(te==int[].class||te==char[].class||te==double[].class||te==float[].class||te==byte[].class||te==long[].class||te==boolean[].class){
boolean dotflag=true;
for (int i = 0; i < Array.getLength(c); i++) {
writer.append(Array.get(c, i).getClass().getSimpleName()+" "+name+"["+i+"] = "+Array.get(c, i)+"\n");
}
return;
}
Object[]arr=(Object[])c;
for (Object object : arr) {
if(object==null){
writer.append(c.getClass().getSimpleName()+" "+name+" = null"+"\n");
}else {
findclass(object, name+"."+object.getClass().getSimpleName(),writer);
}
}
}
Field[] fields=c.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
if(field.get(c)==null){
writer.append(field.getType().getSimpleName()+" "+name+"."+field.getName()+" = null"+"\n");
continue;
}
findclass(field.get(c),name+"."+field.getName(),writer);
}
if(te.getSuperclass()==Number.class||te.getSuperclass()==Object.class||te.getSuperclass()==null)
return;
Field[]faFields=c.getClass().getSuperclass().getDeclaredFields();
for (Field field : faFields) {
field.setAccessible(true);
if(field.get(c)==null){
writer.append(field.getType().getSimpleName()+" "+name+"<"+c.getClass().getSuperclass().getSimpleName()+"."+field.getName()+" = null"+"\n");
continue;
}
Object check=field.get(c);
findclass(field.get(c),name+"<"+c.getClass().getSuperclass().getSimpleName()+"."+field.getName(),writer);
}
}
}