Skip to content

Instantly share code, notes, and snippets.

@rbonatuvic
Created December 21, 2018 20:34
Show Gist options
  • Save rbonatuvic/d3ef9e8dc0c5a78870a8520bc2ab2b74 to your computer and use it in GitHub Desktop.
Save rbonatuvic/d3ef9e8dc0c5a78870a8520bc2ab2b74 to your computer and use it in GitHub Desktop.
Formatter for spring webflow, org.springframework.webflow.engine.Flow, toString output and unit test
package ca.uvic.idm.cas.web.flow;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.web.flow.configurer.AbstractCasWebflowConfigurer;
import org.springframework.context.ApplicationContext;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.engine.ActionState;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.regex.Pattern;
@Slf4j
public abstract class AbstractUvicCasWebflowConfigurer extends AbstractCasWebflowConfigurer {
protected String outputFileName = "/tmp/flow.txt";
public AbstractUvicCasWebflowConfigurer(FlowBuilderServices flowBuilderServices,
FlowDefinitionRegistry loginFlowDefinitionRegistry,
ApplicationContext applicationContext,
CasConfigurationProperties casProperties) {
super(flowBuilderServices, loginFlowDefinitionRegistry, applicationContext, casProperties);
}
/**
* Inserts an inbound state into a transition for a target state and sets inbound
* state's matching transition to the previous target (preserving overall flow).
* Inbound state inserts itself into target state by replacing target transition.
* @param flow
* @param inboundStateId bean identifier for insertable action state
* @param inboundActionId action to be performed
* @param targetStateid bean identifier for state immediately before inbound
* @param targetTransitionId transition point for insertion
*/
protected ActionState insertIntoFlow(
final Flow flow,
final String inboundStateId,
final String inboundActionId,
final String targetStateid,
final String targetTransitionId) {
val inboundActionState = createActionState(flow, inboundStateId, inboundActionId);
val targetState = getState(flow, targetStateid, ActionState.class);
val destinationStateId = targetState.getTransition(targetTransitionId).getTargetStateId();
val inboundTransitionSet = inboundActionState.getTransitionSet();
inboundTransitionSet.add(createTransition(targetTransitionId, destinationStateId));
createTransitionForState(targetState, targetTransitionId, inboundStateId, true);
if (LOGGER.isTraceEnabled()) {
flowToFile(flow);
}
return inboundActionState;
}
protected void flowToFile(final Flow flow) {
flowToFile(flow, outputFileName);
}
protected void flowToFile(final Flow flow, final String fileName) {
String s = flow.toString().trim();
String formatted = formatFlow(s);
try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
writer.write(s);
writer.write("\n\n\n\n");
writer.write(formatted);
} catch (IOException e) {
LOGGER.error(e.getMessage());
}
}
/**
* Formats a spring webflow flow to help determine how to modify a flow.
* Adds new lines and indents to make it easier to read.
* @param input flow.toString()
* @return nicely formatted flow
*/
public String formatFlow(final String input) {
//LOGGER.debug("input: ." + input + ".");
// used to add an extra indent for an object's field members
java.util.Stack<java.util.AbstractMap.SimpleEntry> stack = new java.util.Stack<>();
int currPosition = 0;
String indent = "";
String indentor = "\t";
String newLine = "\n";
// object identifier
java.util.regex.Pattern objPattern = Pattern.compile("^(\\w+@\\w+)\\b.*");
String in = input.trim();
StringBuilder out = new StringBuilder();
while (in.length() > currPosition) {
java.util.regex.Matcher m = objPattern.matcher(in.substring(currPosition));
String firstTwo = "";
// capture first two characters to match against ']' or '],'
if (1 < in.length() - currPosition) {
firstTwo = in.substring(currPosition, currPosition + 2);
} else {
// at end of input
firstTwo = in.substring(currPosition, currPosition + 1);
}
if (in.startsWith("[", currPosition)) {
out.append(indent).append(in.charAt(currPosition)).append(newLine);
indent += indentor;
currPosition++;
if (!stack.empty()) {
java.util.AbstractMap.SimpleEntry<String, Integer> se = stack.pop();
se.setValue(se.getValue() + 1);
stack.push(se);
}
} else if (firstTwo.startsWith("]")) {
if (!stack.empty()) {
java.util.AbstractMap.SimpleEntry<String, Integer> se = stack.pop();
if (1 > se.getValue()) {
// outdent after printing member variables
indent = indent.replaceFirst(indentor, "");
if (!stack.empty()) {
// this ] closes from outer object
java.util.AbstractMap.SimpleEntry<String, Integer> seOuter = stack.pop();
seOuter.setValue(seOuter.getValue() - 1);
stack.push(seOuter);
}
} else {
se.setValue(se.getValue() - 1);
stack.push(se);
}
}
indent = indent.replaceFirst(indentor, "");
out.append(indent).append("]");
if ("],".equals(firstTwo)) {
out.append(",");
currPosition++;
}
out.append(newLine);
currPosition++;
} else if (m.matches()) {
String obj = m.group(1);
out.append(indent).append(obj).append(newLine);
indent = indent + indentor;
// prepare for members
stack.push(new java.util.AbstractMap.SimpleEntry<String, Integer>(obj, 0));
currPosition += obj.length();
} else {
int nextOpenBracket = in.indexOf("[", currPosition);
int nextCloseBracket = in.indexOf("]", currPosition);
int nextComma = in.indexOf(",", currPosition);
int nextMark = 0;
boolean increaseIndent = false;
// if [ or , not found, push beyond last position which would be ]
if (0 > nextOpenBracket) {
nextOpenBracket = in.length();
}
if (0 > nextComma) {
nextComma = in.length();
}
// add 1 when [ and , since they should remain on same line and ] should be on next line
if (nextCloseBracket > nextOpenBracket) {
if (nextOpenBracket > nextComma) {
nextMark = nextComma + 1;
} else {
nextMark = nextOpenBracket + 1;
// bypass empty and null
if ((in.substring(nextMark).startsWith("[empty]]"))
|| (in.substring(nextMark).startsWith("null]"))) {
if (in.substring(nextMark).startsWith("[empty]],")) {
nextMark += 9;
} else if (in.substring(nextMark).startsWith("[empty]]")) {
nextMark += 8;
} else if (in.substring(nextMark).startsWith("null],")) {
nextMark += 6;
} else if (in.substring(nextMark).startsWith("null]")) {
nextMark += 5;
}
} else {
// indent members
increaseIndent = true;
if (!stack.empty()) {
java.util.AbstractMap.SimpleEntry<String, Integer> se = stack.pop();
se.setValue(se.getValue() + 1);
stack.push(se);
}
}
}
} else if (nextCloseBracket > nextComma) {
nextMark = nextComma + 1;
} else {
nextMark = nextCloseBracket;
}
String s = in.substring(currPosition, nextMark).trim();
if (0 < s.length()) {
out.append(indent).append(s).append(newLine);
currPosition = nextMark;
}
if (increaseIndent) {
// for next line
indent = indent + indentor;
}
}
}
String formatted = out.toString().trim();
//LOGGER.debug("formatted: ." + formatted + ".");
return formatted;
}
}
package ca.uvic.idm.cas.web.flow;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.junit.Test;
import org.mockito.Mock;
import org.springframework.context.ApplicationContext;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
import static org.junit.Assert.*;
public class UvicSSoCookieWebflowConfigurerTest {
UvicSsoCookieWebflowConfigurer uvicSsoCookieWebflowConfigurer;
@Mock
FlowBuilderServices flowBuilderServices;
FlowDefinitionRegistry loginFlowDefinitionRegistry;
ApplicationContext applicationContext;
CasConfigurationProperties casProperties;
FlowDefinitionRegistry logoutFlowDefinitionRegistry;//*/
@org.junit.Before
public void setUp() throws Exception {
uvicSsoCookieWebflowConfigurer = new UvicSsoCookieWebflowConfigurer(
flowBuilderServices,
loginFlowDefinitionRegistry,
applicationContext,
casProperties,
logoutFlowDefinitionRegistry
);
}
@org.junit.After
public void tearDown() throws Exception {
Thread.sleep(2000);
}//*/
@Test
public void testFormatFlow() {
//assertTrue(true);
String input;
String expected;
String result;
input = "[EvaluateAction@61e922c5]";
expected = "[\n\tEvaluateAction@61e922c5\n]";
result = uvicSsoCookieWebflowConfigurer.formatFlow(input);
assertEquals(expected, result);
input = "[EvaluateAction@61e922c5 expression = terminateSessionAction]";
expected = "[\n\tEvaluateAction@61e922c5\n\t\texpression = terminateSessionAction\n]";
result = uvicSsoCookieWebflowConfigurer.formatFlow(input);
assertEquals(expected, result);
input = "[EvaluateAction@61e922c5 expression = terminateSessionAction, on = warn, to = confirmLogoutView]";
expected = "[\n\tEvaluateAction@61e922c5\n\t\texpression = terminateSessionAction,\n\t\ton = warn,\n\t\tto = confirmLogoutView\n]";
result = uvicSsoCookieWebflowConfigurer.formatFlow(input);
assertEquals(expected, result);
input = "[EvaluateAction@61e922c5 expression = terminateSessionAction, resultExpression = [null]]";
expected = "[\n\tEvaluateAction@61e922c5\n" +
"\t\texpression = terminateSessionAction,\n" +
"\t\tresultExpression = [null]\n" +
"]";
result = uvicSsoCookieWebflowConfigurer.formatFlow(input);
assertEquals(expected, result);
input = "[EvaluateAction@61e922c5 expression = terminateSessionAction, resultExpression = [null], popup = false]";
expected = "[\n\tEvaluateAction@61e922c5\n" +
"\t\texpression = terminateSessionAction,\n" +
"\t\tresultExpression = [null],\n" +
"\t\tpopup = false\n" +
"]";
result = uvicSsoCookieWebflowConfigurer.formatFlow(input);
assertEquals(expected, result);
input = "[EvaluateAction@61e922c5 expression = terminateSessionAction, exitActionList = list[[empty]]]";
expected = "[\n\tEvaluateAction@61e922c5\n" +
"\t\texpression = terminateSessionAction,\n" +
"\t\texitActionList = list[[empty]]\n" +
"]";
result = uvicSsoCookieWebflowConfigurer.formatFlow(input);
assertEquals(expected, result);
input = "[EvaluateAction@61e922c5 expression = terminateSessionAction, exitActionList = list[[empty]], popup = false]";
expected = "[\n\tEvaluateAction@61e922c5\n" +
"\t\texpression = terminateSessionAction,\n" +
"\t\texitActionList = list[[empty]],\n" +
"\t\tpopup = false\n" +
"]";
result = uvicSsoCookieWebflowConfigurer.formatFlow(input);
assertEquals(expected, result);
input = "[ActionState@311ec3d4 id = 'terminateSession', flow = 'logout', entryActionList = list[" +
"[empty]], exceptionHandlerSet = list[[empty]], actionList = list[[" +
"EvaluateAction@61e922c5 expression = terminateSessionAction, resultExpression = [null]]]" +
"]";
expected = "[\n" +
"\tActionState@311ec3d4\n" +
"\t\tid = 'terminateSession',\n" +
"\t\tflow = 'logout',\n" +
"\t\tentryActionList = list[[empty]],\n" +
"\t\texceptionHandlerSet = list[[empty]],\n" +
"\t\tactionList = list[\n" +
"\t\t\t[\n" +
"\t\t\t\tEvaluateAction@61e922c5\n" +
"\t\t\t\t\texpression = terminateSessionAction,\n" +
"\t\t\t\t\tresultExpression = [null]\n" +
"\t\t\t]\n" +
"\t\t]\n" +
"]";
result = uvicSsoCookieWebflowConfigurer.formatFlow(input);
assertEquals(expected, result);
input = "[ActionState@311ec3d4 id = 'terminateSession', flow = 'logout', entryActionList = list[" +
"[empty]], exceptionHandlerSet = list[[empty]], actionList = list[[" +
"EvaluateAction@61e922c5 expression = terminateSessionAction, resultExpression = [null]]" +
"], transitions = list[[Transition@5e6728c5 on = warn, to = confirmLogoutView], [" +
"Transition@565ce8 on = *, to = doLogout]], exitActionList = list[[empty]]]";
expected = "[\n" +
"\tActionState@311ec3d4\n" +
"\t\tid = 'terminateSession',\n" +
"\t\tflow = 'logout',\n" +
"\t\tentryActionList = list[[empty]],\n" +
"\t\texceptionHandlerSet = list[[empty]],\n" +
"\t\tactionList = list[\n" +
"\t\t\t[\n" +
"\t\t\t\tEvaluateAction@61e922c5\n" +
"\t\t\t\t\texpression = terminateSessionAction,\n" +
"\t\t\t\t\tresultExpression = [null]\n" +
"\t\t\t]\n" +
"\t\t],\n" +
"\t\ttransitions = list[\n" +
"\t\t\t[\n" +
"\t\t\t\tTransition@5e6728c5\n" +
"\t\t\t\t\ton = warn,\n" +
"\t\t\t\t\tto = confirmLogoutView\n" +
"\t\t\t],\n" +
"\t\t\t[\n" +
"\t\t\t\tTransition@565ce8\n" +
"\t\t\t\t\ton = *,\n" +
"\t\t\t\t\tto = doLogout\n" +
"\t\t\t]\n" +
"\t\t],\n" +
"\t\texitActionList = list[[empty]]\n" +
"]";
result = uvicSsoCookieWebflowConfigurer.formatFlow(input);
assertEquals(expected, result);
input = "[ActionState@311ec3d4 id = 'terminateSession', flow = 'logout', entryActionList = list[" +
"[empty]], exceptionHandlerSet = list[[empty]], actionList = list[[" +
"EvaluateAction@61e922c5 expression = terminateSessionAction, resultExpression = [null], resultExpression = [null]]" +
"], transitions = list[[Transition@5e6728c5 on = warn, to = confirmLogoutView], [" +
"Transition@565ce8 on = *, to = doLogout]], exitActionList = list[[empty]]]";
expected = "[\n" +
"\tActionState@311ec3d4\n" +
"\t\tid = 'terminateSession',\n" +
"\t\tflow = 'logout',\n" +
"\t\tentryActionList = list[[empty]],\n" +
"\t\texceptionHandlerSet = list[[empty]],\n" +
"\t\tactionList = list[\n" +
"\t\t\t[\n" +
"\t\t\t\tEvaluateAction@61e922c5\n" +
"\t\t\t\t\texpression = terminateSessionAction,\n" +
"\t\t\t\t\tresultExpression = [null],\n" +
"\t\t\t\t\tresultExpression = [null]\n" +
"\t\t\t]\n" +
"\t\t],\n" +
"\t\ttransitions = list[\n" +
"\t\t\t[\n" +
"\t\t\t\tTransition@5e6728c5\n" +
"\t\t\t\t\ton = warn,\n" +
"\t\t\t\t\tto = confirmLogoutView\n" +
"\t\t\t],\n" +
"\t\t\t[\n" +
"\t\t\t\tTransition@565ce8\n" +
"\t\t\t\t\ton = *,\n" +
"\t\t\t\t\tto = doLogout\n" +
"\t\t\t]\n" +
"\t\t],\n" +
"\t\texitActionList = list[[empty]]\n" +
"]";
result = uvicSsoCookieWebflowConfigurer.formatFlow(input);
assertEquals(expected, result);
input = "[ActionState@311ec3d4 id = 'terminateSession', flow = 'logout', entryActionList = list[" +
"[empty]], exceptionHandlerSet = list[[empty]], actionList = list[[" +
"EvaluateAction@61e922c5 expression = terminateSessionAction, resultExpression = [null], resultExpression = [null]]" +
"], transitions = list[[Transition@5e6728c5 on = warn, to = confirmLogoutView], [" +
"Transition@565ce8 on = *, to = doLogout]], exitActionList = list[[empty]], popup = false]";
expected = "[\n" +
"\tActionState@311ec3d4\n" +
"\t\tid = 'terminateSession',\n" +
"\t\tflow = 'logout',\n" +
"\t\tentryActionList = list[[empty]],\n" +
"\t\texceptionHandlerSet = list[[empty]],\n" +
"\t\tactionList = list[\n" +
"\t\t\t[\n" +
"\t\t\t\tEvaluateAction@61e922c5\n" +
"\t\t\t\t\texpression = terminateSessionAction,\n" +
"\t\t\t\t\tresultExpression = [null],\n" +
"\t\t\t\t\tresultExpression = [null]\n" +
"\t\t\t]\n" +
"\t\t],\n" +
"\t\ttransitions = list[\n" +
"\t\t\t[\n" +
"\t\t\t\tTransition@5e6728c5\n" +
"\t\t\t\t\ton = warn,\n" +
"\t\t\t\t\tto = confirmLogoutView\n" +
"\t\t\t],\n" +
"\t\t\t[\n" +
"\t\t\t\tTransition@565ce8\n" +
"\t\t\t\t\ton = *,\n" +
"\t\t\t\t\tto = doLogout\n" +
"\t\t\t]\n" +
"\t\t],\n" +
"\t\texitActionList = list[[empty]],\n" +
"\t\tpopup = false\n" +
"]";
result = uvicSsoCookieWebflowConfigurer.formatFlow(input);
assertEquals(expected, result);
}//*/
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment