package org.apereo.cas.web.report;
import org.apache.commons.lang3.StringUtils;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.springframework.binding.expression.Expression;
import org.springframework.http.MediaType;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.engine.EndState;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.engine.FlowVariable;
import org.springframework.webflow.engine.State;
import org.springframework.webflow.engine.TransitionableState;
import org.springframework.webflow.engine.ViewState;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
/**
* This is {@link SpringWebflowReportController}.
*
* @author Misagh Moayyed
* @since 5.1.0
*/
public class SpringWebflowReportController extends BaseCasMvcEndpoint {
/**
* Instantiates a new Base cas mvc endpoint.
*
* @param casProperties the cas properties
*/
public SpringWebflowReportController(final CasConfigurationProperties casProperties) {
super("swfReport", "/swf", casProperties.getMonitor().getEndpoints().getSpringWebflowReport(), casProperties);
}
/**
* Get SWF report.
*
* @return JSON representing the current state of SWF.
*/
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Map<?, ?> getReport() {
final Map<String, Object> jsonMap = new HashMap<>();
final Map<String, FlowDefinitionRegistry> map =
this.applicationContext.getBeansOfType(FlowDefinitionRegistry.class, false, true);
map.forEach((k, v) -> {
Arrays.stream(v.getFlowDefinitionIds()).forEach(id -> {
final Map<String, Object> flowDetails = new HashMap<>();
final Flow def = Flow.class.cast(v.getFlowDefinition(id));
final Map<String, Map> states = new HashMap<>();
Arrays.stream(def.getStateIds()).forEach(st -> {
final State state = (State) def.getState(st);
final Map<String, Object> stateMap = new HashMap<>();
if (!state.getAttributes().asMap().isEmpty()) {
stateMap.put("attributes", state.getAttributes().asMap());
}
if (StringUtils.isNotBlank(state.getCaption())) {
stateMap.put("caption", state.getCaption());
}
List acts = StreamSupport.stream(state.getEntryActionList().spliterator(), false)
.map(Object::toString)
.collect(Collectors.toList());
if (!acts.isEmpty()) {
stateMap.put("entryActions", acts);
}
if (state instanceof EndState) {
stateMap.put("isEndState", Boolean.TRUE);
}
if (state.isViewState()) {
stateMap.put("isViewState", state.isViewState());
stateMap.put("isRedirect", ((ViewState) state).getRedirect());
acts = StreamSupport.stream(state.getEntryActionList().spliterator(), false)
.map(Object::toString)
.collect(Collectors.toList());
if (!acts.isEmpty()) {
stateMap.put("renderActions", ((ViewState) state).getRenderActionList());
}
acts = Arrays.stream(((ViewState) state).getVariables())
.map(value -> value.getName() + " -> " + value.getValueFactory().toString())
.collect(Collectors.toList());
if (!acts.isEmpty()) {
stateMap.put("viewVariables", acts);
}
final Field field = ReflectionUtils.findField(((ViewState) state).getViewFactory().getClass(), "viewId");
ReflectionUtils.makeAccessible(field);
final Expression exp = (Expression) ReflectionUtils.getField(field, ((ViewState) state).getViewFactory());
stateMap.put("viewId", StringUtils.defaultIfBlank(exp.getExpressionString(), exp.getValue(null).toString()));
}
if (state instanceof TransitionableState) {
final TransitionableState stDef = TransitionableState.class.cast(state);
acts = StreamSupport.stream(stDef.getExitActionList().spliterator(), false)
.map(Object::toString)
.collect(Collectors.toList());
if (!acts.isEmpty()) {
stateMap.put("exitActions", acts);
}
acts = Arrays.stream(stDef.getTransitions())
.map(tr -> tr.getId() + " -> " + tr.getTargetStateId())
.collect(Collectors.toList());
if (!acts.isEmpty()) {
stateMap.put("transitions", acts);
}
}
states.put(st, stateMap);
});
flowDetails.put("states", states);
flowDetails.put("startState", def.getStartState().getId());
flowDetails.put("possibleOutcomes", def.getPossibleOutcomes());
flowDetails.put("stateCount", def.getStateCount());
List acts = StreamSupport.stream(def.getEndActionList().spliterator(), false)
.map(Object::toString)
.collect(Collectors.toList());
if (!acts.isEmpty()) {
flowDetails.put("endActions", acts);
}
acts = StreamSupport.stream(def.getGlobalTransitionSet().spliterator(), false)
.map(tr -> tr.getId() + " -> " + tr.getTargetStateId() + " @ " + tr.getExecutionCriteria().toString())
.collect(Collectors.toList());
if (!acts.isEmpty()) {
flowDetails.put("globalTransitions", acts);
}
acts = Arrays.stream(def.getExceptionHandlerSet().toArray())
.map(Object::toString)
.collect(Collectors.toList());
if (!acts.isEmpty()) {
flowDetails.put("exceptionHandlers", acts);
}
final String vars = Arrays.stream(def.getVariables())
.map(FlowVariable::getName)
.collect(Collectors.joining(","));
if (StringUtils.isNotBlank(vars)) {
flowDetails.put("variables", vars);
}
jsonMap.put(id, flowDetails);
});
});
return jsonMap;
}
}