Need help with beans - adding properties at run time???

  • Thread starter Nikita A. Visnevski
  • Start date
N

Nikita A. Visnevski

Hi everyone,

I am rather new to Java beans. Just picked up a book last night and
started reading about them. I am building an application that allows a
user to define objects with dynamic properties. Something similar to a
java hash table. One can add new properties at runtime and remove them
as well.

I have a really fancy third party property inspector available to me
that I would very much like to reuse. This inspector uses beans API.
My hope was that I could build a set of Java bean wrappers for my
dynamic objects such that they could be inspectable in this inspector.

Well, from what I read so far it seams that I have to have a getXXX and
setXXX method defined in my wrapper class for every property I want to
inspect. I can not define these methods since I have no idea what
properties the user might add to my original object. Is there any way
to work around this difficulty?

Any help is appreciated.

Nik


--
=======================================
Nikita A. Visnevski
Adaptive Systems Laboratory
CRL, McMaster University
Phone : (905) 525-9140 x 27282
Web : http://soma.crl.mcmaster.ca
=======================================
 
B

Bryce (Work)

Hi everyone,

I am rather new to Java beans. Just picked up a book last night and
started reading about them. I am building an application that allows a
user to define objects with dynamic properties. Something similar to a
java hash table. One can add new properties at runtime and remove them
as well.

I have a really fancy third party property inspector available to me
that I would very much like to reuse. This inspector uses beans API.
My hope was that I could build a set of Java bean wrappers for my
dynamic objects such that they could be inspectable in this inspector.

Well, from what I read so far it seams that I have to have a getXXX and
setXXX method defined in my wrapper class for every property I want to
inspect. I can not define these methods since I have no idea what
properties the user might add to my original object. Is there any way
to work around this difficulty?

The only thing I can think of is using java reflection and proxy
methods... That would be a bit complicated though.

I, personally, would us a hashtable or something to map a property to
a value.
 
C

Chris Smith

Nikita said:
Well, from what I read so far it seams that I have to have a getXXX and
setXXX method defined in my wrapper class for every property I want to
inspect. I can not define these methods since I have no idea what
properties the user might add to my original object. Is there any way
to work around this difficulty?

In the end, you'll have to provide a pair of accessor/mutator methods
for each property. You don't have to call them setXXX and getXXX is you
don't want to (see BeanInfo for an alternate way to specify these
methods if they have different names), but you do need for them to
exist.

One way around this, if you're very determined, may be for you to
generate a JavaBeans wrapper class around your original table-like
object. You'd need to do some Java code generation, and BCEL would be
very helpful in accomplishing this. Essentially, you'd want to generate
a class whose objects hold a reference to your original object and
expose a set of methods that look like JavaBeans methods. These methods
would pass their operations through to your original object. You'd need
to generate the bean class and object on the fly whenever you want to
inspect the data as if it were a JavaBean.

--
www.designacourse.com
The Easiest Way to Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
N

Nikita A. Visnevski

Chris, Bryce,

thanks for your replies. I just arrived at the same conclusion
regarding the on-the-fly code generation. The idea of the Proxy object
seemed very attractive, but when I dug deeper I realized that everything
boils down to the interface with accessor/mutator methods that I would
have to provide. Well, it is exactly the interface that I do not have.
So, I suppose, my choices are:
1. Generate interfaces or perhaps even the entire BeanInfo classes on
the fly.
2. Abandon this idea altogether and write my own inspector for
inspecting Java hashtable-like objects.

Neither 1, nor 2 sound very exciting :(

Here is a question (since I have never done something like that before).
Suppose I can generate an interface on the fly into a java file. I
would have to compile it. Suppose I already have a class with the same
name loaded into jvm. Can I unload/reload/update it with the new class,
or should I just create and load an entirely new class with new name and
forget about the old one?

Thanks in advance.

Nik

Chris said:
In the end, you'll have to provide a pair of accessor/mutator methods
for each property. You don't have to call them setXXX and getXXX is you
don't want to (see BeanInfo for an alternate way to specify these
methods if they have different names), but you do need for them to
exist.

