Announcement Announcement Module
Collapse
No announcement yet.
Make Bean With Only Some Property Value Known at Config Time Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Make Bean With Only Some Property Value Known at Config Time

    Hi,

    I have bean which can have multiple instance at runtime. So I cannot make it a "singleton" bean in app context. I did think about making it a prototype bean (i.e. non-singleton) but problem is some property value are known at config time (i.e. when I make app context file -- so can be set with setter injection) and some only known at run time. And to complicate it, my bean cannot do its "initialization" (i.e. afterPropertiesSet) until all property values are set. Example:

    Code:
    public class MyBean implements InitializingBean
    {
      private String propertyKnownAtConfigTime;
      private String propertyKnownAtRunTime;
      // also other properties known at config time and run time
    
      public void setPropertyKnownAtConfigTime(String propertyKnownAtConfigTime)
      {
        this.propertyKnownAtConfigTime = propertyKnownAtConfigTime;
      }
    
      public void setPropertyKnownAtRunTime(String propertyKnownAtRunTime)
      {
        this.propertyKnownAtRunTime = propertyKnownAtRunTime;
      }
    
      public void afterPropertiesSet() throws Exception
      {
        // do something that needs both propertyKnownAtConfigTime 
        // and propertyKnownAtRunTime
      }
    }
    So here is one way how can do this, I think:

    Code:
    <bean name="myBeanConfigTimeProperties" class="java.util.HashMap">
      <constructor-arg index="0">
        <map>
          <entry key="propertyKnownAtConfigTime">
            <value>hello world</value>
          </entry>
          <!-- plus other properties known at config time -->
        </map>
      </constructor-arg>
    </bean>
    At runtime then do this in class that uses MyBean:

    Code:
    public class MyClassThatUsesMyBean
    &#123;
      private Map myBeanConfigTimeProperties;
     
      // setter called by container via setter injection
      public void setMyBeanConfigTimeProperties&#40;Map myBeanConfigTimeProperties&#41;
      &#123;
        this.myBeanConfigTimeProperties = myBeanConfigTimeProperties;
      &#125;
    
      public void useMyBeanForSomething&#40;String foo&#41;
      &#123;
        MyBean myBean = new MyBean&#40;&#41;;
        myBean.setPropertyKnownAtRunTime&#40;foo&#41;;
        BeanWrapper bwMyBean = BeanWrapperImpl&#40;myBean&#41;;
        bwMyBean.setPropertyValues&#40;myBeanConfigTimeProperties&#41;;
        try
        &#123;
          myBean.afterPropertiesSet&#40;&#41;;
        &#125;
        catch&#40;Exception e&#41;
        &#123;
          throw new RuntimeException&#40;e&#41;;
        &#125;
        // now do something with myBean
      &#125;
    &#125;
    Perhaps, is needed, some utility like this for better abstraction:

    Code:
    public class someUtilClass
    &#123;
      public static void setPropertyValues&#40;Object bean, Map propertyValues, boolean callAfterPropertiesSetIfInitializingBean&#41;
      &#123;
        BeanWrapper bwBean = new BeanWrapperImpl&#40;bean&#41;;
        bwBean.setPropertyValues&#40;propertyValues&#41;;
    
        if &#40;callAfterPropertiesSetIfInitializingBean && bean instanceof InitializingBean&#41;
        &#123;
          try
          &#123;
            &#40;&#40;InitializingBean&#41; bean&#41;.afterPropertiesSet&#40;&#41;;
          &#125;
          catch&#40;Exception e&#41;
          &#123;
            throw new RuntimeException&#40;e&#41;;
          &#125;    
        &#125;
      &#125;
    &#125;
    Then if use this util, previous code now look like this:
    Code:
      public void useMyBeanForSomething&#40;String foo&#41;
      &#123;
        MyBean myBean = new MyBean&#40;&#41;;
        myBean.setPropertyKnownAtRunTime&#40;foo&#41;;
        someUtilClass.setPropertyValues&#40;myBean, myBeanConfigTimeProperties, true&#41;;
        // now do something with myBean
      &#125;
    I think this works but maybe Spring already have an abstraction for this that I can use? It seems I reinvent wheel. Am I crazy guy?

  • #2
    Stefano,

    I think Spring already has an abstraction to do this but I can't remember what it is called. I skimmed the manual and the API but I can't see it. It seems like a common problem and, as such, I'm sure the Spring gods must have already solved it.

    Having said that, it seems like your solution could work. One suggestion might be to overload someUtilClass.setPropertyValues() with a signature that allows you to pass the name of an arbitrary initializer method, similar to the init-method attribute of the bean element in an app context file. For example, you could have this signature:
    Code:
    void setPropertyValues&#40;Object bean, Map propertyValues, String initMethod&#41;
    In the implementation, you can invoke the initMethod via reflection -- see AbstractAutowireCapableBeanFactory.invokeCustomIni tMethod() for an example that you can adapt to your needs.

    Comment


    • #3
      Originally posted by cyboc
      One suggestion might be to overload someUtilClass.setPropertyValues() with a signature that allows you to pass the name of an arbitrary initializer method
      Good suggestion. I was just being lazy guy.

      Originally posted by steve_smith
      I did think about making it a prototype bean (i.e. non-singleton) but problem is some property value are known at config time (i.e. when I make app context file -- so can be set with setter injection) and some only known at run time. And to complicate it, my bean cannot do its "initialization" (i.e. afterPropertiesSet) until all property values are set.
      Another reason why I am thinking prototype bean is not working for this problem, is because beans that depend on MyBean can use zero-to-many instance of MyBean. What dependent beans really need is reference to a factory that creates MyBean instances, where some properties values are set in app context file and some set on the fly. Am I crazy guy? Is this already provided in Spring Framework somehow?

      Comment


      • #4
        Originally posted by steve_smith
        What dependent beans really need is reference to a factory that creates MyBean instances, where some properties values are set in app context file and some set on the fly. Am I crazy guy? Is this already provided in Spring Framework somehow?
        Maybe ObjectFactoryCreatingFactoryBean could be useful for you.
        For providing additional runtime parameters for the instances to create, I think you need to implement your own subclass. This is because the getObject() Method itself is parameterless.

        To make it work, I think the factory itself should be declared as prototype. Otherwise there might be problems if multiple threads parameterize the factory and then try to create an instance. Alternatively, your subclass might use ThreadLocal for parameterization. That should eliminate the need for prototype factories.

        Hope that helps,
        Andreas

        Comment


        • #5
          Andreas,

          Thanks for tips. I look into your idea and I think can work. I think two complications are:
          1. additional runtime properties
          2. I think, is best for code reuse, for this FactoryBean to be generic (e.g. additional runtime properties set through property-name-to-property-value map instead of having specific property setters on FactoryBean)
          3. calling init-method (e.g. afterPropertiesSet) only after both config-time and runtime properties have been set first
          For complication 1 and 2, I think can solve with subclass ObjectFactoryCreatingFactoryBean and add method:
          Code:
          public void setRuntimePropertyValues&#40;Map propertyValues&#41;
          Then, need also to override createInstance like this:
          Code:
          protected Object createInstance&#40;&#41; 
          &#123;
            return new ObjectFactory&#40;&#41; 
            &#123;
              public Object getObject&#40;&#41; throws BeansException 
              &#123;
                // get bean with config-time properties set
                Object bean = beanFactory.getBean&#40;targetBeanName&#41;;
          
                // now set also runtime properties
                BeanWrapper bwBean = new BeanWrapperImpl&#40;bean&#41;;
                bwBean.setPropertyValues&#40;runtimePropertyValues&#41;;
          
                return bean;
              &#125;
            &#125;;
          &#125;
          Now for complication 3, I cannot simply have target bean implement InitializingBean. For this, problem is, somewhere in call to beanFactory.getBean(targetBeanName), container will call afterPropertiesSet(). But, you see, this is not good because only config-time properties have been set so far, not runtime properties. So maybe I need to make new interface and have target bean implement it. Example:
          Code:
          public interface RuntimeInitializingBean 
          &#123;
            void afterRuntimePropertiesSet&#40;&#41; throws Exception;
          &#125;
          Then, must change createInstance to somethings like this one:
          Code:
          protected Object createInstance&#40;&#41; 
          &#123;
            return new ObjectFactory&#40;&#41; 
            &#123;
              public Object getObject&#40;&#41; throws BeansException 
              &#123;
                // get bean with config-time properties set
                Object bean = beanFactory.getBean&#40;targetBeanName&#41;;
          
                // now set also runtime properties
                BeanWrapper bwBean = new BeanWrapperImpl&#40;bean&#41;;
                bwBean.setPropertyValues&#40;runtimePropertyValues&#41;;
          
                // now call init method
                if &#40;bean instanceof RuntimeInitializingBean&#41;
                &#123;
                  try
                  &#123;
                    &#40;&#40;RuntimeInitializingBean&#41; bean&#41;.afterRuntimePropertiesSet&#40;&#41;;
                  &#125;
                  catch &#40;Exception e&#41;
                  &#123;
                    throw new BeanInitializationException&#40;
                      "An error occurred in afterRuntimePropertiesSet&#40;&#41;", e&#41;;
                  &#125;
                &#125;
          
                return bean;
              &#125;
            &#125;;
          &#125;
          So what you thinking about this one? Maybe not worth effort (without using ObjectFactoryCreatingFactoryBean, I only need 3 lines of code to create bean - see useMyBeanForSomething() in above post)?

          Maybe Rod can also say about this one?

          Comment


          • #6
            Originally posted by steve_smith
            I think, is best for code reuse, for this FactoryBean to be generic (e.g. additional runtime properties set through property-name-to-property-value map instead of having specific property setters on FactoryBean)
            I agree. A generic subclass using BeanWrapper (as sketched by you) is a preferrable solution.

            Originally posted by steve_smith
            So what you thinking about this one? Maybe not worth effort (without using ObjectFactoryCreatingFactoryBean, I only need 3 lines of code to create bean - see useMyBeanForSomething() in above post)?
            I think a generic approach would be worthwile. It could be applied in different cases as well. For the initialization method I would prefer a reflective approach (like the normal init-method) in favour of an additional attribute.

            Regards,
            Andreas

            Comment


            • #7
              Stefano,

              Give the following a try. I took your code snippets and added in some of Andreas' comments to create an initial implementation. If anyone finds this handy, please let me know.

              First, two new interfaces and the FactoryBean implementation. Note that the FactoryBean allows you to either supply a custom init-method name or you can implement DynamicInitializingBean in the bean that the factory creates:
              Code:
              package com.cbconstantini.core.spring;
              
              import java.util.Map;
              
              import org.springframework.beans.factory.ObjectFactory;
              
              /**
               * An ObjectFactory that allows you to supply bean property values at 
               * dynamically at runtime &#40;ObjectFactory only allows you to supply bean property
               * values at config time&#41;.
               * @author jkelly
               */
              public interface DynamicObjectFactory extends ObjectFactory
              &#123;
                /**
                 * Sets the dynamicPropertyValues that this factory will set on the bean it
                 * creates.
                 * @param dynamicPropertyValues a property-name-to-property-value Map
                 */
                void setDynamicPropertyValues&#40;Map dynamicPropertyValues&#41;;
              &#125;
              Code:
              package com.cbconstantini.core.spring;
              
              /**
               * Interface to be implemented by beans that need to react once all their
               * dynamic properties have been set by a DynamicObjectFactory. For example, to
               * perform custom initialization, or merely to check that all mandatory dynamic
               * properties have been set.
               * @author jkelly
               */
              public interface DynamicInitializingBean
              &#123;
                /**
                 * Invoked by a DynamicObjectFactory after it has set all dynamic bean
                 * properties supplied.
                 * <p>
                 * This method allows the bean instance to perform initialization only
                 * possible when all dynamic bean properties have been set and to throw an
                 * exception in the event of misconfiguration.
                 * @throws Exception in the event of misconfiguration &#40;such as failure to set
                 * an essential property&#41; or if initialization fails.
                 */
                void afterDynamicPropertiesSet&#40;&#41; throws Exception;
              &#125;
              Code:
              package com.cbconstantini.core.spring;
              
              import java.lang.reflect.InvocationTargetException;
              import java.lang.reflect.Method;
              import java.lang.reflect.Modifier;
              import java.util.Map;
              
              import org.springframework.beans.BeanUtils;
              import org.springframework.beans.BeanWrapper;
              import org.springframework.beans.BeanWrapperImpl;
              import org.springframework.beans.BeansException;
              import org.springframework.beans.factory.BeanInitializationException;
              import org.springframework.beans.factory.ObjectFactory;
              import org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean;
              import org.springframework.util.StringUtils;
              
              /**
               * An ObjectFactoryCreatingFactoryBean that creates DynamicObjectFactory instances.
               * @author jkelly
               */
              public class DynamicObjectFactoryCreatingFactoryBean extends
                ObjectFactoryCreatingFactoryBean
              &#123;
                private String dynamicInitMethodName;
                
                /**
                 * Sets the name of the initialization method that this factory will call
                 * after it sets the dynamicPropertyValues.
                 * @param dynamicInitMethodName
                 */
                public void setDynamicInitMethodName&#40;String dynamicInitMethodName&#41;
                &#123;
                  this.dynamicInitMethodName = dynamicInitMethodName;
                &#125;
                
                /**
                 * Overrides ObjectFactoryCreatingFactoryBean.createInstance&#40;&#41; to return a 
                 * DynamicObjectFactory instead of an ObjectFactory.
                 */
                protected Object createInstance&#40;&#41;
                &#123;
                  return new DynamicObjectFactory&#40;&#41;
                  &#123;
                    private Map dynamicPropertyValues;
                    private ObjectFactory wrappedObjectFactory = &#40;ObjectFactory&#41;
                      DynamicObjectFactoryCreatingFactoryBean.super.createInstance&#40;&#41;;
              
                    public void setDynamicPropertyValues&#40;Map dynamicPropertyValues&#41;
                    &#123;
                      this.dynamicPropertyValues = dynamicPropertyValues;
                    &#125;
              
                    public Object getObject&#40;&#41; throws BeansException
                    &#123;
                      // get the bean with only config time property values set
                      Object bean = wrappedObjectFactory.getObject&#40;&#41;;
                      
                      // now set also dynamic property values
                      BeanWrapper bwBean = new BeanWrapperImpl&#40;bean&#41;;
                      bwBean.setPropertyValues&#40;dynamicPropertyValues&#41;;
                
                      // now call either custom dynamic init-method or afterDynamicPropertiesSet&#40;&#41;
                      if &#40;StringUtils.hasText&#40;dynamicInitMethodName&#41;&#41;
                      &#123;
                        Method initMethod = BeanUtils.findMethod&#40;bean.getClass&#40;&#41;,
                          dynamicInitMethodName, null&#41;;
                        if &#40;initMethod == null&#41;
                        &#123;
                          throw new BeanInitializationException&#40;
                            "Couldn't find an init method named '" + dynamicInitMethodName
                              + "' on bean of type '" + bean.getClass&#40;&#41;.getName&#40;&#41; + "'"&#41;;
                        &#125;
               
                        if &#40;!Modifier.isPublic&#40;initMethod.getModifiers&#40;&#41;&#41;&#41;
                        &#123;
                          initMethod.setAccessible&#40;true&#41;;
                        &#125;          
                        
                        try
                        &#123;
                          initMethod.invoke&#40;bean, &#40;Object&#91;&#93;&#41; null&#41;;
                        &#125;
                        catch &#40;InvocationTargetException ite&#41;
                        &#123;
                          throw new BeanInitializationException&#40;
                            "An error occurred in afterDynamicPropertiesSet&#40;&#41;", ite.getTargetException&#40;&#41;&#41;;            
                        &#125;
                        catch &#40;Exception e&#41;
                        &#123;
                          throw new BeanInitializationException&#40;
                            "An error occurred in afterDynamicPropertiesSet&#40;&#41;", e&#41;;            
                        &#125;
                      &#125;
                      else if &#40;bean instanceof DynamicInitializingBean&#41;
                      &#123;
                        try
                        &#123;
                          &#40;&#40;DynamicInitializingBean&#41; bean&#41;.afterDynamicPropertiesSet&#40;&#41;;
                        &#125;
                        catch &#40;Exception e&#41;
                        &#123;
                          throw new BeanInitializationException&#40;
                            "An error occurred in afterDynamicPropertiesSet&#40;&#41;", e&#41;;
                        &#125;
                      &#125;        
                      
                      return bean;
                    &#125;
                  &#125;; 
                &#125;
              &#125;
              Now a TestCase (with accompanying application context file and helper classes) that shows sample usage:

              Code:
              <?xml version="1.0" encoding="UTF-8"?>
              <!DOCTYPE beans PUBLIC  "-//SPRING//DTD BEAN//EN" "http&#58;//www.springframework.org/dtd/spring-beans.dtd">
              
              <!--
                Test data for DynamicObjectFactoryCreatingFactoryBeanTest
              -->
              
              <beans>
                <bean id="prototypeTarget" class="com.cbconstantini.core.spring.MyBean" singleton="false">
                  <property name="propertyKnownAtConfigTime">
                    <value>foo</value>
                  </property>
                </bean> 	
              
                <bean id="prototype" class="com.cbconstantini.core.spring.DynamicObjectFactoryCreatingFactoryBean">
                  <property name="targetBeanName"><idref local="prototypeTarget"/></property>
                </bean> 
                
                <bean id="testBean" class="com.cbconstantini.core.spring.DynamicObjectFactoryCreatingFactoryBeanTest$TestBean">
                  <property name="dynamicObjectFactory"><ref local="prototype"/></property>
                </bean>
                
                <bean id="prototypeTargetWithCustomInit" class="com.cbconstantini.core.spring.MyBeanWithCustomInit" singleton="false">
                  <property name="propertyKnownAtConfigTime">
                    <value>foo</value>
                  </property>
                </bean> 	
              
                <bean id="prototypeWithCustomInit" class="com.cbconstantini.core.spring.DynamicObjectFactoryCreatingFactoryBean">
                  <property name="targetBeanName"><idref local="prototypeTargetWithCustomInit"/></property>
                  <property name="dynamicInitMethodName">
                    <value>myCustomInitMethod</value>
                  </property>
                </bean> 
                
                <bean id="testBeanWithCustomInit" class="com.cbconstantini.core.spring.DynamicObjectFactoryCreatingFactoryBeanTest$TestBean">
                  <property name="dynamicObjectFactory"><ref local="prototypeWithCustomInit"/></property>
                </bean>     
              </beans>
              Code:
              package com.cbconstantini.core.spring;
              
              import java.util.HashMap;
              import java.util.Map;
              
              import org.springframework.beans.factory.BeanFactory;
              import org.springframework.beans.factory.xml.XmlBeanFactory;
              import org.springframework.core.io.ClassPathResource;
              
              import junit.framework.TestCase;
              
              /**
               * TestCase for DynamicObjectFactoryCreatingFactoryBean.
               * @author jkelly
               */
              public class DynamicObjectFactoryCreatingFactoryBeanTest extends TestCase
              &#123;
                private BeanFactory beanFactory;
              
                protected void setUp&#40;&#41; throws Exception
                &#123;
                  super.setUp&#40;&#41;;
                  this.beanFactory = new XmlBeanFactory&#40;new ClassPathResource&#40;
                    "DynamicObjectFactoryCreatingFactoryBeanTest.xml", getClass&#40;&#41;&#41;&#41;;    
                &#125;
              
                public void testGetObject&#40;&#41;
                &#123;
                  // get DynamicObjectFactory
                  TestBean testBean = &#40;TestBean&#41; beanFactory.getBean&#40;"testBean"&#41;;
                  DynamicObjectFactory dynamicObjectFactory = testBean.getDynamicObjectFactory&#40;&#41;;
                 
                  // set dynamicProperties
                  Map dynamicProperties = new HashMap&#40;&#41;;
                  dynamicProperties.put&#40;"propertyKnownAtRunTime", "bar"&#41;;
                  dynamicObjectFactory.setDynamicPropertyValues&#40;dynamicProperties&#41;;
                  
                  // create a bean with the factory
                  Object obj = dynamicObjectFactory.getObject&#40;&#41;;
                  DynamicObjectFactoryTestBean bean = &#40;DynamicObjectFactoryTestBean&#41; dynamicObjectFactory.getObject&#40;&#41;;
                  
                  // do assertions
                  assertEquals&#40;"getConcatenated&#40;&#41;", "foobar", bean.getConcatenated&#40;&#41;&#41;;    
                &#125;
                
                public void testGetObjectWithCustomInit&#40;&#41;
                &#123;
                  // get DynamicObjectFactory
                  TestBean testBean = &#40;TestBean&#41; beanFactory.getBean&#40;"testBeanWithCustomInit"&#41;;
                  DynamicObjectFactory dynamicObjectFactory = testBean.getDynamicObjectFactory&#40;&#41;;
                 
                  // set dynamicProperties
                  Map dynamicProperties = new HashMap&#40;&#41;;
                  dynamicProperties.put&#40;"propertyKnownAtRunTime", "bar"&#41;;
                  dynamicObjectFactory.setDynamicPropertyValues&#40;dynamicProperties&#41;;
                  
                  // create a bean with the factory
                  Object obj = dynamicObjectFactory.getObject&#40;&#41;;
                  DynamicObjectFactoryTestBeanWithCustomInit bean = &#40;DynamicObjectFactoryTestBeanWithCustomInit&#41; dynamicObjectFactory.getObject&#40;&#41;;
                  
                  // do assertions
                  assertEquals&#40;"getConcatenated&#40;&#41;", "barfoo", bean.getConcatenated&#40;&#41;&#41;;    
                &#125;  
                
                public static class TestBean &#123;
                  public DynamicObjectFactory dynamicObjectFactory;
              
                  public DynamicObjectFactory getDynamicObjectFactory&#40;&#41;
                  &#123;
                    return dynamicObjectFactory;
                  &#125;
                  public void setDynamicObjectFactory&#40;
                    DynamicObjectFactory dynamicObjectFactory&#41;
                  &#123;
                    this.dynamicObjectFactory = dynamicObjectFactory;
                  &#125;
                &#125;
              &#125;
              Code:
              package com.cbconstantini.core.spring;
              
              /**
               * Test class to be instantiated by a DynamicObjectFactory in 
               * DynamicObjectFactoryCreatingFactoryBeanTest.
               * @author jkelly
               */
              public class MyBean implements DynamicInitializingBean
              &#123;
                private String propertyKnownAtConfigTime;
                private String propertyKnownAtRunTime;
                private String concatenated;
              
                public void setPropertyKnownAtConfigTime&#40;String propertyKnownAtConfigTime&#41;
                &#123;
                  this.propertyKnownAtConfigTime = propertyKnownAtConfigTime;
                &#125;
              
                public void setPropertyKnownAtRunTime&#40;String propertyKnownAtRunTime&#41;
                &#123;
                  this.propertyKnownAtRunTime = propertyKnownAtRunTime;
                &#125;
                
                public String getConcatenated&#40;&#41;
                &#123;
                  return concatenated;
                &#125;  
                
                /* &#40;non-Javadoc&#41;
                 * @see com.cbconstantini.core.spring.DynamicInitializingBean#afterDynamicPropertiesSet&#40;&#41;
                 */
                public void afterDynamicPropertiesSet&#40;&#41; throws Exception
                &#123;
                  concatenated = propertyKnownAtConfigTime + propertyKnownAtRunTime;
                &#125; 
              &#125;
              Code:
              package com.cbconstantini.core.spring;
              
              /**
               * Test class to be instantiated by a DynamicObjectFactory in 
               * DynamicObjectFactoryCreatingFactoryBeanTest. This class has a custom 
               * init-method called myCustomInitMethod&#40;&#41;.
               * @author jkelly
               */
              public class MyBeanWithCustomInit
              &#123;
                private String propertyKnownAtConfigTime;
                private String propertyKnownAtRunTime;
                private String concatenated;
              
                public void setPropertyKnownAtConfigTime&#40;String propertyKnownAtConfigTime&#41;
                &#123;
                  this.propertyKnownAtConfigTime = propertyKnownAtConfigTime;
                &#125;
              
                public void setPropertyKnownAtRunTime&#40;String propertyKnownAtRunTime&#41;
                &#123;
                  this.propertyKnownAtRunTime = propertyKnownAtRunTime;
                &#125;
                
                public String getConcatenated&#40;&#41;
                &#123;
                  return concatenated;
                &#125;
                
                public void myCustomInitMethod&#40;&#41;
                &#123;
                  concatenated = propertyKnownAtRunTime + propertyKnownAtConfigTime;
                &#125;
              &#125;

              Comment


              • #8
                Cyboc,

                I take quick look just now and I think is great. I think will work for me! I will try closer look today and let you know if any problem.

                Andreas, what you thinking about this one by Cyboc? It seems you can now have custom init method.

                Okay, see you now.

                Comment


                • #9
                  Excellent solution cyboc! I would like to ask you if I can use the above ideas and code in my project?

                  tia,

                  Comment

                  Working...
                  X