org.springframework.statemachine.processor.StateMachineHandlerCallHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.statemachine.processor.StateMachineHandlerCallHelper.java

Source

/*
 * Copyright 2015-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.statemachine.processor;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.annotation.OnEventNotAccepted;
import org.springframework.statemachine.annotation.OnExtendedStateChanged;
import org.springframework.statemachine.annotation.OnStateChanged;
import org.springframework.statemachine.annotation.OnStateEntry;
import org.springframework.statemachine.annotation.OnStateExit;
import org.springframework.statemachine.annotation.OnStateMachineError;
import org.springframework.statemachine.annotation.OnStateMachineStart;
import org.springframework.statemachine.annotation.OnStateMachineStop;
import org.springframework.statemachine.annotation.OnTransition;
import org.springframework.statemachine.annotation.OnTransitionEnd;
import org.springframework.statemachine.annotation.OnTransitionStart;
import org.springframework.statemachine.annotation.WithStateMachine;
import org.springframework.statemachine.config.configuration.StateMachineHandlerApplicationListener;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.support.StateMachineUtils;
import org.springframework.util.Assert;

/**
 * Helper class which is used from a StateMachineObjectSupport to ease handling
 * of StateMachineHandlers and provides needed caching so that a runtime calls
 * are fast. Also provides dedicated methods for each annotated methods so that
 * parameters are handled accordingly.
 *
 * @author Janne Valkealahti
 *
 * @param <S> the type of state
 * @param <E> the type of event
 */
public class StateMachineHandlerCallHelper<S, E> implements InitializingBean, BeanFactoryAware {

    private final Log log = LogFactory.getLog(StateMachineHandlerCallHelper.class);
    private final Map<String, List<CacheEntry>> cache = new HashMap<>();
    private ListableBeanFactory beanFactory;
    private StateMachineHandlerApplicationListener stateMachineHandlerApplicationListener;
    private long last = Long.MIN_VALUE;

    @SuppressWarnings("unchecked")
    @Override
    public void afterPropertiesSet() throws Exception {
        if (!(beanFactory instanceof ListableBeanFactory)) {
            log.info("Beanfactory is not instance of ListableBeanFactory, was " + beanFactory
                    + " thus Disabling handlers.");
            return;
        }
        if (beanFactory.containsBean(StateMachineHandlerApplicationListener.BEAN_NAME)) {
            this.stateMachineHandlerApplicationListener = beanFactory.getBean(
                    StateMachineHandlerApplicationListener.BEAN_NAME, StateMachineHandlerApplicationListener.class);
        }
        for (StateMachineHandler<? extends Annotation, S, E> handler : beanFactory
                .getBeansOfType(StateMachineHandler.class).values()) {
            Annotation annotation = handler.getAnnotation();
            Annotation metaAnnotation = handler.getMetaAnnotation();
            WithStateMachine withStateMachine = AnnotationUtils.findAnnotation(handler.getBeanClass(),
                    WithStateMachine.class);
            String statemachineBeanName = withStateMachine.name();
            String key = metaAnnotation.annotationType().getName() + statemachineBeanName;
            List<CacheEntry> list = cache.get(key);
            if (list == null) {
                list = new ArrayList<>();
                cache.put(key, list);
            }
            list.add(new CacheEntry(handler, annotation, metaAnnotation));
        }
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        Assert.state(beanFactory instanceof ListableBeanFactory,
                "Bean factory must be instance of ListableBeanFactory, was " + beanFactory);
        this.beanFactory = (ListableBeanFactory) beanFactory;
    }

    public void callOnStateChanged(String stateMachineId, StateContext<S, E> stateContext) {
        List<StateMachineHandler<? extends Annotation, S, E>> handlersList = new ArrayList<StateMachineHandler<? extends Annotation, S, E>>();
        String cacheKey = OnStateChanged.class.getName() + stateMachineId;
        List<CacheEntry> list = getCacheEntries(cacheKey);
        if (list == null) {
            return;
        }
        for (CacheEntry entry : list) {
            if (annotationHandlerSourceTargetMatch(
                    (String[]) AnnotationUtils.getValue(entry.metaAnnotation, "source"),
                    (String[]) AnnotationUtils.getValue(entry.metaAnnotation, "target"), entry.annotation,
                    stateContext.getSource(), stateContext.getTarget())) {
                handlersList.add(entry.handler);
            }
        }
        getStateMachineHandlerResults(handlersList, stateContext);
    }