One way around this, if you're very determined, may be for you to
generate a JavaBeans wrapper class around your original table-like
object. You'd need to do some Java code generation, and BCEL would be
very helpful in accomplishing this. Essentially, you'd want to generate
a class whose objects hold a reference to your original object and
expose a set of methods that look like JavaBeans methods. These methods
would pass their operations through to your original object. You'd need
to generate the bean class and object on the fly whenever you want to
inspect the data as if it were a JavaBean.

--
=======================================
Nikita A. Visnevski
Adaptive Systems Laboratory
CRL, McMaster University
Phone : (905) 525-9140 x 27282
Web : http://soma.crl.mcmaster.ca
=======================================
 
N

Nikita A. Visnevski

Chris,

I just thought of something interesting reflecting upon your message:

Chris said:
In the end, you'll have to provide a pair of accessor/mutator methods
for each property. You don't have to call them setXXX and getXXX is you
don't want to (see BeanInfo for an alternate way to specify these
methods if they have different names), but you do need for them to
exist.

Instead of providing a pair for each property, is there a way to define
one accessor and one mutator method in the BeanInfo, that would somehow
be able to handle all properties. That would include:

1. In the BeanInfo, mapping all accessors to one method, and all
mutators to the other
2. Distinguishing within the accessor/mutator methods which property
they were called for.

As I said, I have a very limited Java beans experiance, but this is how
I would've done that in C/C++ or some other language.

Thanks,

Nik

--
=======================================
Nikita A. Visnevski
Adaptive Systems Laboratory
CRL, McMaster University
Phone : (905) 525-9140 x 27282
Web : http://soma.crl.mcmaster.ca
=======================================
 
C

Chris Smith

Nikita said:
Here is a question (since I have never done something like that before).
Suppose I can generate an interface on the fly into a java file. I
would have to compile it. Suppose I already have a class with the same
name loaded into jvm. Can I unload/reload/update it with the new class,
or should I just create and load an entirely new class with new name and
forget about the old one?

First of all, I'd suggest not creating a Java source file and then
compiling. Instead, generate the bytecode directly. You then don't
need to worry about a compiler at all; and a bytecode loader is a
required piece of any compliant JVM. If you generate interfaces and
then implement them with java.lang.reflect.Proxy, then this should not
be too awfully difficult.

As for name conflicts, you can generate the interface using its own
ClassLoader. This avoids naming conflicts with other classes in the
system.

For bytecode generation, look at http://jakarta.apache.org/bcel/.

--
www.designacourse.com
The Easiest Way to Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
N

Nikita A. Visnevski

Hi Chris,

thanks for your help. I have a few more points to clarify:

Chris said:
As for name conflicts, you can generate the interface using its own
ClassLoader. This avoids naming conflicts with other classes in the
system.

I guess I did not really make myself clear (my ignorance in the
functionality of JVMs is to blame I suppose). When I asked about
reloading/updating classes, I meant in fact the following.

Suppose I have a byte-code of an already compiled class. Let's call it
Foo.class. In my application I instantiate an object of this class (or
load this class in some other way - system ClassLoader, Class.forName
method, etc.). Suppose at some point in time later I have to change
this class. Suppose I use this BCEL package to generate bytecode of the
new class. From what I understand, the last step will just
update/recreate the Foo.class file, am I right? If I am right, how do I
then update this class within the JVM? Is there an analog of reloading
the class from the newly created bytecode? Perhaps I simply
misunderstand something and matter is simpler than it appears to me. I
just do not understand how do I now use this new bytecode for a class
that has already previously been loaded into JVM.

For bytecode generation, look at http://jakarta.apache.org/bcel/.

Thanks a lot for this link. I might go for it and try to generate
interfaces on the fly and use the Proxy object to create the beans.

Thank you for your help once again.

Nik

--
=======================================
Nikita A. Visnevski
Adaptive Systems Laboratory
CRL, McMaster University
Phone : (905) 525-9140 x 27282
Web : http://soma.crl.mcmaster.ca
=======================================
 
C

Chris Smith

Nikita said:
I guess I did not really make myself clear (my ignorance in the
functionality of JVMs is to blame I suppose). When I asked about
reloading/updating classes, I meant in fact the following.

