Announcement Announcement Module
Collapse
No announcement yet.
Getting flow definition location path from its id Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Getting flow definition location path from its id

    Hi,

    I need to load a specific resource bundle (other than messages.properties), located next to each XML flow definition file. I have searched the API to find the way to get the location of the flow definition file, but haven't found a clean one of doing that. Hereunder a summary of what I've found.

    The class Flow inherit from FlowDefinition and doesn't provide any information on the location of its configuration file.

    The class DefaultFlowRegistry provide a way to retrieve a FlowModelRegistry, which in turn provide a way toget a FlowModelHolder. The FlowModelHolder contains all the information I'm seeking for, but there's no way of obtaining one because the class DefaultFlowRegistry is visibility package and the method FlowModelRegistryImpl.getFlowModelHolder(String id) is private.

    Finally, there is the class FlowRegistryFactoryBean that is initialized by FlowRegistryBeanDefinitionParser, but those two classes are also package restricted.

    In the end, I would like to mimic what is done in FlowModelFlowBuilder in the method registerMessageSource() with the use of flowResource.createRelative("messages.properties") with my specific bundle.

    One crappy id is to use reflection to make all this stuff accessible (yuk ), but no way to get a package visible class accessible with that trick. Any other more seducing idea would be interesting.

    Thanks in advance.
    Last edited by PomCompot; Dec 13th, 2010, 10:51 AM.

  • #2
    Hook to give access to FlowModelRegistry#getFlowModelHolder

    I have finally found a way (not really clean) to achieve my goal. Hereunder the code. I will post an enhancement ticket on JIRA to ask for giving access to those informations through the exposed Web Flow API. Sample use in the next post.

    Code:
    package org.springframework.webflow.config;
    
    import javax.annotation.PostConstruct;
    import javax.inject.Inject;
    
    import org.springframework.util.Assert;
    import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
    import org.springframework.webflow.engine.model.registry.AccessibleFlowModelRegistryAdapter;
    import org.springframework.webflow.engine.model.registry.FlowModelHolder;
    import org.springframework.webflow.engine.model.registry.FlowModelRegistry;
    import org.springframework.webflow.engine.model.registry.FlowModelRegistryImpl;
    
    /**
     * <p>
     * This class is a hook to give accessibility to the package visible class
     * [email protected] DefaultFlowRegistry}, base implementation of the flow registry, which
     * transitively give access to the [email protected] FlowModelRegistry}.
     * </p>
     * 
     * <p>
     * The final goal is to retrieve [email protected] FlowModelHolder} thanks to the
     * [email protected] FlowModelRegistry} with the flow id.
     * </p>
     * 
     * <p>
     * The typical use is based on [email protected] AccessibleFlowModelRegistryAdapter},
     * Spring bean that call this class to retrieve the [email protected] FlowModelRegistry}.
     * See the documentation of this class for a sample use case.
     * </p>
     * 
     * @see <a href="http://forum.springsource.org/showthread.php?p=334261">Spring
     *      forum related post</a>
     * @see AccessibleFlowModelRegistryAdapter
     */
    public class AccessibleFlowDefinitionRegistryAdapter extends DefaultFlowRegistry {
        /**
         * The official Web Flow flows registry.
         */
        @Inject
        private FlowDefinitionRegistry flowRegistry;
    
        /**
         * Checks to detect a modification of implementation either of
         * [email protected] FlowDefinitionRegistry}, or of [email protected] FlowModelRegistry}.
         */
        @PostConstruct
        public void postConstruct() {
            Assert.notNull(flowRegistry, "flowRegistry bean has not been injected.");
            Assert.isInstanceOf(DefaultFlowRegistry.class, flowRegistry,
                    "The implementation class of FlowDefinitionRegistry doesn't match the expected one (DefaultFlowRegistry). "
                            + "Check if a version upgrade of Spring Web Flow hasn't modified the underlying implementation.");
            Assert.notNull(((DefaultFlowRegistry) flowRegistry).getFlowModelRegistry(), "No FlowModelRegistry found inside flowRegistry bean."
                    + "Check if a version upgrade of Spring Web Flow hasn't modified the underlying implementation.");
            Assert.isInstanceOf(FlowModelRegistryImpl.class, ((DefaultFlowRegistry) flowRegistry).getFlowModelRegistry(),
                    "The implementation class of FlowModelRegistry doesn't match the expected one (FlowModelRegistryImpl). "
                            + "Check if a version upgrade of Spring Web Flow hasn't modified the underlying implementation");
        }
    
        /**
         * Retrieve the [email protected] FlowModelRegistry} associated to the default
         * [email protected] FlowDefinitionRegistry}.
         * 
         * @return the [email protected] FlowModelRegistry}
         */
        @Override
        public FlowModelRegistry getFlowModelRegistry() {
            return ((DefaultFlowRegistry) flowRegistry).getFlowModelRegistry();
        }
    }
    Code:
    package org.springframework.webflow.engine.model.registry;
    
    import java.lang.reflect.Method;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanDefinition;
    import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.beans.factory.support.RootBeanDefinition;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.util.Assert;
    import org.springframework.util.ReflectionUtils;
    import org.springframework.webflow.config.AccessibleFlowDefinitionRegistryAdapter;
    
    /**
     * <p>
     * This class is a hook to give accessibility to the [email protected] FlowModelRegistry},
     * and more specifically to its private methode
     * [email protected] FlowModelRegistry#getFlowModelHolder(String)}. It is based on another
     * hook through the class [email protected] AccessibleFlowDefinitionRegistryAdapter}.
     * </p>
     * 
     * <p>
     * The final goal is to retrieve [email protected] FlowModelHolder} thanks to the
     * [email protected] FlowModelRegistry} with the flow id.
     * </p>
     * 
     * <h1>Sample use</h1>
     * 
     * <h2>Spring configuration file</h2>
     * <code><pre>&lt;bean class="org.springframework.webflow.engine.model.registry.AccessibleFlowModelRegistryAdapter" primary="false" /&gt;</pre></code>
     * 
     * <h2>Concrete use</h2>
     * <p>
     * <code><pre>@Inject
    private AccessibleFlowModelRegistryAdapter flowModelRegistry;
    
    RequestContext requestContext = RequestContextHolder.getRequestContext();
    String flowId = requestContext.getActiveFlow().getId();
    
    FlowModelHolder flowModelHolder = flowModelRegistry.getFlowModelHolder(flowId);</pre></code>
     * 
     * <p>
     * This class implements [email protected] BeanFactoryPostProcessor} and
     * [email protected] ApplicationContextAware} interfaces because it's responsible for
     * registering and retrieveing the
     * [email protected] AccessibleFlowDefinitionRegistryAdapter}.
     * </p>
     * 
     * @see <a href="http://forum.springsource.org/showthread.php?p=334261">Spring
     *      forum related post</a>
     * @see AccessibleFlowDefinitionRegistryAdapter
     */
    public class AccessibleFlowModelRegistryAdapter extends FlowModelRegistryImpl implements BeanFactoryPostProcessor, ApplicationContextAware {
        /**
         * The [email protected] ApplicationContext} setted by Spring when the bean is
         * initialized thanks to the implementation [email protected] ApplicationContextAware}.
         */
        private ApplicationContext applicationContext;
    
        /**
         * <p>
         * Key method which is the main goal of all this hook. It give the ability
         * to retrieve a [email protected] FlowModelHolder} associated to a flow from its
         * flowId. This methode retrieve the [email protected] FlowModelHolder} from the
         * underlying [email protected] FlowModelRegistry}.
         * </p>
         * 
         * <p>
         * This method give access to the normally inaccessible private
         * [email protected] #getFlowModelHolder(String)} of [email protected] FlowModelRegistryImpl}
         * through some reflection.
         * </p>
         * 
         * @param id
         *            the flowId for which we want to retrieve the
         *            [email protected] FlowModelHolder}
         * @return the [email protected] FlowModelHolder} of the specified flow or
         *         <code>null</code> if there is no [email protected] FlowModelHolder}
         *         associated to this flowId
         */
        public FlowModelHolder getFlowModelHolder(String id) {
            Method getFlowModelHolderMethod = ReflectionUtils.findMethod(FlowModelRegistryImpl.class, "getFlowModelHolder", String.class);
            Assert.notNull(getFlowModelHolderMethod, "La méthode getFlowModelHolder(String id) n'a pas été trouvée sur la classe FlowModelRegistryImpl."
                    + "Vérifier si une montée de version de Spring Web Flow n'aurait pas modifié l'implémentation sous-jacente.");
            ReflectionUtils.makeAccessible(getFlowModelHolderMethod);
            FlowModelHolder flowModelHolder = (FlowModelHolder) ReflectionUtils.invokeMethod(getFlowModelHolderMethod, getFlowRegistry()
                    .getFlowModelRegistry(), id);
            return flowModelHolder;
        }
    
        /**
         * [email protected]}
         */
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    
        /**
         * Getter to retrieve the [email protected] AccessibleFlowDefinitionRegistryAdapter}
         * bean. This bean is not injected because its definition is registered by
         * this self class (through the implementation of
         * [email protected] BeanFactoryPostProcessor}) and injecting a bean in the bean
         * registering it doesn't work. So we retrieve it through
         * [email protected] ApplicationContext#getBean(String, Class)}.
         * 
         * @return the [email protected] AccessibleFlowDefinitionRegistryAdapter}
         */
        private AccessibleFlowDefinitionRegistryAdapter getFlowRegistry() {
            return applicationContext.getBean(getAccessibleFlowDefinitionRegistryAdapterName(), AccessibleFlowDefinitionRegistryAdapter.class);
        }
    
        /**
         * The implentation of [email protected] BeanFactoryPostProcessor} preserve from
         * declaring the two hook beans (
         * [email protected] AccessibleFlowDefinitionRegistryAdapter} and
         * [email protected] AccessibleFlowModelRegistryAdapter}) in the XML configuration.
         * 
         * [email protected]}
         */
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
            BeanDefinition flowDefinitionRegistryBean = new RootBeanDefinition(AccessibleFlowDefinitionRegistryAdapter.class);
            flowDefinitionRegistryBean.setPrimary(false);
            registry.registerBeanDefinition(getAccessibleFlowDefinitionRegistryAdapterName(), flowDefinitionRegistryBean);
        }
    
        /**
         * Retrieve a camel case unique identifier of the
         * [email protected] AccessibleFlowDefinitionRegistryAdapter} bean derived from the
         * className.
         * 
         * @return the unique identifier of the
         *         [email protected] AccessibleFlowDefinitionRegistryAdapter} bean
         */
        private String getAccessibleFlowDefinitionRegistryAdapterName() {
            String className = AccessibleFlowDefinitionRegistryAdapter.class.getName();
            return className.substring(0, 1).toLowerCase() + className.substring(1);
        }
    }

    Comment


    • #3
      One more class and Sample use

      AccessibleFlowRelativeResourceLoader class :

      Code:
      package org.springframework.webflow.engine.builder.model;
      
      import org.springframework.context.support.ReloadableResourceBundleMessageSource;
      import org.springframework.core.io.Resource;
      
      /**
       * <p>
       * This class is a very little hook to give access to the class
       * [email protected] FlowRelativeResourceLoader}. This class give the ability to load
       * resources that are located next to the flow.
       * </p>
       * 
       * <p>
       * For instance, it can be used to define another resource loader than the
       * default one on [email protected] ReloadableResourceBundleMessageSource} to load some
       * properties file next to the flow.
       * </p>
       */
      public class AccessibleFlowRelativeResourceLoader extends FlowRelativeResourceLoader {
          /**
           * Build a new [email protected] AccessibleFlowRelativeResourceLoader}, delegating the
           * construction to the super class.
           * 
           * @param resource
           *            the flow resource which is used as reference to load neighbour
           *            resources.
           */
          public AccessibleFlowRelativeResourceLoader(Resource resource) {
              super(resource);
          }
      }
      Sample use :

      Code:
      <bean class="org.springframework.webflow.engine.model.registry.AccessibleFlowModelRegistryAdapter" primary="false" />
      Code:
      private static final String[] DEFAULT_FLOW_ERROR_BUNDLES = new String[] {"errors"};
      
      @Inject
      private AccessibleFlowModelRegistryAdapter flowModelRegistry;
      
      [...]
      
      FlowModelHolder flowModelHolder = flowModelRegistry.getFlowModelHolder(flowId);
      Resource flowModelResource = flowModelHolder.getFlowModelResource();
      AccessibleFlowRelativeResourceLoader flowRelativeResourceLoader = new AccessibleFlowRelativeResourceLoader(flowModelResource);
      
      ReloadableResourceBundleMessageSource flowErrorResourceBundle = new ReloadableResourceBundleMessageSource();
      flowErrorResourceBundle.setResourceLoader(flowRelativeResourceLoader);
      flowErrorResourceBundle.setBasenames(DEFAULT_FLOW_ERROR_BUNDLES);
      flowErrorMessageSource = flowErrorResourceBundle;

      Comment


      • #4
        JIRA Ticket

        Here the related JIRA ticket: https://jira.springframework.org/browse/SWF-1441.

        Comment

        Working...
        X