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

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Nikita A. Visnevski

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

    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
    =============== =============== =========

  • Bryce (Work)

    #2
    Re: Need help with beans - adding properties at run time???

    On Mon, 19 Apr 2004 07:53:53 -0400, "Nikita A. Visnevski"
    <visnev@soma.cr l.mcmaster.ca> wrote:
    [color=blue]
    >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?[/color]

    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.

    --
    now with more cowbell

    Comment

    • Chris Smith

      #3
      Re: Need help with beans - adding properties at run time???

      Nikita A. Visnevski wrote:[color=blue]
      > 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?[/color]

      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.

      --

      The Easiest Way to Train Anyone... Anywhere.

      Chris Smith - Lead Software Developer/Technical Trainer
      MindIQ Corporation

      Comment

      • Nikita A. Visnevski

        #4
        Re: Need help with beans - adding properties at run time???



        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 Smith wrote:
        [color=blue]
        > 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.
        >[/color]

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

        Comment

        • Nikita A. Visnevski

          #5
          Re: Need help with beans - adding properties at run time???

          Chris,

          I just thought of something interesting reflecting upon your message:

          Chris Smith wrote:
          [color=blue]
          > 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.[/color]

          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
          =============== =============== =========

          Comment

          • Chris Smith

            #6
            Re: Need help with beans - adding properties at run time???

            Nikita A. Visnevski wrote:[color=blue]
            > 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?[/color]

            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.refle ct.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/.

            --

            The Easiest Way to Train Anyone... Anywhere.

            Chris Smith - Lead Software Developer/Technical Trainer
            MindIQ Corporation

            Comment

            • Nikita A. Visnevski

              #7
              Re: Need help with beans - adding properties at run time???

              Hi Chris,

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

              Chris Smith wrote:
              [color=blue]
              > As for name conflicts, you can generate the interface using its own
              > ClassLoader. This avoids naming conflicts with other classes in the
              > system.[/color]

              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.

              [color=blue]
              > For bytecode generation, look at http://jakarta.apache.org/bcel/.[/color]

              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
              =============== =============== =========

              Comment

              • Chris Smith

                #8
                Re: Need help with beans - adding properties at run time???

                Nikita A. Visnevski wrote:[color=blue]
                > 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.[/color]

                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.
                [color=blue]
                > 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.[/color]

                That would be my recommendation.

                --

                The Easiest Way to Train Anyone... Anywhere.

                Chris Smith - Lead Software Developer/Technical Trainer
                MindIQ Corporation

                Comment

                • Nikita A. Visnevski

                  #9
                  Re: Need help with beans - adding properties at run time???

                  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 Smith wrote:
                  [color=blue]
                  > 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.[/color]

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

                  Comment

                  • Chris Smith

                    #10
                    Re: Need help with beans - adding properties at run time???

                    Nikita A. Visnevski wrote:[color=blue]
                    > 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.[/color]

                    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.ByteArr ayOutputStream;
                    import java.io.IOExcep tion;
                    import java.lang.refle ct.InvocationHa ndler;
                    import java.lang.refle ct.Method;
                    import java.lang.refle ct.Proxy;

                    import org.apache.bcel .Constants;
                    import org.apache.bcel .generic.ClassG en;
                    import org.apache.bcel .generic.Consta ntPoolGen;
                    import org.apache.bcel .generic.Instru ctionList;
                    import org.apache.bcel .generic.Method Gen;
                    import org.apache.bcel .generic.Type;

                    public class DynamicBeanFact ory implements Constants
                    {
                    private static final class BeanInvocationH andler
                    implements InvocationHandl er
                    {
                    private final BeanAdapter adapter;

                    private BeanInvocationH andler(BeanAdap ter adapter)
                    {
                    this.adapter = adapter;
                    }

                    public Object invoke(Object proxy, Method method,
                    Object[] args) throws Throwable
                    {
                    String methodName = method.getName( );
                    if (methodName.sta rtsWith("set"))
                    {
                    String propName =
                    Character.toLow erCase(methodNa me.charAt(3))
                    + methodName.subs tring(4);
                    adapter.setProp erty(propName, args[0]);
                    return null;
                    }
                    else if (methodName.sta rtsWith("get"))
                    {
                    String propName =
                    Character.toLow erCase(methodNa me.charAt(3))
                    + methodName.subs tring(4);
                    return adapter.getProp erty(propName);
                    }
                    else if (methodName.sta rtsWith("is"))
                    {
                    String propName =
                    Character.toLow erCase(methodNa me.charAt(2))
                    + methodName.subs tring(3);
                    return adapter.getProp erty(propName);
                    }

                    throw new RuntimeExceptio n(method.toStri ng());
                    }
                    }

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

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

                    ByteArrayOutput Stream out = new ByteArrayOutput Stream();

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

                    if (propTypes[i] == boolean.class)
                    {
                    methodName = "is"
                    + Character.toUpp erCase(propName s[i].charAt(0))
                    + propNames[i].substring(1);
                    }
                    else
                    {
                    methodName = "get"
                    + Character.toUpp erCase(propName s[i].charAt(0))
                    + propNames[i].substring(1);
                    }

                    Type t = Type.getType(pr opTypes[i]);

                    InstructionList il = new InstructionList ();
                    MethodGen method = new MethodGen(ACC_P UBLIC | ACC_ABSTRACT,
                    t, Type.NO_ARGS, new String[] { },
                    methodName, CLASS_NAME, il, cp);
                    method.setMaxSt ack();
                    method.setMaxLo cals();
                    cg.addMethod(me thod.getMethod( ));
                    il.dispose();
                    }

                    for (int i = 0; i < propNames.lengt h; i++)
                    {
                    String methodName = "set"
                    + Character.toUpp erCase(propName s[i].charAt(0))
                    + propNames[i].substring(1);

                    Type t = Type.getType(pr opTypes[i]);

                    InstructionList il = new InstructionList ();
                    MethodGen method = new MethodGen(ACC_P UBLIC | ACC_ABSTRACT,
                    Type.VOID, new Type[] { t }, new String[] { "arg" },
                    methodName, CLASS_NAME, il, cp);
                    method.setMaxSt ack();
                    method.setMaxLo cals();
                    cg.addMethod(me thod.getMethod( ));
                    il.dispose();
                    }

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

                    final byte[] classData = out.toByteArray ();
                    ClassLoader loader = new ClassLoader() {
                    protected Class findClass(Strin g name)
                    throws ClassNotFoundEx ception
                    {
                    return defineClass(
                    name, classData, 0, classData.lengt h);
                    }
                    };

                    try
                    {
                    Class ifcClass = loader.loadClas s(CLASS_NAME);
                    return Proxy.newProxyI nstance(
                    loader, new Class[] { ifcClass },
                    new BeanInvocationH andler(adapter) );
                    }
                    catch (ClassNotFoundE xception e)
                    {
                    throw new RuntimeExceptio n(e);
                    }
                    }
                    }

                    --

                    The Easiest Way to Train Anyone... Anywhere.

                    Chris Smith - Lead Software Developer/Technical Trainer
                    MindIQ Corporation

                    Comment

                    • Nikita A. Visnevski

                      #11
                      Re: Need help with beans - adding properties at run time???

                      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 Smith wrote:
                      [color=blue]
                      > 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.[/color]


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

                      Comment

                      Working...