Suppose I have a byte-code of an already compiled class. Let's call it
Foo.class. In my application I instantiate an object of this class (or
load this class in some other way - system ClassLoader, Class.forName
method, etc.). Suppose at some point in time later I have to change
this class. Suppose I use this BCEL package to generate bytecode of the
new class. From what I understand, the last step will just
update/recreate the Foo.class file, am I right? If I am right, how do I
then update this class within the JVM? Is there an analog of reloading
the class from the newly created bytecode? Perhaps I simply
misunderstand something and matter is simpler than it appears to me. I
just do not understand how do I now use this new bytecode for a class
that has already previously been loaded into JVM.

I think my previous answer applies. You can never change a class in the
JVM once it's been loaded (except through means intended for debuggers
and the like, which you don't want to use for this). What you'd do is
separate your object from the JavaBean, and then create new JavaBean
objects as required.

If you're following my advice, when you create these new JavaBean
objects, you will not do it with Class.forName or the system
ClassLoader. You'll do it with a new ClassLoader that you've created
for exactly that purpose. Each time you need to update the JavaBean
with a different set of properties, you will create a new ClassLoader
and discard the old one, leaving it for the garbage collector. Naming
conflicts will never be an issue, since you'll only load one class with
that ClassLoader.
Thanks a lot for this link. I might go for it and try to generate
interfaces on the fly and use the Proxy object to create the beans.

That would be my recommendation.

--
www.designacourse.com
The Easiest Way to Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
N

Nikita A. Visnevski

Hi Chris,

well, I just started playing with the BCEL package. I was totally
confused before. Stuff seems to work really well. I really enjoy the
package. This is exactly what I need. Thank you very much once again.

Nik

Chris said:
I think my previous answer applies. You can never change a class in the
JVM once it's been loaded (except through means intended for debuggers
and the like, which you don't want to use for this). What you'd do is
separate your object from the JavaBean, and then create new JavaBean
objects as required.

If you're following my advice, when you create these new JavaBean
objects, you will not do it with Class.forName or the system
ClassLoader. You'll do it with a new ClassLoader that you've created
for exactly that purpose. Each time you need to update the JavaBean
with a different set of properties, you will create a new ClassLoader
and discard the old one, leaving it for the garbage collector. Naming
conflicts will never be an issue, since you'll only load one class with
that ClassLoader.

--
=======================================
Nikita A. Visnevski
Adaptive Systems Laboratory
CRL, McMaster University
Phone : (905) 525-9140 x 27282
Web : http://soma.crl.mcmaster.ca
=======================================
 
C

Chris Smith

Nikita said:
well, I just started playing with the BCEL package. I was totally
confused before. Stuff seems to work really well. I really enjoy the
package. This is exactly what I need. Thank you very much once again.

Well, I found your project interesting here's basically what you need.
Just call createBean and pass it a list of the bean properties and an
instance of BeanAdapter that knows how to store and retrieve bean data.
It's not perfect, but it's a start. Someone with more knowledge of the
JavaBeans specification could probably do a better job of getting all
the border cases right.


import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.apache.bcel.Constants;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.Type;

