Let's say I have an abstract class:
abstract class Foo extends Bar {
public abstract int foo();
}
that I want to extend at runtime to create a Class object. The hope would be that I could have a dynamically generated class:
class FooImpl extends Foo {
#Override
public int foo() {
return 5;
}
}
that would be represented by a Class object and that I could then use reflection to create new instances of. The key is that I would like to decide the return value of the method foo() at runtime. My thought is to use ASM to create the bytecode for the class and then use reflection on a ClassLoader object to define the Class.
Is using ASM and then reflection of the method ClassLoader#defineClass on the generated bytes the best way to implement abstract methods at runtime with non-hardcoded values?
If yes, how would I go about doing that. My gut is to utilize the ASMifierClassVisitor, but I'm not quite sure on the exact method of doing that. I know that if all else fails I can manually go through the JVM instructions required to define a specific class but I feel there must be an easier way.
If no, what is the best way and how would I go about using the best way?
EDIT: I checked out all of the answers and I decided that none of them were exactly what I was looking for. I ended up creating a small implementation of what I was talking about with ASM. I figured I should post it here:
import org.objectweb.asm.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
/**
* Created by IntelliJ IDEA.
* User: Matt
* Date: 9/17/11
* Time: 12:42 PM
*/
public class OverrideClassAdapter extends ClassAdapter {
private final HashMap<String, Object> code;
private final String className;
private final ClassWriter writer;
private String superName;
public OverrideClassAdapter(ClassWriter writer, String className, Queue<int[]> constructorCode, HashMap<String, Object> code) {
super(writer);
this.writer = writer;
this.className = className;
this.code = code;
}
#Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
this.superName = name;
if((access & Opcodes.ACC_ABSTRACT) != 0)
access &= ~Opcodes.ACC_ABSTRACT;
if((access & Opcodes.ACC_INTERFACE) != 0)
access &= ~Opcodes.ACC_INTERFACE;
cv.visit(version, access, className, signature, name, null);
}
#Override
public void visitSource(String source, String debug) {
}
#Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
boolean isAbstract = (access & Opcodes.ACC_ABSTRACT) != 0;
if(isAbstract)
access &= ~Opcodes.ACC_ABSTRACT;
MethodWriter mw = (MethodWriter) cv.visitMethod(access, name, desc, signature, exceptions);
Object value = code.get(name);
if(isAbstract || value != null) {
if(value instanceof BytecodeValue) {
BytecodeValue returnableValue = (BytecodeValue) value;
int[] byteCode = new int[returnableValue.getValueCode().length + 1];
System.arraycopy(returnableValue.getValueCode(), 0, byteCode, 0, returnableValue.getValueCode().length);
if(returnableValue.getValueCode().length > 1 && returnableValue.getValueCode()[1] == 0) {
byteCode[1] = writer.newConst(returnableValue.getValue());
}
byteCode[byteCode.length - 1] = returnableValue.getReturnCode();
value = byteCode;
}
return new OverrideMethodAdapter(mw, (int[]) value);
}
return mw;
}
private class OverrideMethodAdapter extends MethodAdapter {
private final int[] code;
private final MethodWriter writer;
public OverrideMethodAdapter(MethodWriter writer, int[] code) {
super(writer);
this.writer = writer;
this.code = code;
}
#Override
public void visitEnd() {
try {
Field code = MethodWriter.class.getDeclaredField("code");
code.setAccessible(true);
ByteVector bytes = new ByteVector();
for(int b : this.code)
bytes.putByte(b);
code.set(writer, bytes);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static byte[] extendClassBytes(Class clazz, String className, HashMap<String, Object> methodImpls) throws IOException {
ClassReader cr = new ClassReader(clazz.getName());
ClassWriter cw = new ClassWriter(0);
cr.accept(new OverrideClassAdapter(cw, className, methodImpls), ClassReader.SKIP_DEBUG);
cr = new ClassReader(cw.toByteArray());
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
cr.accept(cw, ClassReader.SKIP_DEBUG);
//CheckClassAdapter.verify(new org.objectweb.asm.ClassReader(cw.toByteArray()), true, new PrintWriter(System.out));
/*File file = new File(className + ".class");
new FileOutputStream(file).write(cw.toByteArray());*/
return cw.toByteArray();
}
public static Class extendClass(Class clazz, String className, HashMap<String, Object> methodImpls) throws IOException {
return defineClass(extendClassBytes(clazz, className, methodImpls), className);
}
public static Class defineClass(byte[] code, String name) {
try {
Method method = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
method.setAccessible(true);
return (Class) method.invoke(Thread.currentThread().getContextClassLoader(), name, code, 0, code.length);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
You might want to look at using CGLib. It can do what Java's dynamic proxies can do but for abstract classes as well as interfaces, and it has a similar API to java.lang.reflect.Proxy for doing this as well. CGLib uses ASM behind the scenes anyway, but by using CGLib you wont have to craft bytecode directly.
Here's an example of how to use CGLib to do this:
package cglibtest;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CGLibTest
{
public static void main(String... args)
{
MyAbstract instance = (MyAbstract)Enhancer.create(MyAbstract.class, new MyInterceptor(42));
System.out.println("Value from instance: " + instance.valueMethod());
}
public static class MyInterceptor implements MethodInterceptor
{
private final Object constantValue;
public MyInterceptor(Object constantValue)
{
this.constantValue = constantValue;
}
#Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable
{
if ("valueMethod".equals(method.getName()))
return(constantValue);
else
return(null);
}
}
public static abstract class MyAbstract
{
public abstract int valueMethod();
}
}
What's stopping you from reading the value 5 from say properties and return it back? That's too simple so, I guess you must have something more complex than returning an int that you want to accomplish here. I agree with the posts above that generating classes at runtime would be very expensive. If you know your business logic in advance, you can apply the Factory pattern to load the desired implementation of defined interfaces at runtime. That's how JDBC libraries work.
If you do not know the business logic in advance and have lot's of it then, you might benefit from using an off the shelf Rule Engine to process the logic and return results back to your Java program. It is much easier to maintain this logic in a Rule Engine specially if it is changing frequently.
Yes, that approach should work. But it will be expensive if you do a lot of class generation. (We are probably talking about hundreds of thousands of instructions to generate the bytecode file and then load it. And then there's the memory need to represent the class when it is loaded.)
Another approach (also expensive) is to generate source code and compile and load it at runtime.
Finally, you should consider the approach of making the logic of the objects table-driven or implementing it using some kind of interpreter. If you actually need to have different classes, you could wrap this up using Java's dynamic proxy class mechanism; e.g. see java.lang.reflect.Proxy
Related
In Java, how can you pass a type as a parameter (or declare as a variable)?
I don't want to pass an instance of the type but the type itself (eg. int, String, etc).
In C#, I can do this:
private void foo(Type t)
{
if (t == typeof(String)) { ... }
else if (t == typeof(int)) { ... }
}
private void bar()
{
foo(typeof(String));
}
Is there a way in Java without passing an instance of type t?
Or do I have to use my own int constants or enum?
Or is there a better way?
Edit: Here is the requirement for foo:
Based on type t, it generates a different short, xml string.
The code in the if/else will be very small (one or two lines) and will use some private class variables.
You could pass a Class<T> in.
private void foo(Class<?> cls) {
if (cls == String.class) { ... }
else if (cls == int.class) { ... }
}
private void bar() {
foo(String.class);
}
Update: the OOP way depends on the functional requirement. Best bet would be an interface defining foo() and two concrete implementations implementing foo() and then just call foo() on the implementation you've at hand. Another way may be a Map<Class<?>, Action> which you could call by actions.get(cls). This is easily to be combined with an interface and concrete implementations: actions.get(cls).foo().
I had a similar question, so I worked up a complete runnable answer below. What I needed to do is pass a class (C) to an object (O) of an unrelated class and have that object (O) emit new objects of class (C) back to me when I asked for them.
The example below shows how this is done. There is a MagicGun class that you load with any subtype of the Projectile class (Pebble, Bullet or NuclearMissle). The interesting is you load it with subtypes of Projectile, but not actual objects of that type. The MagicGun creates the actual object when it's time to shoot.
The Output
You've annoyed the target!
You've holed the target!
You've obliterated the target!
click
click
The Code
import java.util.ArrayList;
import java.util.List;
public class PassAClass {
public static void main(String[] args) {
MagicGun gun = new MagicGun();
gun.loadWith(Pebble.class);
gun.loadWith(Bullet.class);
gun.loadWith(NuclearMissle.class);
//gun.loadWith(Object.class); // Won't compile -- Object is not a Projectile
for(int i=0; i<5; i++){
try {
String effect = gun.shoot().effectOnTarget();
System.out.printf("You've %s the target!\n", effect);
} catch (GunIsEmptyException e) {
System.err.printf("click\n");
}
}
}
}
class MagicGun {
/**
* projectiles holds a list of classes that extend Projectile. Because of erasure, it
* can't hold be a List<? extends Projectile> so we need the SuppressWarning. However
* the only way to add to it is the "loadWith" method which makes it typesafe.
*/
private #SuppressWarnings("rawtypes") List<Class> projectiles = new ArrayList<Class>();
/**
* Load the MagicGun with a new Projectile class.
* #param projectileClass The class of the Projectile to create when it's time to shoot.
*/
public void loadWith(Class<? extends Projectile> projectileClass){
projectiles.add(projectileClass);
}
/**
* Shoot the MagicGun with the next Projectile. Projectiles are shot First In First Out.
* #return A newly created Projectile object.
* #throws GunIsEmptyException
*/
public Projectile shoot() throws GunIsEmptyException{
if (projectiles.isEmpty())
throw new GunIsEmptyException();
Projectile projectile = null;
// We know it must be a Projectile, so the SuppressWarnings is OK
#SuppressWarnings("unchecked") Class<? extends Projectile> projectileClass = projectiles.get(0);
projectiles.remove(0);
try{
// http://www.java2s.com/Code/Java/Language-Basics/ObjectReflectioncreatenewinstance.htm
projectile = projectileClass.newInstance();
} catch (InstantiationException e) {
System.err.println(e);
} catch (IllegalAccessException e) {
System.err.println(e);
}
return projectile;
}
}
abstract class Projectile {
public abstract String effectOnTarget();
}
class Pebble extends Projectile {
#Override public String effectOnTarget() {
return "annoyed";
}
}
class Bullet extends Projectile {
#Override public String effectOnTarget() {
return "holed";
}
}
class NuclearMissle extends Projectile {
#Override public String effectOnTarget() {
return "obliterated";
}
}
class GunIsEmptyException extends Exception {
private static final long serialVersionUID = 4574971294051632635L;
}
Oh, but that's ugly, non-object-oriented code. The moment you see "if/else" and "typeof", you should be thinking polymorphism. This is the wrong way to go. I think generics are your friend here.
How many types do you plan to deal with?
UPDATE:
If you're just talking about String and int, here's one way you might do it. Start with the interface XmlGenerator (enough with "foo"):
package generics;
public interface XmlGenerator<T>
{
String getXml(T value);
}
And the concrete implementation XmlGeneratorImpl:
package generics;
public class XmlGeneratorImpl<T> implements XmlGenerator<T>
{
private Class<T> valueType;
private static final int DEFAULT_CAPACITY = 1024;
public static void main(String [] args)
{
Integer x = 42;
String y = "foobar";
XmlGenerator<Integer> intXmlGenerator = new XmlGeneratorImpl<Integer>(Integer.class);
XmlGenerator<String> stringXmlGenerator = new XmlGeneratorImpl<String>(String.class);
System.out.println("integer: " + intXmlGenerator.getXml(x));
System.out.println("string : " + stringXmlGenerator.getXml(y));
}
public XmlGeneratorImpl(Class<T> clazz)
{
this.valueType = clazz;
}
public String getXml(T value)
{
StringBuilder builder = new StringBuilder(DEFAULT_CAPACITY);
appendTag(builder);
builder.append(value);
appendTag(builder, false);
return builder.toString();
}
private void appendTag(StringBuilder builder) { this.appendTag(builder, false); }
private void appendTag(StringBuilder builder, boolean isClosing)
{
String valueTypeName = valueType.getName();
builder.append("<").append(valueTypeName);
if (isClosing)
{
builder.append("/");
}
builder.append(">");
}
}
If I run this, I get the following result:
integer: <java.lang.Integer>42<java.lang.Integer>
string : <java.lang.String>foobar<java.lang.String>
I don't know if this is what you had in mind.
You should pass a Class...
private void foo(Class<?> t){
if(t == String.class){ ... }
else if(t == int.class){ ... }
}
private void bar()
{
foo(String.class);
}
If you want to pass the type, than the equivalent in Java would be
java.lang.Class
If you want to use a weakly typed method, then you would simply use
java.lang.Object
and the corresponding operator
instanceof
e.g.
private void foo(Object o) {
if(o instanceof String) {
}
}//foo
However, in Java there are primitive types, which are not classes (i.e. int from your example), so you need to be careful.
The real question is what you actually want to achieve here, otherwise it is difficult to answer:
Or is there a better way?
You can pass an instance of java.lang.Class that represents the type, i.e.
private void foo(Class cls)
I am using the factory pattern to create objects of different connections in java version "1.7.0_60"
The problem I am facing is that each concrete class will have unique properties for that particular class. As the factory will use polymorpthism when it return the instance of the concrete class, I cannot access the unique properties. i.e. getHostType() is unique only for SqlServerConnection.
The workaround I have done is to declare getHostType() abstract in the super class and implement it in each concrete class. However, I don't really want to do it that way as the more concrete classes I add that have their unique properties the more abstract methods I will have to include in the super class, and then implement them in each concrete class.
I want to keep my factory pattern and the abstract super class. I am just wondering if there is any other way instead of having the abstract methods in the super class? Any design patterns I can include to get around this?
public abstract class Connection {
private int port;
private int ipAddress;
public Connection() {}
public String description() {
return "Generic";
}
/* Implement in every concrete class, even if the concrete type doesn't have that property */
public abstract int getHostType();
}
public class SqlServerConnection extends Connection {
private int sqlHostType;
public SqlServerConnection() {
sqlHostType = 5060;
}
#Override
public String description() {
return "Created a Sql Server connection type";
}
#Override
public int getHostType() {
return sqlHostType;
}
}
public class OracleConnection extends Connection {
public OracleConnection() {}
#Override
public String description() {
return "Created an Oracle connection type";
}
}
final public class ConnectionFactory {
protected String mType;
public ConnectionFactory(String type) {
mType = type;
}
/* Create the connection we want to use */
public Connection createConnection() {
if(mType.equals("Oracle")) {
return new OracleConnection();
}
else if(mType.equals("SQLServer")) {
return new SqlServerConnection();
}
else {
return null;
}
}
}
public class TestConnection {
public static void main(String[] args) {
ConnectionFactory factory = new ConnectionFactory("SQLServer");
Connection conn = factory.createConnection();
conn = factory.createConnection();
System.out.println(conn.description());
/* need to access the getHostType() */
System.out.println(conn.getHostType());
}
}
You should have a look at the visitor pattern. You need to declare an interface ConnectionVisitor and add an method visit for each of your connection class in your hierarchy.
public interface ConnectionVisitor {
public int visit (Connection connection);
public int visit (SqlServerConnection sqlconnection);
public int visit (OracleConnection oracleConnection)
}
Now you need to add an accept method in your base class connection and that accepts a ConnectionVisitor and then calls visit on it. Your new Connection class will look something like
public abstract class Connection {
private int port;
private int ipAddress;
public Connection() {}
public String description() {
return "Generic";
}
public int accept(ConnectionVisitor visitor){
return visitor.visit(this);
}
}
Notice that the accept method does a dual dispatch. It dispatches on the base of the object on which it is called and the parameter that is passed to this method. This is at the heart of visitor pattern.
You can then implement the ConnectionVisitor interface to define any new functionality without changing your base class.
class DemoVisitor implements ConnectionVisitor{
public int visit(Connection connection){
System.out.println("Visiting Connection");
return 1;
}
public int visit(SqlServerConnection sqlServerConnection){
System.out.println("Visiting SqlServerConnection");
return 1;
}
public int visit(OracleConnection oracleConnection){
System.out.println("Visiting Oracle Connection");
return 1;
}
}
In your TestConnection class you can simply create a new connection object and then call accept method on that object passing a visitor object.
public class TestConnection {
public static void main(String[] args) {
ConnectionFactory factory = new ConnectionFactory("SQLServer");
Connection conn = factory.createConnection();
conn = factory.createConnection();
System.out.println(conn.description());
ConnectionVisitor visitor = new DemoVisitor();
System.out.println(conn.accept(visitor));
}
}
So now any child class specific functionality must not reside in connection class hierarchy instead they must be implemented in new visitors.
Note that this pattern is not going to fit as such in your scenario. One of the limitation of this pattern in that the return type for all the methods in visitor interface must be same. This pattern may or may not fit your needs but it is worth looking into your case as such. You will probably need to modify this pattern to fit your needs. And that is what patterns are all about looking into some common solutions and then modifying those solutions to fit into your problem.
Why do you want that? I mean, I use a Factory to hide specific implementation of factorized object, returning a common abstract class (or an interface).
I wonder why you may want to do something like:
ConnectionFactory factory = new ConnectionFactory("SQLServer");
Connection conn = factory.createConnection();
if(conn.getHostType() == 1 ) {
doSomethingLogic();
}
if(conn.getHostType() == 2) {
doSomethingElseLogic();
}
shouldn't all the ifs be inside the factory?
You need to have the getHostType() method in your Connection class for this method to be called polymorphically.
The only other solution is to type cast the object returned by the factory to the one expected which is not at all a good approach. Reason for this is you will have to keep check on the Class type of the returned object whether it is of ORACLE or MySQL etc through if else statements (unnecessary we have ploymorphism to prevent this). If you just define the method in your Connection class you dont need to worry about the getHostType() method, as it will be called from the correct class polymorphically.
In the oracleConnection class you can just add the method getHostType() that returns null object with an error message in the code you wrote.
I don't know how applicable this approach will be to your specific situation, but you could try grouping related, optional, subclass-specific connection behavior into its own interfaces and then let each concrete class implement the interfaces that are appropriate for it.
In your example, both connection classes implement description() so you could create an Interface called Descriptor and have a method on your abstract class called getDescriptor():
public Descriptor getDescriptor() throws HasNoDescriptorException {
if (self instanceof Descriptor) {
return self;
}
throw new HasNoDescriptorException();
}
And then let interface Descriptor provide the description() method.
You'd get a connector's description like this:
String desc = "";
try {
desc = connector.getDescriptor().description();
} catch (HasNoDescriptorException e) {
// connector doesn't have a description() method;
}
If you don't like exceptions you could just return and test for nulls.
For that matter, your code could simply test to see if the connection instance is an instance of the Descriptor interface and if it is then you know you have full access to any of the methods appropriate for a Descriptor.
To continue the example, you could have a TypedHost interface whose implementing connection classes provide a getSqlHost() method.
I'd do it like this:
Remove the getHostTypeCreate method from the abstract class since not all of the connections have this property. Then add a new interface IHostTypeProvider(you might pick a better name though):
public interface IHostTypeProvider {
int getHostType();
}
Now, make some of the sub-classes implement this interface:
public SqlServerConnection extends Connection implements IHostTypeProvider {
....
public int getHostType() {
return 5060;
}
}
When you actually need access to the property, you fist need to check if it's available for this type of connection:
Connection con = ...;
//Check if host type is available
if (connection instanceof IHostTypeProvider) {
System.out.println(((IHostTypeProvider)con).getHostType());
}
Hope that helps.
Sounds like a use case for typesafe heterogeneous container. I'll just post my example, I think it's pretty much explaining itself. If there are any questions left, I'll answer them.
The Pros are that it is easily extensible and it supports multiple types.
import java.util.HashMap;
import java.util.Map;
public abstract class Connection
{
/* typesafe heterogeneous container */
private final Map<Property<?>, Object> properties = new HashMap<Property<?>, Object>();
public Connection(String ip, int port)
{
addProperty(ConnectionProperties.IP_ADDRESS, ip);
addProperty(ConnectionProperties.PORT, port);
}
/**
* Gets property in its respective type.
*
* #param p property
* #return value of property.
*/
public <T> T getProperty(Property<T> p)
{
Object obj = properties.get(p);
if (obj == null)
return null;
Class<T> clazz = p.getClazz();
return clazz.cast(obj);
}
/**
* Checks whether property is available
*
* #param p property to check for
* #return <code>true</code>, if property is available
*/
public boolean hasProperty(Property<?> p)
{
return properties.get(p) != null;
}
/* helper method to add properties */
protected <T> void addProperty(Property<T> p, T value)
{
properties.put(p, value);
}
}
class SqlServerConnection extends Connection
{
public SqlServerConnection(String ip, int port)
{
super(ip, port);
addProperty(ConnectionProperties.DESCRIPTION, "Created a Sql Server connection type");
addProperty(ConnectionProperties.SQL_HOST_TYPE, 5090);
}
}
/* all properties are stored here (...there could be more classes if needed) */
final class ConnectionProperties
{
private ConnectionProperties()
{
// private contructor to prevent instantiation of utility class
}
public static final Property<String> IP_ADDRESS = new Property<String>("IP_ADDRESS", String.class);
public static final Property<Integer> PORT = new Property<Integer>("PORT", Integer.class);
public static final Property<String> DESCRIPTION = new Property<String>("DESCRIPTION", String.class);
public static final Property<Integer> SQL_HOST_TYPE = new Property<Integer>("SQL_HOST_TYPE", Integer.class);
}
/* property class that serves as key for typesafe heterogeneous container */
final class Property<T>
{
/* has to be unique */
private final String name;
private final Class<T> clazz;
public Property(String name, Class<T> clazz)
{
this.name = name;
this.clazz = clazz;
}
public String getName()
{
return name;
}
public Class<T> getClazz()
{
return clazz;
}
#Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Property<?> other = (Property<?>) obj;
if (name == null)
{
if (other.name != null)
return false;
}
else if (!name.equals(other.name))
return false;
return true;
}
}
You could also define a interface for Property<T> and for Connection, that would have the future benefit of having the possibility to exchange the implementations, but I left it out to spaer some space here.
If the instantion of properties is to complex, there are also other alternatives. For example following
public final class PropertyV2<T>
{
private static final AtomicInteger KEY_SUPPLY = new AtomicInteger();
/* unique key for property distinction */
private final int key;
private final Class<T> clazz;
private PropertyV2(Class<T> clazz)
{
this.key = KEY_SUPPLY.getAndIncrement();
this.clazz = clazz;
}
/* factory method for string properties */
public static PropertyV2<String> string()
{
return new PropertyV2<String>(String.class);
}
/* factory method for integer properties */
public static PropertyV2<Integer> integer()
{
return new PropertyV2<Integer>(Integer.class);
}
public Class<T> getClazz()
{
return clazz;
}
#Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + key;
return result;
}
#Override
public boolean equals(Object obj)
{
if (obj == null || getClass() != obj.getClass())
return false;
PropertyV2<?> other = (PropertyV2<?>) obj;
if (key != other.key)
return false;
return true;
}
}
class ConnectionPropertiesV2
{
private ConnectionPropertiesV2()
{
// private constructor to prevent instatiation of utiltiy class
}
PropertyV2<String> IP_ADDRESS = PropertyV2.string();
PropertyV2<Integer> PORT = PropertyV2.integer();
}
The problem hereby is, that you lose the name attribute, which can be useful if you want to use the property name at runtime - let's say in an exception.
The more OO approach is to push the implementation details into the child class, rather than expose them in abstract methods (which may not have a well-defined implementation for some children).
For example rather than writing,
System.out.println(conn.description());
/* need to access the getHostType() */
System.out.println(conn.getHostType());
Instead, write
conn.printTo(System.out);
Then provide a printTo method for each child. In this way, you've refactored the code to hide all of the implementation details of the Connection objects and how it prints itself to a stream.
I need to get a list of all caller methods for a method of interest for me in Java. Is there a tool that can help me with this?
Edit: I forgot to mention that I need to do this from a program. I'm usig Java Pathfinder and I want to run it an all the methods that call my method of interest.
For analyzing bytecode, I would recommend ASM. Given a list of Classes to analyze, a visitor can be made which finds the method calls you're interested in. One implementation which analyses classes in a jar file is below.
Note that ASM uses internalNames with '/' instead of '.' as a separator. Specify the target method as a standard declaration without modifiers.
For example, to list methods that could be calling System.out.println("foo") in the java runtime jar:
java -cp "classes;asm-3.1.jar;asm-commons-3.1.jar" App \
c:/java/jdk/jre/lib/rt.jar \
java/io/PrintStream "void println(String)"
Edit: source and line numbers added: Note that this only indicates the last target method invocation per calling method - the original q only wanted to know which methods. I leave it as an exercise for the reader to show line numbers of the calling method declaration, or the line numbers of every target invocation, depending on what you're actually after. :)
results in:
LogSupport.java:44 com/sun/activation/registries/LogSupport log (Ljava/lang/String;)V
LogSupport.java:50 com/sun/activation/registries/LogSupport log (Ljava/lang/String;Ljava/lang/Throwable;)V
...
Throwable.java:498 java/lang/Throwable printStackTraceAsCause (Ljava/io/PrintStream;[Ljava/lang/StackTraceElement;)V
--
885 methods invoke java/io/PrintStream println (Ljava/lang/String;)V
source:
public class App {
private String targetClass;
private Method targetMethod;
private AppClassVisitor cv;
private ArrayList<Callee> callees = new ArrayList<Callee>();
private static class Callee {
String className;
String methodName;
String methodDesc;
String source;
int line;
public Callee(String cName, String mName, String mDesc, String src, int ln) {
className = cName; methodName = mName; methodDesc = mDesc; source = src; line = ln;
}
}
private class AppMethodVisitor extends MethodAdapter {
boolean callsTarget;
int line;
public AppMethodVisitor() { super(new EmptyVisitor()); }
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
if (owner.equals(targetClass)
&& name.equals(targetMethod.getName())
&& desc.equals(targetMethod.getDescriptor())) {
callsTarget = true;
}
}
public void visitCode() {
callsTarget = false;
}
public void visitLineNumber(int line, Label start) {
this.line = line;
}
public void visitEnd() {
if (callsTarget)
callees.add(new Callee(cv.className, cv.methodName, cv.methodDesc,
cv.source, line));
}
}
private class AppClassVisitor extends ClassAdapter {
private AppMethodVisitor mv = new AppMethodVisitor();
public String source;
public String className;
public String methodName;
public String methodDesc;
public AppClassVisitor() { super(new EmptyVisitor()); }
public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {
className = name;
}
public void visitSource(String source, String debug) {
this.source = source;
}
public MethodVisitor visitMethod(int access, String name,
String desc, String signature,
String[] exceptions) {
methodName = name;
methodDesc = desc;
return mv;
}
}
public void findCallingMethodsInJar(String jarPath, String targetClass,
String targetMethodDeclaration) throws Exception {
this.targetClass = targetClass;
this.targetMethod = Method.getMethod(targetMethodDeclaration);
this.cv = new AppClassVisitor();
JarFile jarFile = new JarFile(jarPath);
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry.getName().endsWith(".class")) {
InputStream stream = new BufferedInputStream(jarFile.getInputStream(entry), 1024);
ClassReader reader = new ClassReader(stream);
reader.accept(cv, 0);
stream.close();
}
}
}
public static void main( String[] args ) {
try {
App app = new App();
app.findCallingMethodsInJar(args[0], args[1], args[2]);
for (Callee c : app.callees) {
System.out.println(c.source+":"+c.line+" "+c.className+" "+c.methodName+" "+c.methodDesc);
}
System.out.println("--\n"+app.callees.size()+" methods invoke "+
app.targetClass+" "+
app.targetMethod.getName()+" "+app.targetMethod.getDescriptor());
} catch(Exception x) {
x.printStackTrace();
}
}
}
Edit: the original question was edited to indicate a runtime solution was needed - this answer was given before that edit and only indicates how to do it during development.
If you are using Eclipse you can right click the method and choose "Open call hierarchy" to get this information.
Updated after reading comments: Other IDEs support this as well in a similar fashion (at least Netbeans and IntelliJ do)
Annotate the method with #Deprecated ( or tag it with #deprecated ), turn on deprecation warnings, run your compile and see which warnings get triggered.
The run your compile bit can be done either by invoking an external ant process or by using the Java 6 compiler API.
right click on method
Go to references and (depending on your requirement)
choose workspace/project/Hierarchy.
This pops up a panel that shows all references to this functions. Eclipse FTW !
In eclipse, highlight the method name and then Ctrl+Shift+G
There isn't a way to do this (programmatically) via the Java reflection libraries - you can't ask a java.lang.reflect.Method "which methods do you call?"
That leaves two other options I can think of:
Static analysis of the source code. I'm sure this is what the Eclipse Java toolset does - you could look at the Eclipse source behind the JDT, and find what it does when you ask Eclipse to "Find References" to a method.
Bytecode analysis. You could inspect the bytecode for calls to the method. I'm not sure what libraries or examples are out there to help with this - but I can't imagine that something doesn't exist.
Yes, most modern IDE:s will let you either search for usages of a method or variable. Alternatively, you could use a debugger and set a trace point on the method entry, printing a stack trace or whatever every time the method is invoked.
Finally, you could use some simple shell util to just grep for the method, such as
find . -name '*.java' -exec grep -H methodName {} ;
The only method that will let you find invokations made through some reflection method, though, would be using the debugger.
I made a small example using #Chadwick's one. It's a test that assesses if calls to getDatabaseEngine() are made by methods that implement #Transaction.
/**
* Ensures that methods that call {#link DatabaseProvider#getDatabaseEngine()}
* implement the {#link #Transaction} annotation.
*
* #throws Exception If something occurs while testing.
*/
#Test
public void ensure() throws Exception {
final Method method = Method.getMethod(
DatabaseEngine.class.getCanonicalName() + " getDatabaseEngine()");
final ArrayList<java.lang.reflect.Method> faultyMethods = Lists.newArrayList();
for (Path p : getAllClasses()) {
try (InputStream stream = new BufferedInputStream(Files.newInputStream(p))) {
ClassReader reader = new ClassReader(stream);
reader.accept(new ClassAdapter(new EmptyVisitor()) {
#Override
public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
return new MethodAdapter(new EmptyVisitor()) {
#Override
public void visitMethodInsn(int opcode, String owner, String nameCode, String descCode) {
try {
final Class<?> klass = Class.forName(Type.getObjectType(owner).getClassName());
if (DatabaseProvider.class.isAssignableFrom(klass) &&
nameCode.equals(method.getName()) &&
descCode.equals(method.getDescriptor())) {
final java.lang.reflect.Method method = klass.getDeclaredMethod(name,
getParameters(desc).toArray(new Class[]{}));
for (Annotation annotation : method.getDeclaredAnnotations()) {
if (annotation.annotationType().equals(Transaction.class)) {
return;
}
}
faultyMethods.add(method);
}
} catch (Exception e) {
Throwables.propagate(e);
}
}
};
}
}, 0);
}
}
if (!faultyMethods.isEmpty()) {
fail("\n\nThe following methods must implement #Transaction because they're calling getDatabaseEngine().\n\n" + Joiner.on("\n").join
(faultyMethods) + "\n\n");
}
}
/**
* Gets all the classes from target.
*
* #return The list of classes.
* #throws IOException If something occurs while collecting those classes.
*/
private List<Path> getAllClasses() throws IOException {
final ImmutableList.Builder<Path> builder = new ImmutableList.Builder<>();
Files.walkFileTree(Paths.get("target", "classes"), new SimpleFileVisitor<Path>() {
#Override
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
if (file.getFileName().toString().endsWith(".class")) {
builder.add(file);
}
return FileVisitResult.CONTINUE;
}
});
return builder.build();
}
/**
* Gets the list of parameters given the description.
*
* #param desc The method description.
* #return The list of parameters.
* #throws Exception If something occurs getting the parameters.
*/
private List<Class<?>> getParameters(String desc) throws Exception {
ImmutableList.Builder<Class<?>> obj = new ImmutableList.Builder<>();
for (Type type : Type.getArgumentTypes(desc)) {
obj.add(ClassUtils.getClass(type.getClassName()));
}
return obj.build();
}
1)In eclipse it is ->right click on the method and select open call hierarchy or CLT+ALT+H
2)In jdeveloper it is -> right click on the method and select calls or ALT+SHIFT+H
The closest that I could find was the method described in this StackOverflow questions selected answer.check this out
You can do this with something in your IDE such as "Find Usages" (which is what it is called in Netbeans and JDeveloper). A couple of things to note:
If your method implements a method from an interface or base class, you can only know that your method is POSSIBLY called.
A lot of Java frameworks use Reflection to call your method (IE Spring, Hibernate, JSF, etc), so be careful of that.
On the same note, your method could be called by some framework, reflectively or not, so again be careful.
Is there a way to set my own custom test case names when using parameterized tests in JUnit4?
I'd like to change the default — [Test class].runTest[n] — to something meaningful.
This feature has made it into JUnit 4.11.
To use change the name of parameterized tests, you say:
#Parameters(name="namestring")
namestring is a string, which can have the following special placeholders:
{index} - the index of this set of arguments. The default namestring is {index}.
{0} - the first parameter value from this invocation of the test.
{1} - the second parameter value
and so on
The final name of the test will be the name of the test method, followed by the namestring in brackets, as shown below.
For example (adapted from the unit test for the Parameterized annotation):
#RunWith(Parameterized.class)
static public class FibonacciTest {
#Parameters( name = "{index}: fib({0})={1}" )
public static Iterable<Object[]> data() {
return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 },
{ 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });
}
private final int fInput;
private final int fExpected;
public FibonacciTest(int input, int expected) {
fInput= input;
fExpected= expected;
}
#Test
public void testFib() {
assertEquals(fExpected, fib(fInput));
}
private int fib(int x) {
// TODO: actually calculate Fibonacci numbers
return 0;
}
}
will give names like testFib[1: fib(1)=1] and testFib[4: fib(4)=3]. (The testFib part of the name is the method name of the #Test).
Looking at JUnit 4.5, its runner clearly doesn't support that, as that logic is buried inside a private class inside the Parameterized class. You could not use the JUnit Parameterized runner, and create your own instead which would understand the concept of names (which leads to the question of how you might set a name ...).
From a JUnit perspective, it would be nice if instead of (or in addition to) just passing an increment, they would pass the comma delimited arguments. TestNG does this. If the feature is important to you, you can comment on the yahoo mailing list referenced at www.junit.org.
I recently came across the same problem when using JUnit 4.3.1. I implemented a new class which extends Parameterized called LabelledParameterized. It has been tested using JUnit 4.3.1, 4.4 and 4.5. It reconstructs the Description instance using the String representation of the first argument of each parameter array from the #Parameters method. You can see the code for this at:
http://code.google.com/p/migen/source/browse/trunk/java/src/.../LabelledParameterized.java?r=3789
and an example of its use at:
http://code.google.com/p/migen/source/browse/trunk/java/src/.../ServerBuilderTest.java?r=3789
The test description formats nicely in Eclipse which is what I wanted since this makes failed tests a lot easier to find! I will probably further refine and document the classes over the next few days/weeks. Drop the '?' part of the URLs if you want the bleeding edge. :-)
To use it, all you have to do is copy that class (GPL v3), and change #RunWith(Parameterized.class) to #RunWith(LabelledParameterized.class) assuming the first element of your parameter list is a sensible label.
I don't know if any later releases of JUnit address this issue but even if they did, I can't update JUnit since all my co-developers would have to update too and we have higher priorities than re-tooling. Hence the work in the class to be compilable by multiple versions of JUnit.
Note: there is some reflection jiggery-pokery so that it runs across the different JUnit versions as listed above. The version specifically for JUnit 4.3.1 can be found here and, for JUnit 4.4 and 4.5, here.
With Parameterized as a model, I wrote my own custom test runner / suite -- only took about half an hour. It's slightly different from darrenp's LabelledParameterized in that it lets you specify a name explicitly rather than relying on the first parameter's toString().
It also doesn't use arrays because I hate arrays. :)
public class PolySuite extends Suite {
// //////////////////////////////
// Public helper interfaces
/**
* Annotation for a method which returns a {#link Configuration}
* to be injected into the test class constructor
*/
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public static #interface Config {
}
public static interface Configuration {
int size();
Object getTestValue(int index);
String getTestName(int index);
}
// //////////////////////////////
// Fields
private final List<Runner> runners;
// //////////////////////////////
// Constructor
/**
* Only called reflectively. Do not use programmatically.
* #param c the test class
* #throws Throwable if something bad happens
*/
public PolySuite(Class<?> c) throws Throwable {
super(c, Collections.<Runner>emptyList());
TestClass testClass = getTestClass();
Class<?> jTestClass = testClass.getJavaClass();
Configuration configuration = getConfiguration(testClass);
List<Runner> runners = new ArrayList<Runner>();
for (int i = 0, size = configuration.size(); i < size; i++) {
SingleRunner runner = new SingleRunner(jTestClass, configuration.getTestValue(i), configuration.getTestName(i));
runners.add(runner);
}
this.runners = runners;
}
// //////////////////////////////
// Overrides
#Override
protected List<Runner> getChildren() {
return runners;
}
// //////////////////////////////
// Private
private Configuration getConfiguration(TestClass testClass) throws Throwable {
return (Configuration) getConfigMethod(testClass).invokeExplosively(null);
}
private FrameworkMethod getConfigMethod(TestClass testClass) {
List<FrameworkMethod> methods = testClass.getAnnotatedMethods(Config.class);
if (methods.isEmpty()) {
throw new IllegalStateException("#" + Config.class.getSimpleName() + " method not found");
}
if (methods.size() > 1) {
throw new IllegalStateException("Too many #" + Config.class.getSimpleName() + " methods");
}
FrameworkMethod method = methods.get(0);
int modifiers = method.getMethod().getModifiers();
if (!(Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new IllegalStateException("#" + Config.class.getSimpleName() + " method \"" + method.getName() + "\" must be public static");
}
return method;
}
// //////////////////////////////
// Helper classes
private static class SingleRunner extends BlockJUnit4ClassRunner {
private final Object testVal;
private final String testName;
SingleRunner(Class<?> testClass, Object testVal, String testName) throws InitializationError {
super(testClass);
this.testVal = testVal;
this.testName = testName;
}
#Override
protected Object createTest() throws Exception {
return getTestClass().getOnlyConstructor().newInstance(testVal);
}
#Override
protected String getName() {
return testName;
}
#Override
protected String testName(FrameworkMethod method) {
return testName + ": " + method.getName();
}
#Override
protected void validateConstructor(List<Throwable> errors) {
validateOnlyOneConstructor(errors);
}
#Override
protected Statement classBlock(RunNotifier notifier) {
return childrenInvoker(notifier);
}
}
}
And an example:
#RunWith(PolySuite.class)
public class PolySuiteExample {
// //////////////////////////////
// Fixture
#Config
public static Configuration getConfig() {
return new Configuration() {
#Override
public int size() {
return 10;
}
#Override
public Integer getTestValue(int index) {
return index * 2;
}
#Override
public String getTestName(int index) {
return "test" + index;
}
};
}
// //////////////////////////////
// Fields
private final int testVal;
// //////////////////////////////
// Constructor
public PolySuiteExample(int testVal) {
this.testVal = testVal;
}
// //////////////////////////////
// Test
#Ignore
#Test
public void odd() {
assertFalse(testVal % 2 == 0);
}
#Test
public void even() {
assertTrue(testVal % 2 == 0);
}
}
You may also want to try JUnitParams: https://github.com/Pragmatists/JUnitParams
from junit4.8.2, you can create your own MyParameterized class by simply copy Parameterized class. change the getName() and testName() methods in TestClassRunnerForParameters.
None of it was working for me, so I got the source for Parameterized and modified it create a a new test runner. I didn't have to change much but IT WORKS!!!
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.junit.Assert;
import org.junit.internal.runners.ClassRoadie;
import org.junit.internal.runners.CompositeRunner;
import org.junit.internal.runners.InitializationError;
import org.junit.internal.runners.JUnit4ClassRunner;
import org.junit.internal.runners.MethodValidator;
import org.junit.internal.runners.TestClass;
import org.junit.runner.notification.RunNotifier;
public class LabelledParameterized extends CompositeRunner {
static class TestClassRunnerForParameters extends JUnit4ClassRunner {
private final Object[] fParameters;
private final String fParameterFirstValue;
private final Constructor<?> fConstructor;
TestClassRunnerForParameters(TestClass testClass, Object[] parameters, int i) throws InitializationError {
super(testClass.getJavaClass()); // todo
fParameters = parameters;
if (parameters != null) {
fParameterFirstValue = Arrays.asList(parameters).toString();
} else {
fParameterFirstValue = String.valueOf(i);
}
fConstructor = getOnlyConstructor();
}
#Override
protected Object createTest() throws Exception {
return fConstructor.newInstance(fParameters);
}
#Override
protected String getName() {
return String.format("%s", fParameterFirstValue);
}
#Override
protected String testName(final Method method) {
return String.format("%s%s", method.getName(), fParameterFirstValue);
}
private Constructor<?> getOnlyConstructor() {
Constructor<?>[] constructors = getTestClass().getJavaClass().getConstructors();
Assert.assertEquals(1, constructors.length);
return constructors[0];
}
#Override
protected void validate() throws InitializationError {
// do nothing: validated before.
}
#Override
public void run(RunNotifier notifier) {
runMethods(notifier);
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public static #interface Parameters {
}
private final TestClass fTestClass;
public LabelledParameterized(Class<?> klass) throws Exception {
super(klass.getName());
fTestClass = new TestClass(klass);
MethodValidator methodValidator = new MethodValidator(fTestClass);
methodValidator.validateStaticMethods();
methodValidator.validateInstanceMethods();
methodValidator.assertValid();
int i = 0;
for (final Object each : getParametersList()) {
if (each instanceof Object[])
add(new TestClassRunnerForParameters(fTestClass, (Object[]) each, i++));
else
throw new Exception(String.format("%s.%s() must return a Collection of arrays.", fTestClass.getName(), getParametersMethod().getName()));
}
}
#Override
public void run(final RunNotifier notifier) {
new ClassRoadie(notifier, fTestClass, getDescription(), new Runnable() {
public void run() {
runChildren(notifier);
}
}).runProtected();
}
private Collection<?> getParametersList() throws IllegalAccessException, InvocationTargetException, Exception {
return (Collection<?>) getParametersMethod().invoke(null);
}
private Method getParametersMethod() throws Exception {
List<Method> methods = fTestClass.getAnnotatedMethods(Parameters.class);
for (Method each : methods) {
int modifiers = each.getModifiers();
if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))
return each;
}
throw new Exception("No public static parameters method on class " + getName());
}
public static Collection<Object[]> eachOne(Object... params) {
List<Object[]> results = new ArrayList<Object[]>();
for (Object param : params)
results.add(new Object[] { param });
return results;
}
}
You can create a method like
#Test
public void name() {
Assert.assertEquals("", inboundFileName);
}
While I wouldn't use it all the time it would be useful to figure out exactly which test number 143 is.
I make extensive use of static import for Assert and friends, so it is easy for me to redefine assertion:
private <T> void assertThat(final T actual, final Matcher<T> expected) {
Assert.assertThat(editThisToDisplaySomethingForYourDatum, actual, expected);
}
For example, you could add a "name" field to your test class, initialized in the constructor, and display that on test failure. Just pass it in as the first elements of your parameters array for each test. This also helps label the data:
public ExampleTest(final String testLabel, final int one, final int two) {
this.testLabel = testLabel;
// ...
}
#Parameters
public static Collection<Object[]> data() {
return asList(new Object[][]{
{"first test", 3, 4},
{"second test", 5, 6}
});
}
A workaround would be to catch and nest all Throwables into a new Throwable with a custom message that contains all information about the parameters. The message would appear in the stack trace.
This works whenever a test fails for all assertions, errors and exceptions as they are all subclasses of Throwable.
My code looks like this:
#RunWith(Parameterized.class)
public class ParameterizedTest {
int parameter;
public ParameterizedTest(int parameter) {
super();
this.parameter = parameter;
}
#Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] { {1}, {2} });
}
#Test
public void test() throws Throwable {
try {
assertTrue(parameter%2==0);
}
catch(Throwable thrown) {
throw new Throwable("parameter="+parameter, thrown);
}
}
}
The stack trace of the failed test is:
java.lang.Throwable: parameter=1
at sample.ParameterizedTest.test(ParameterizedTest.java:34)
Caused by: java.lang.AssertionError
at org.junit.Assert.fail(Assert.java:92)
at org.junit.Assert.assertTrue(Assert.java:43)
at org.junit.Assert.assertTrue(Assert.java:54)
at sample.ParameterizedTest.test(ParameterizedTest.java:31)
... 31 more
When you want the parameter values in test name then you can do something like -
#ParameterizedTest(name="{index} {arguments} then return false" )
#ValueSource(strings = {"false","FALSE"," ","123","abc"})
#DisplayName("When Feature JVM argument is ")
void test_Feature_JVM_Argument_Is_Empty_Or_Blank_Strings_Or_False(String params) {
System.setProperty("FeatureName", params);
assertFalse(Boolean.parseBoolean(System.getProperty("FeatureName")));
}
Test name will look like -
JUnit Test image
Check out JUnitParams as dsaff mentioned, works using ant to build parameterized test method descriptions in the html report.
This was after trying LabelledParameterized and finding that it although it works with eclipse it does not work with ant as far as the html report is concerned.
Cheers,
Since the parameter accessed (e.g. with "{0}" always returns the toString() representation, one workaround would be to make an anonymous implementation and override toString() in each case. For example:
public static Iterable<? extends Object> data() {
return Arrays.asList(
new MyObject(myParams...) {public String toString(){return "my custom test name";}},
new MyObject(myParams...) {public String toString(){return "my other custom test name";}},
//etc...
);
}
Parameterized test is calling toString() internally.
If you create an object wrapper overiding toString(), it will change the names of the test.
Here is an example, I answered in other post.
https://stackoverflow.com/a/67023556/1839360
For a more complex object you may do the following (example with JUnit 4):
#RunWith(Parameterized.class)
public class MainTest {
private static Object[] makeSample(String[] array, int expectedLength) {
return new Object[]{array, expectedLength, Arrays.toString(array)};
}
#Parameterized.Parameters(name = "for input {2} length should equal {1}")
public static Collection<Object[]> data() {
return Arrays.asList(
makeSample(new String[]{"a"}, 1),
makeSample(new String[]{"a", "b"}, 2)
);
}
private final int expectedLength;
private final String[] array;
public MainTest(String[] array, int expectedLength, String strArray) {
this.array = array;
this.expectedLength = expectedLength;
}
#Test
public void should_have_expected_length() {
assertEquals(expectedLength, array.length);
}
}
The trick here is to use one input parameter as a string describing either some part of input or the whole test case.
Before adding third parameter it looked like this
And after like this
Is there a well-established approach for documenting Java "properties" file contents, including:
specifying the data type/contents expected for a given key
specifying whether a key is required for the application to function
providing a description of the key's meaning
Currently, I maintain (by hand) a .properties file that is the default, and I write a prose description of the data type and description of each key in a comment before. This does not lead to a programmatically accessible properties file.
I guess what I'm looking for is a "getopt" equivalent for properties files...
[EDIT: Related]
Java Configuration Frameworks
You could use some of the features in the Apache Commons Configuration package. It at least provides type access to your properties.
There are only conventions in the traditional java properties file. Some I've seen include providing, like you said, an example properties file. Another is to provide the default configuration with all the properties, but commented out.
If you really want to require something, maybe you're not looking for a properties file. You could use an XML configuration file and specify a schema with datatypes and requirements. You can use jaxb to compile the schema into java and read it i that way. With validation you can make sure the required properties are there.
The best you could hope for is when you execute your application, it reads, parses, and validates the properties in the file. If you absolutely had to stay properties based and didn't want to go xml, but needed this parsing. You could have a secondary properties file that listed each property that could be included, its type, and whether it was required. You'd then have to write a properties file validator that would take in a file to validate as well as a validation schema-like properties file. Something like
#list of required properties
required=prop1,prop2,prop3
#all properties and their types
prop1.type=Integer
prop2.type=String
I haven't looked through all of the Apache Configuration package, but they often have useful utilities like this. I wouldn't be surprised if you could find something in there that would simplify this.
Another option to check out is the project called OWNER. There, you define the interface that serves as the configuration object in your application, using types and annotations. Then, OWNER does the finding and parsing of the correct Properties file. Thus, you could write a javadoc for your interface and use that as the documentation.
I have never seen a standard way of doing it. What I would probably do is:
wrap or extend the java.util.Properties class
override (of extending) or provide a method (if wrapping) the store method (or storeToXML, etc) that writes out a comment for each line.
have the method that stores the properties have some sort of input file where you describe the properties of each one.
It doesn't get you anything over what you are doing by hand, except that you can manage the information in a different way that might be easier to deal with - for example you could have a program that spit out the comments to read in. It would potentially give you the programmatic access that you need, but it is a roll-your-own sort of thing.
Or it might just be too much work for too little to gain (which is why there isn't something obvious out there).
If you can specify the sort of comments you want to see I could take a stab at writing something if I get bored :-) (it is the sort of thing I like to do for fun, sick I know :-).
Ok... I got bored... here is something that is at least a start :-)
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
public class PropertiesVerifier
{
private final Map<String, PropertyInfo> optionalInfo;
private final Map<String, PropertyInfo> requiredInfo;
{
optionalInfo = new HashMap<String, PropertyInfo>();
requiredInfo = new HashMap<String, PropertyInfo>();
}
public PropertiesVerifier(final PropertyInfo[] infos)
{
for(final PropertyInfo info : infos)
{
final Map<String, PropertyInfo> infoMap;
if(info.isRequired())
{
infoMap = requiredInfo;
}
else
{
infoMap = optionalInfo;
}
infoMap.put(info.getName(), info);
}
}
public void verifyProperties(final Properties properties)
{
for(final Entry<Object, Object> property : properties.entrySet())
{
final String key;
final String value;
key = (String)property.getKey();
value = (String)property.getValue();
if(!(isValid(key, value)))
{
throw new IllegalArgumentException(value + " is not valid for: " + key);
}
}
}
public boolean isRequired(final String key)
{
return (requiredInfo.get(key) != null);
}
public boolean isOptional(final String key)
{
return (optionalInfo.get(key) != null);
}
public boolean isKnown(final String key)
{
return (isRequired(key) || isOptional(key));
}
public Class getType(final String key)
{
final PropertyInfo info;
info = getPropertyInfoFor(key);
return (info.getType());
}
public boolean isValid(final String key,
final String value)
{
final PropertyInfo info;
info = getPropertyInfoFor(key);
return (info.verify(value));
}
private PropertyInfo getPropertyInfoFor(final String key)
{
PropertyInfo info;
info = requiredInfo.get(key);
if(info == null)
{
info = optionalInfo.get(key);
if(info == null)
{
// should be a better exception maybe... depends on how you
// want to deal with it
throw new IllegalArgumentException(key + "
is not a valid property name");
}
}
return (info);
}
protected final static class PropertyInfo
{
private final String name;
private final boolean required;
private final Class clazz;
private final Verifier verifier;
protected PropertyInfo(final String nm,
final boolean mandatory,
final Class c)
{
this(nm, mandatory, c, getDefaultVerifier(c));
}
protected PropertyInfo(final String nm,
final boolean mandatory,
final Class c,
final Verifier v)
{
// check for null
name = nm;
required = mandatory;
clazz = c;
verifier = v;
}
#Override
public int hashCode()
{
return (getName().hashCode());
}
#Override
public boolean equals(final Object o)
{
final boolean retVal;
if(o instanceof PropertyInfo)
{
final PropertyInfo other;
other = (PropertyInfo)o;
retVal = getName().equals(other.getName());
}
else
{
retVal = false;
}
return (retVal);
}
public boolean verify(final String value)
{
return (verifier.verify(value));
}
public String getName()
{
return (name);
}
public boolean isRequired()
{
return (required);
}
public Class getType()
{
return (clazz);
}
}
private static Verifier getDefaultVerifier(final Class clazz)
{
final Verifier verifier;
if(clazz.equals(Boolean.class))
{
// shoudl use a singleton to save space...
verifier = new BooleanVerifier();
}
else
{
throw new IllegalArgumentException("Unknown property type: " +
clazz.getCanonicalName());
}
return (verifier);
}
public static interface Verifier
{
boolean verify(final String value);
}
public static class BooleanVerifier
implements Verifier
{
public boolean verify(final String value)
{
final boolean retVal;
if(value.equalsIgnoreCase("true") ||
value.equalsIgnoreCase("false"))
{
retVal = true;
}
else
{
retVal = false;
}
return (retVal);
}
}
}
And a simple test for it:
import java.util.Properties;
public class Main
{
public static void main(String[] args)
{
final Properties properties;
final PropertiesVerifier verifier;
properties = new Properties();
properties.put("property.one", "true");
properties.put("property.two", "false");
// properties.put("property.three", "5");
verifier = new PropertiesVerifier(
new PropertiesVerifier.PropertyInfo[]
{
new PropertiesVerifier.PropertyInfo("property.one",
true,
Boolean.class),
new PropertiesVerifier.PropertyInfo("property.two",
false,
Boolean.class),
// new PropertiesVerifier.PropertyInfo("property.three",
// true,
// Boolean.class),
});
System.out.println(verifier.isKnown("property.one"));
System.out.println(verifier.isKnown("property.two"));
System.out.println(verifier.isKnown("property.three"));
System.out.println(verifier.isRequired("property.one"));
System.out.println(verifier.isRequired("property.two"));
System.out.println(verifier.isRequired("property.three"));
System.out.println(verifier.isOptional("property.one"));
System.out.println(verifier.isOptional("property.two"));
System.out.println(verifier.isOptional("property.three"));
System.out.println(verifier.getType("property.one"));
System.out.println(verifier.getType("property.two"));
// System.out.println(verifier.getType("property.tthree"));
System.out.println(verifier.isValid("property.one", "true"));
System.out.println(verifier.isValid("property.two", "false"));
// System.out.println(verifier.isValid("property.tthree", "5"));
verifier.verifyProperties(properties);
}
}
One easy way is to distribute your project with a sample properties file, e.g. my project has in svn a "build.properties.example",with properties commented as necessary. The locally correct properties don't go into svn.
Since you mention "getopt", though, I'm wondering if you're really thinking of cmd line arguments? If there's a "main" that needs specific properties, I usually put it the relevant instructions in a "useage" message that prints out if the arguments are incorrect or "-h".