    public void callOnStateEntry(String stateMachineId, StateContext<S, E> stateContext) {
        List<StateMachineHandler<? extends Annotation, S, E>> handlersList = new ArrayList<StateMachineHandler<? extends Annotation, S, E>>();
        String cacheKey = OnStateEntry.class.getName() + stateMachineId;
        List<CacheEntry> list = getCacheEntries(cacheKey);
        if (list == null) {
            return;
        }
        for (CacheEntry entry : list) {
            if (annotationHandlerSourceTargetMatch(
                    (String[]) AnnotationUtils.getValue(entry.metaAnnotation, "source"),
                    (String[]) AnnotationUtils.getValue(entry.metaAnnotation, "target"), entry.annotation,
                    stateContext.getSource(), stateContext.getTarget())) {
                handlersList.add(entry.handler);
            }
        }
        getStateMachineHandlerResults(handlersList, stateContext);
    }

    public void callOnStateExit(String stateMachineId, StateContext<S, E> stateContext) {
        List<StateMachineHandler<? extends Annotation, S, E>> handlersList = new ArrayList<StateMachineHandler<? extends Annotation, S, E>>();
        String cacheKey = OnStateExit.class.getName() + stateMachineId;
        List<CacheEntry> list = getCacheEntries(cacheKey);
        if (list == null) {
            return;
        }
        for (CacheEntry entry : list) {
            if (annotationHandlerSourceTargetMatch(
                    (String[]) AnnotationUtils.getValue(entry.metaAnnotation, "source"),
                    (String[]) AnnotationUtils.getValue(entry.metaAnnotation, "target"), entry.annotation,
                    stateContext.getSource(), stateContext.getTarget())) {
                handlersList.add(entry.handler);
            }
        }
        getStateMachineHandlerResults(handlersList, stateContext);
    }

    public void callOnEventNotAccepted(String stateMachineId, StateContext<S, E> stateContext) {
        List<StateMachineHandler<? extends Annotation, S, E>> handlersList = new ArrayList<StateMachineHandler<? extends Annotation, S, E>>();
        String cacheKey = OnEventNotAccepted.class.getName() + stateMachineId;
        List<CacheEntry> list = getCacheEntries(cacheKey);
        if (list == null) {
            return;
        }
        for (CacheEntry entry : list) {
            E event = stateContext.getEvent();
            if (event != null) {
                if (annotationHandlerEventVariableMatch(entry.metaAnnotation, new String[] { event.toString() })) {
                    handlersList.add(entry.handler);
                }
            } else {
                handlersList.add(entry.handler);
            }
        }
        getStateMachineHandlerResults(handlersList, stateContext);
    }

    public void callOnTransitionStart(String stateMachineId, StateContext<S, E> stateContext) {
        List<StateMachineHandler<? extends Annotation, S, E>> handlersList = new ArrayList<StateMachineHandler<? extends Annotation, S, E>>();
        String cacheKey = OnTransitionStart.class.getName() + stateMachineId;
        List<CacheEntry> list = getCacheEntries(cacheKey);
        if (list == null) {
            return;
        }
        for (CacheEntry entry : list) {
            if (annotationHandlerSourceTargetMatch(
                    (String[]) AnnotationUtils.getValue(entry.metaAnnotation, "source"),
                    (String[]) AnnotationUtils.getValue(entry.metaAnnotation, "target"), entry.annotation,
                    stateContext.getTransition().getSource(), stateContext.getTransition().getTarget())) {
                handlersList.add(entry.handler);
            }
        }
        getStateMachineHandlerResults(handlersList, stateContext);
    }

    public void callOnTransition(String stateMachineId, StateContext<S, E> stateContext) {
        List<StateMachineHandler<? extends Annotation, S, E>> handlersList = new ArrayList<StateMachineHandler<? extends Annotation, S, E>>();
        String cacheKey = OnTransition.class.getName() + stateMachineId;
        List<CacheEntry> list = getCacheEntries(cacheKey);
        if (list == null) {
            return;
        }
        for (CacheEntry entry : list) {
            if (annotationHandlerSourceTargetMatch(
                    (String[]) AnnotationUtils.getValue(entry.metaAnnotation, "source"),
                    (String[]) AnnotationUtils.getValue(entry.metaAnnotation, "target"), entry.annotation,
                    stateContext.getTransition().getSource(), stateContext.getTransition().getTarget())) {
                handlersList.add(entry.handler);
            }
        }
        getStateMachineHandlerResults(handlersList, stateContext);
    }