public class DynamicBeanFactory implements Constants
{
private static final class BeanInvocationHandler
implements InvocationHandler
{
private final BeanAdapter adapter;

private BeanInvocationHandler(BeanAdapter adapter)
{
this.adapter = adapter;
}

public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable
{
String methodName = method.getName();
if (methodName.startsWith("set"))
{
String propName =
Character.toLowerCase(methodName.charAt(3))
+ methodName.substring(4);
adapter.setProperty(propName, args[0]);
return null;
}
else if (methodName.startsWith("get"))
{
String propName =
Character.toLowerCase(methodName.charAt(3))
+ methodName.substring(4);
return adapter.getProperty(propName);
}
else if (methodName.startsWith("is"))
{
String propName =
Character.toLowerCase(methodName.charAt(2))
+ methodName.substring(3);
return adapter.getProperty(propName);
}

throw new RuntimeException(method.toString());
}
}

public static interface BeanAdapter
{
public void setProperty(String name, Object value);
public Object getProperty(String name);
}

public static Object createBean(
String[] propNames,
Class[] propTypes,
BeanAdapter adapter)
{
final String CLASS_NAME = "net.twu.cdsmith.BeanInterface";
ClassGen cg = new ClassGen(
CLASS_NAME, "java.lang.Object", null,
ACC_PUBLIC | ACC_INTERFACE | ACC_ABSTRACT,
new String[] { });
ConstantPoolGen cp = cg.getConstantPool();

ByteArrayOutputStream out = new ByteArrayOutputStream();

for (int i = 0; i < propNames.length; i++)
{
String methodName;

if (propTypes == boolean.class)
{
methodName = "is"
+ Character.toUpperCase(propNames.charAt(0))
+ propNames.substring(1);
}
else
{
methodName = "get"
+ Character.toUpperCase(propNames.charAt(0))
+ propNames.substring(1);
}

Type t = Type.getType(propTypes);

InstructionList il = new InstructionList();
MethodGen method = new MethodGen(ACC_PUBLIC | ACC_ABSTRACT,
t, Type.NO_ARGS, new String[] { },
methodName, CLASS_NAME, il, cp);
method.setMaxStack();
method.setMaxLocals();
cg.addMethod(method.getMethod());
il.dispose();
}

for (int i = 0; i < propNames.length; i++)
{
String methodName = "set"
+ Character.toUpperCase(propNames.charAt(0))
+ propNames.substring(1);

Type t = Type.getType(propTypes);

InstructionList il = new InstructionList();
MethodGen method = new MethodGen(ACC_PUBLIC | ACC_ABSTRACT,
Type.VOID, new Type[] { t }, new String[] { "arg" },
methodName, CLASS_NAME, il, cp);
method.setMaxStack();
method.setMaxLocals();
cg.addMethod(method.getMethod());
il.dispose();
}

try
{
cg.getJavaClass().dump(out);
}
catch (IOException e)
{
throw new RuntimeException(e);
}

final byte[] classData = out.toByteArray();
ClassLoader loader = new ClassLoader() {
protected Class findClass(String name)
throws ClassNotFoundException
{
return defineClass(
name, classData, 0, classData.length);
}
};

try
{
Class ifcClass = loader.loadClass(CLASS_NAME);
return Proxy.newProxyInstance(
loader, new Class[] { ifcClass },
new BeanInvocationHandler(adapter));
}
catch (ClassNotFoundException e)
{
throw new RuntimeException(e);
}
}
}

--
www.designacourse.com
The Easiest Way to Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
N

Nikita A. Visnevski

WOW!!! Thanks Chris,

I have started playing with your example. I have not made it work yet
for my needs, but I think I am on the right track. I am going to resume
tomorrow and see how far I can get with this stuff. The sample that you
created makes a lot of sense and it seems like it is exactly what I need.

If you are really interested in my project, I can give you a few more
details. I am actually developing an electronic warfare software
prototype of a radar warning receiver for my Ph.D. thesis. The software
is mostly written in Matlab/Simulink as well as C/C++. It does some
really cool stuff, but it lacks any kind of user interface at this
point. Radar emitter library that I have created is customizable
through a set of files that contain property-value pairs. I have a
customer for the prototype - Canadian Defence Department. They are
paying for my studies and want some nice GUI on top of the project. I
am allowed to see some emitter definition files, but others have a high
military clearance requirement, and I have no access to them. God only
knows what parameters they define there.

So, Matlab has this property inspector built in that is JavaBeans
compliant. I have developed a set of flexible Matlab objects to
represent emitter library entries, but in order for me to inspect them
in the Matlab's inspector, I have to create java bean wrappers for my
Matlab objects the structure of which I do not necessarily know. I hope
that I can make it work using the example that you gave me. This would
be the cleanest and most painless solution to the GUI front end for my
project.

Anyhow, thank you very much once again for your help.

Nik

Chris said:
Well, I found your project interesting here's basically what you need.
Just call createBean and pass it a list of the bean properties and an
instance of BeanAdapter that knows how to store and retrieve bean data.
It's not perfect, but it's a start. Someone with more knowledge of the
JavaBeans specification could probably do a better job of getting all
the border cases right.


--
=======================================
Nikita A. Visnevski
Adaptive Systems Laboratory
CRL, McMaster University
Phone : (905) 525-9140 x 27282
Web : http://soma.crl.mcmaster.ca
=======================================
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,999
Messages
2,570,247
Members
46,844
Latest member
JudyGvh32

Latest Threads

Top