Announcement Announcement Module
Collapse
No announcement yet.
FactoryBean problem: getObject() invoked before properties set Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • FactoryBean problem: getObject() invoked before properties set

    I'm using a FactoryBean to instantiate an object, however, Spring invokes the getObject() method before any properties are set on the factorybean. As far as I know this is not normal behaviour. I must be missing something, but I can't seem to find what it is. The created object is used to run a task every hour.

    Here is the Spring bean configuration:

    Code:
      <bean id="importUpdates" class="nl....domain.logic.spring.ImportUpdatesFactoryBean">
            <property name="xmlConfigAsResource"><value>/.../data/orderupdate.xml</value></property>
            <property name="storeName"><value>statusUpdate</value></property>
            <property name="dataDirectory"><value>data</value></property>
            <property name="archiveDirectory"><value>data/archive</value></property>
            <property name="orderDao"><ref local="orderDAO"/></property>
            <property name="organisationDao"><ref local="organisationDAO"/></property>
        </bean>
        
        <bean id="updateTask" class="org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean">
            <property name="targetObject"><ref bean="importUpdates"/></property>
            <property name="targetMethod"><value>update</value></property>
        </bean>
    ..
    Here is part of the factory bean source:

    Code:
        private URL _xmlConfigLocation;
        private URI _dataDirectory;
        private File _archiveDirectory;
        private String _storeName;
        private OrderDAO _orderDao;
        private OrganisationDAO _organisationDao;
        
            
        /**
         * Sets the XML configuration store location as an URL. 
         */
        public void setXmlConfigAsURL(URL url) {
            if (DEBUG) {
                LOG.debug("setXmlConfigAsURL(" + url + ")");
            }
            if (url == null) {
                throw new NullPointerException("url");
            }
            _xmlConfigLocation = url;                        
        }
        
        /**
         * Sets the XML configuration store location as an resource. 
         */
        public void setXmlConfigAsResource(String resource) {
            if (DEBUG) {
                LOG.debug("setXmlConfigAsResource(" + resource + ")");
            }
            
            if (resource == null) {
                throw new NullPointerException("resource");
            }
            _xmlConfigLocation = getClass().getResource(resource);
            if (_xmlConfigLocation == null) {
                throw new IllegalArgumentException("Resource " + resource + " not found");
            }
        }
    // ...    
        
        /**
         * getObject is an overwritten/implemented
         * method of a parent or interface.
         *
         * @see org.springframework.beans.factory.FactoryBean#getObject()
         */
        public Object getObject() throws Exception {
            if (DEBUG) {
                LOG.debug("getObject()");
            }
            if (_dataDirectory == null) {
                throw new IllegalStateException("Could not instantiate Updater: No data directory");
            }
    
            if (_archiveDirectory == null) {
                throw new IllegalStateException("Could not instantiate Updater: No archive directory");
            }
            if (_xmlConfigLocation == null) {
                throw new IllegalStateException("Could not instantiate Updater: No XML configuration");
            }
    
            if (_storeName == null) {
                throw new IllegalStateException("Could not instantiate Updater: No store name");
            }
            
            if (_organisationDao == null) {
                throw new IllegalStateException("Could not instantiate Updater: " +
                        "No organisation DAO set");
            }
    
            if (_orderDao == null) {
                throw new IllegalStateException("Could not instantiate Updater: No order DAO set");
            }
            
            
            XmlTableStoreFactory tsf = new XmlTableStoreFactory(_xmlConfigLocation, _dataDirectory);
            TableStore ts = tsf.getTableStore(_storeName);
                    
            
            ImportUpdates iu = new ImportUpdates(ts, _archiveDirectory);
            iu.setOrderDAO(_orderDao);
            iu.setOrganisationDAO(_organisationDao);
            return iu;
        }
    
        /**
         * getObjectType is an overwritten/implemented
         * method of a parent or interface.
         *
         * @see org.springframework.beans.factory.FactoryBean#getObjectType()
         */
        public Class getObjectType() {
            return ImportUpdates.class;
        }
    
        /**
         * isSingleton is an overwritten/implemented
         * method of a parent or interface.
         *
         * @see org.springframework.beans.factory.FactoryBean#isSingleton()
         */
        public boolean isSingleton() {
            return true;
        }
    And here is part of the stacktrace, where things go wrong.
    Code:
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'importUpdates': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalStateException: Could not instantiate Updater: No data directory
    java.lang.IllegalStateException: Could not instantiate Updater: No data directory
    	at nl.....domain.logic.spring.ImportUpdatesFactoryBean.getObject(ImportUpdatesFactoryBean.java:144)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForSharedInstance(AbstractBeanFactory.java:794)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:191)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:147)
    	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:176)
    	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:105)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:920)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:731)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:340)
    So can anyone help me out? What am I doing wrong? How can I make it work.
    Last edited by DaVinci79; Dec 20th, 2005, 10:18 AM. Reason: clarifying title

  • #2
    I use Quartz for this kind of stuff, so not too familiar with other options, but can you post your setDataDirectory method to verify you're assigning the injected 'data' value to the instance variable?

    Comment


    • #3
      Here is the rest of the setters. But thats not the problem. It doesn't matter in which order I set it, non of the debug statements are invoked (and DEBUG is set to true).


      Code:
          /**
           * Sets the archive directory. 
           */
          public void setArchiveDirectory(File file) {
              if (DEBUG) {
                  LOG.debug("setArchiveDirectory(" + file + ")");
              }
              if (file == null) {
                  throw new NullPointerException("file");
              }
      
              _archiveDirectory = file;                
          }
          
          /**
           * Sets the data directory. 
           */
          public void setDataDirectory(File file) {
              if (DEBUG) {
                  LOG.debug("setDataDirectory(" + file + ")");
              }
              if (file == null) {
                  throw new NullPointerException("file");
              }
              _dataDirectory = file.toURI();        
          }
          
          /**
           * Sets the store name of the table store, as noted in the Xml configuratie. 
           */
          public void setStoreName(String storeName) {
              if (DEBUG) {
                  LOG.debug("setStoreName(" + storeName + ")");
              }
              if (storeName == null) {
                  throw new NullPointerException("storeName");
              }
              _storeName = storeName;
          }
      
          /**
           * Sets the orderDao of this ImportUpdatesFactoryBean.
           *
           * @param orderDao The OrderDAO 'orderDao' to set.
           */
          public void setOrderDao(OrderDAO orderDao) {
              if (DEBUG) {
                  LOG.debug("setOrderDao(" + orderDao + ")");
              }
              if (orderDao == null) {
                  throw new NullPointerException("orderDao");
              }
      
              _orderDao = orderDao;
          }
          /**
           * Sets the orgDao of this ImportUpdatesFactoryBean.
           *
           * @param orgDao The OrganisationDAO 'orgDao' to set.
           */
          public void setOrganisationDao(OrganisationDAO orgDao) {
              if (DEBUG) {
                  LOG.debug("setOrganisationDao(" + orgDao + ")");
              }
              if (orgDao == null) {
                  throw new NullPointerException("orgDao");
              }
              
              _organisationDao = orgDao;
          }
      The problem must be in the configuration. It doesn't matter what timer I use I think.. I disabled the whole timer thing for testing purposes, and it still gave me an error. Only when I don't reference the object anywhere in my config, it doesn't give me an error, but thats because its not instantiated.

      Comment


      • #4
        The problem is that it attempts to create the TimerFactory when it tries to create the importUpdates object, which in its turn depends on the importUpdates... So its a cycle. I haven't found a solution though. Setting lazy-init to true of false doesn't matter. Anyone please? Here is the exception put in a more readable format.
        Code:
        org.springframework.beans.factory.BeanCreationException: 
        	Error creating bean with name 'importUpdates'
        	defined in ServletContext resource [/WEB-INF/spring-servlet.xml]:
        		Can't resolve reference to bean 'orderDAO' while setting property 'orderDao';
        nested exception is org.springframework.beans.factory.BeanCreationException:
        	Error creating bean with name 'timerFactory' defined in ServletContext resource [/WEB-INF/spring-servlet.xml]:
        		Can't resolve reference to bean 'timerTask' while setting property 'scheduledTimerTasks[0]';
        nested exception is org.springframework.beans.factory.BeanCreationException:
        	Error creating bean with name 'timerTask' defined in ServletContext resource [/WEB-INF/spring-servlet.xml]:
        		Can't resolve reference to bean 'importUpdates' while setting property 'timerTask';
        nested exception is org.springframework.beans.factory.BeanCreationException:
        	Error creating bean with name 'importUpdates': FactoryBean threw exception on object creation;
        nested exception is java.lang.IllegalStateException: Could not instantiate Updater: No data directory
        Last edited by DaVinci79; Dec 22nd, 2005, 06:43 AM.

        Comment


        • #5
          Without reviewing your dependencies in detail, you could look at using a BeanPostProcessor to manually configure an importUpdates object.

          Spring automatically calls any BeanPostProcessor implementations that it finds.

          However, can you also review whether you can refactor your importUpdates object to remove the cyclic dependency?

          Comment


          • #6
            If you consider BeanPostProcessor to be too complicated you can use the depends-on attribute to make sure that the beans are instantiated and set in the approapriate order along with afterPropertiesSet() or init-method attribute to add manual initialization.
            In most cases the circular dependencies can be broken down - usually one bean/component contains too much code and it can be divided into smaller pieces:

            A -> B -> A can be transformed into: A1 -> B -> A2.

            Comment


            • #7
              depends-on won't help if you've got cycle dependencies, as mentioned in the original post.

              Refactoring as I said, and you said is likely required if you want to use standard injection, otherwise use a BeanPostProcessor.

              Comment


              • #8
                I didn't wrote that depends-on by itself works - rather that using depends-on along with afterPropertiesSet can do the trick in some cases - the init method is called after spring has injected all dependecies and inside it you can manually retrieve the app context and get your beans from there.
                However, my advice is to refactor the beans - much cleaner, safer and maintainable.

                Comment

                Working...
                X