    public void callOnTransitionEnd(String stateMachineId, StateContext<S, E> stateContext) {
        List<StateMachineHandler<? extends Annotation, S, E>> handlersList = new ArrayList<StateMachineHandler<? extends Annotation, S, E>>();
        String cacheKey = OnTransitionEnd.class.getName() + stateMachineId;
        List<CacheEntry> list = getCacheEntries(cacheKey);
        if (list == null) {
            return;
        }
        for (CacheEntry entry : list) {
            if (annotationHandlerSourceTargetMatch(
                    (String[]) AnnotationUtils.getValue(entry.metaAnnotation, "source"),
                    (String[]) AnnotationUtils.getValue(entry.metaAnnotation, "target"), entry.annotation,
                    stateContext.getTransition().getSource(), stateContext.getTransition().getTarget())) {
                handlersList.add(entry.handler);
            }
        }
        getStateMachineHandlerResults(handlersList, stateContext);
    }

    public void callOnStateMachineStart(String stateMachineId, StateContext<S, E> stateContext) {
        List<StateMachineHandler<? extends Annotation, S, E>> handlersList = new ArrayList<StateMachineHandler<? extends Annotation, S, E>>();
        String cacheKey = OnStateMachineStart.class.getName() + stateMachineId;
        List<CacheEntry> list = getCacheEntries(cacheKey);
        if (list == null) {
            return;
        }
        for (CacheEntry entry : list) {
            handlersList.add(entry.handler);
        }
        getStateMachineHandlerResults(handlersList, stateContext);
    }

    public void callOnStateMachineStop(String stateMachineId, StateContext<S, E> stateContext) {
        List<StateMachineHandler<? extends Annotation, S, E>> handlersList = new ArrayList<StateMachineHandler<? extends Annotation, S, E>>();
        String cacheKey = OnStateMachineStop.class.getName() + stateMachineId;
        List<CacheEntry> list = getCacheEntries(cacheKey);
        if (list == null) {
            return;
        }
        for (CacheEntry entry : list) {
            handlersList.add(entry.handler);
        }
        getStateMachineHandlerResults(handlersList, stateContext);
    }

    public void callOnStateMachineError(String stateMachineId, StateContext<S, E> stateContext) {
        List<StateMachineHandler<? extends Annotation, S, E>> handlersList = new ArrayList<StateMachineHandler<? extends Annotation, S, E>>();
        String cacheKey = OnStateMachineError.class.getName() + stateMachineId;
        List<CacheEntry> list = getCacheEntries(cacheKey);
        if (list == null) {
            return;
        }
        for (CacheEntry entry : list) {
            handlersList.add(entry.handler);
        }
        getStateMachineHandlerResults(handlersList, stateContext);
    }

    public void callOnExtendedStateChanged(String stateMachineId, Object key, Object value,
            StateContext<S, E> stateContext) {
        List<StateMachineHandler<? extends Annotation, S, E>> handlersList = new ArrayList<StateMachineHandler<? extends Annotation, S, E>>();
        String cacheKey = OnExtendedStateChanged.class.getName() + stateMachineId;
        List<CacheEntry> list = getCacheEntries(cacheKey);
        if (list == null) {
            return;
        }
        for (CacheEntry entry : list) {
            if (annotationHandlerVariableMatch(entry.metaAnnotation, key)) {
                handlersList.add(entry.handler);
            }
        }
        getStateMachineHandlerResults(handlersList, stateContext);
    }

    private synchronized List<CacheEntry> getCacheEntries(String cacheKey) {
        if (stateMachineHandlerApplicationListener != null) {
            Long l = stateMachineHandlerApplicationListener.getLastRefreshTime();
            if (l != null && l < System.currentTimeMillis()) {
                if (last != l) {
                    cache.clear();
                    try {
                        afterPropertiesSet();
                    } catch (Exception e) {
                        log.error("Unable to update handler cache", e);
                    }
                    last = l;
                }
            }
        }
        return cache.get(cacheKey);
    }

    private boolean annotationHandlerVariableMatch(Annotation annotation, Object key) {
        boolean handle = false;
        Map<String, Object> annotationAttributes = AnnotationUtils.getAnnotationAttributes(annotation);
        Object object = annotationAttributes.get("key");
        Collection<String> scoll = StateMachineUtils.toStringCollection(object);
        if (!scoll.isEmpty()) {
            if (StateMachineUtils.containsAtleastOne(scoll, StateMachineUtils.toStringCollection(key))) {
                handle = true;
            }
        } else {
            handle = true;
        }
        return handle;
    }

    private boolean annotationHandlerEventVariableMatch(Annotation annotation, Object key) {
        boolean handle = false;
        Map<String, Object> annotationAttributes = AnnotationUtils.getAnnotationAttributes(annotation);
        Object object = annotationAttributes.get("event");
        Collection<String> scoll = StateMachineUtils.toStringCollection(object);
        if (!scoll.isEmpty()) {
            if (StateMachineUtils.containsAtleastOne(scoll, StateMachineUtils.toStringCollection(key))) {
                handle = true;
            }
        } else {
            handle = true;
        }
        return handle;
    }

    private boolean annotationHandlerSourceTargetMatch(String[] msources, String[] mtargets,
            Annotation methodAnnotation, State<S, E> sourceState, State<S, E> targetState) {
        Map<String, Object> annotationAttributes = AnnotationUtils.getAnnotationAttributes(methodAnnotation);
        Object source = annotationAttributes.get("source");
        Object target = annotationAttributes.get("target");

        Collection<String> scoll = StateMachineUtils.toStringCollection(source);
        if (scoll.isEmpty() && msources != null) {
            scoll = Arrays.asList(msources);
        }
        Collection<String> tcoll = StateMachineUtils.toStringCollection(target);
        if (tcoll.isEmpty() && mtargets != null) {
            tcoll = Arrays.asList(mtargets);
        }

        boolean handle = false;
        if (!scoll.isEmpty() && !tcoll.isEmpty()) {
            if (sourceState != null && targetState != null
                    && StateMachineUtils.containsAtleastOne(scoll,
                            StateMachineUtils.toStringCollection(sourceState.getIds()))
                    && StateMachineUtils.containsAtleastOne(tcoll,
                            StateMachineUtils.toStringCollection(targetState.getIds()))) {
                handle = true;
            }
        } else if (!scoll.isEmpty()) {
            if (sourceState != null && StateMachineUtils.containsAtleastOne(scoll,
                    StateMachineUtils.toStringCollection(sourceState.getIds()))) {
                handle = true;
            }
        } else if (!tcoll.isEmpty()) {
            if (targetState != null && StateMachineUtils.containsAtleastOne(tcoll,
                    StateMachineUtils.toStringCollection(targetState.getIds()))) {
                handle = true;
            }
        } else if (scoll.isEmpty() && tcoll.isEmpty()) {
            handle = true;
        }

        return handle;
    }

    private List<Object> getStateMachineHandlerResults(
            List<StateMachineHandler<? extends Annotation, S, E>> stateMachineHandlers,
            final StateContext<S, E> stateContext) {
        StateMachineRuntime<S, E> runtime = new StateMachineRuntime<S, E>() {
            @Override
            public StateContext<S, E> getStateContext() {
                return stateContext;
            }
        };
        List<Object> results = new ArrayList<Object>();
        for (StateMachineHandler<? extends Annotation, S, E> handler : stateMachineHandlers) {
            try {
                results.add(handler.handle(runtime));
            } catch (Throwable e) {
                log.error("Error processing handler " + handler, e);
            }
        }
        return results;
    }

    private class CacheEntry {
        final StateMachineHandler<? extends Annotation, S, E> handler;
        final Annotation annotation;
        final Annotation metaAnnotation;

        public CacheEntry(StateMachineHandler<? extends Annotation, S, E> handler, Annotation annotation,
                Annotation metaAnnotation) {
            this.handler = handler;
            this.annotation = annotation;
            this.metaAnnotation = metaAnnotation;
        }
    }

}