Announcement Announcement Module
Collapse
No announcement yet.
FactoryBean for configuring Resources. Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • FactoryBean for configuring Resources.

    Since Spring Resource class is quite flexible, I want to use it to read some binary files, like digital certificates(sufficient to say it's not a trivial properties). However, I don't want to tie my code to Resource, so I create this FactoryBean class to work with InputStreams only. Ideally, we should have singletons only, i.e., reading happens only once. However, it would be better to expand it to prototypes too. I can make it work, but found several glitches. So I post here to see whether I miss anything else, or there is a better way to do this.

    Here is the XMl file, I have a bean certTest that has a field of type Certificate. This field will be populated from a cert file, in a method initMe()(which takes InputStream(s)).
    Code:
    <beans>
        <bean id="certTest" class="...CertTest" singleton="true"/>
    
        <bean id="configAddress" class="test.ResourceSettingFactoryBean">
            <property name="targetObject">
                <ref local="certTest"/>
            </property>
            <property name="initMethod"><value>initMe</value></property>
            <property name="resource">
             <value>classpath&#58;/.../cert.jks</value>
            </property>
            <property name="singleton"><value>true</value></property>
            <lookup-method name="getTargetObject" bean="certTest"/>
        </bean>
    </beans>
    The lookup-method is necessary for prototypes only.
    Here is the Factory class:
    Code:
    /**
     * This is just one more level of indirection, because we don't want to have
     * Spring Resource dependency in application classes.
     *
     * This should return a singleton instance. A prototype instance just doesn't
     * make any sense, you don't want to load resources everytime.
     */
    import org.springframework.core.io.Resource;
    import org.springframework.beans.factory.*;
    import org.springframework.beans.factory.config.*;
    import java.io.*;
    
    public class ResourceSettingFactoryBean implements FactoryBean, InitializingBean
    &#123;
        private Object targetObject;
        private String initMethod;
        private Resource&#91;&#93; resources;
    
        private boolean singleton = true;
        
        public Object getObject&#40;&#41; throws Exception
        &#123;
            if &#40;this.singleton&#41;
            &#123;
                return this.targetObject;
            &#125;
            else
            &#123;
                // The getter is modified through lookup-method.
                this.setTargetObject&#40;this.getTargetObject&#40;&#41;&#41;;
                resourceConfig&#40;&#41;;
                return this.targetObject;
            &#125;
        &#125;
    
        public Class getObjectType&#40;&#41;
        &#123;
            return targetObject.getClass&#40;&#41;;
        &#125;
    
        public void afterPropertiesSet&#40;&#41; throws Exception
        &#123;
            if &#40;resources == null || resources.length == 0&#41;
            &#123;
                throw new IllegalArgumentException&#40;"No resource found"&#41;;
            &#125;
    
            // singleton setting.
            if &#40;this.singleton&#41;
            &#123;
                resourceConfig&#40;&#41;;
            &#125;
            // if it's prototype, we have to open and close everytime, really ugly.
        &#125;
    
        private void resourceConfig&#40;&#41; throws Exception
        &#123;
            MethodInvokingFactoryBean mifb = new MethodInvokingFactoryBean&#40;&#41;;
            mifb.setSingleton&#40;this.singleton&#41;;
            mifb.setTargetObject&#40;this.targetObject&#41;;
            mifb.setTargetMethod&#40;initMethod&#41;;
    
            InputStream&#91;&#93; mr = convertArguments&#40;resources&#41;;
            mifb.setArguments&#40;mr&#41;;
    
            if &#40;this.singleton&#41;
            &#123;
                // mifb will fire off invoke for singletons
                mifb.afterPropertiesSet&#40;&#41;;
            &#125;
            else
            &#123;
                mifb.afterPropertiesSet&#40;&#41;;
                mifb.invoke&#40;&#41;;
            &#125;
            
            closeInputStreams&#40;mr&#41;;
        &#125;
        
        /**
         * convert the arguments of String array to InputStream array, which will be
         * passed to the init method. So this class hides the reference to the
         * Resource class, just use InputStream.
         * @param resourcesArgs Resource&#91;&#93;
         * @return InputStream&#91;&#93;
         * @throws IOException
         */
        private InputStream&#91;&#93; convertArguments&#40;Resource&#91;&#93; resourcesArgs&#41; throws IOException
        &#123;
            InputStream&#91;&#93; inputStreams = new InputStream&#91;resourcesArgs.length&#93;;
    
            for &#40;int i = 0, j = resourcesArgs.length; i < j; i++&#41;
            &#123;
                inputStreams&#91;i&#93; = resourcesArgs&#91;i&#93;.getInputStream&#40;&#41;;
            &#125;
    
    
            return inputStreams;
        &#125;
    
        /**
         * close the input stream in the array. 
         * @param inputStreams InputStream&#91;&#93;
         * @throws IOException
         */
        private void closeInputStreams&#40;InputStream&#91;&#93; inputStreams&#41; throws IOException
        &#123;
            if &#40;inputStreams != null&#41;
            &#123;
                for &#40;int i = 0, j = inputStreams.length; i < j; i++&#41;
                &#123;
                    if &#40;inputStreams&#91;i&#93; != null&#41; inputStreams&#91;i&#93;.close&#40;&#41;;
                &#125;
            &#125;
    
        &#125;
        
        public boolean isSingleton&#40;&#41; &#123; return singleton; &#125;
        public void setSingleton&#40;boolean singleton&#41; &#123; this.singleton = singleton; &#125;
    
        public void setTargetObject&#40;Object targetObject&#41; &#123; this.targetObject = targetObject; &#125;
        public Object getTargetObject&#40;&#41; &#123; return this.targetObject; &#125;
        public void setInitMethod&#40;String initMethod&#41; &#123; this.initMethod = initMethod; &#125;
    
        public void setResource&#40;Resource resource&#41; &#123; setResources&#40;new Resource&#91;&#93;&#123;resource&#125;&#41;; &#125;
        public void setResources&#40;Resource&#91;&#93; resources&#41; &#123; this.resources = resources; &#125;
    &#125;
    I found out:
    1. afterPropertiesSet() in MethodInvokingFactoryBean actually invokes invoke() when singleton=true.
    2. get a new instance with lookup-method in the right place.
    Not sure whether there is anything else wrong.

    Regards,
    Rick
Working...
X