package org.springframework.data.rest.shell.commands; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.SerializationConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.context.expression.EnvironmentAccessor; import org.springframework.context.expression.MapAccessor; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.env.Environment; import org.springframework.core.env.StandardEnvironment; import org.springframework.expression.BeanResolver; import org.springframework.expression.ParserContext; import org.springframework.expression.PropertyAccessor; import org.springframework.expression.common.TemplateParserContext; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.ReflectivePropertyAccessor; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.shell.core.CommandMarker; import org.springframework.shell.core.annotation.CliAvailabilityIndicator; import org.springframework.shell.core.annotation.CliCommand; import org.springframework.shell.core.annotation.CliOption; import org.springframework.stereotype.Component; /** * @author Jon Brisbin */ @Component public class ContextCommands implements CommandMarker, InitializingBean { private static final Logger LOG = LoggerFactory.getLogger(ContextCommands.class); private static final PropertyAccessor MAP_ACCESOR = new MapAccessor(); private static final PropertyAccessor BEAN_ACCESOR = new ReflectivePropertyAccessor(); private static final PropertyAccessor ENV_ACCESSOR = new EnvironmentAccessor(); private static final Environment ENV = new StandardEnvironment(); final Map<String, Object> variables = new HashMap<String, Object>(); StandardEvaluationContext evalCtx; private final SpelExpressionParser parser = new SpelExpressionParser(); private final ParserContext parserContext = new TemplateParserContext(); private final ObjectMapper mapper = new ObjectMapper(); private BeanResolver beanFactoryResolver = null; private ApplicationContext userAppCtx; { mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true); mapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false); } @Override public void afterPropertiesSet() throws Exception { userAppCtx = new ClassPathXmlApplicationContext("classpath*:META-INF/rest-shell/**/*.xml"); beanFactoryResolver = new BeanFactoryResolver(userAppCtx); clear(); } @CliAvailabilityIndicator({"var clear", "var set", "var get", "var list"}) public boolean available() { return true; } @CliCommand(value = "var clear", help = "Clear this shell's variable context") public void clear() { if(variables.containsKey("links")) { evalCtx.removePropertyAccessor(((Links)variables.get("links")).getPropertyAccessor()); } variables.clear(); setup(); if(LOG.isDebugEnabled()) { LOG.debug("Cleared context variables..."); } } @CliCommand(value = "var set", help = "Set a variable in this shell's context") public void set( @CliOption( key = "name", mandatory = true, help = "The name of the variable to set") String name, @CliOption( key = "value", mandatory = false, help = "The value of the variable to set") String value) { if(null == value) { variables.remove(name); return; } if(value.contains("#{")) { Object val = eval(value); variables.put(name, val); } else if(value.startsWith("{")) { try { variables.put(name, mapper.readValue(value.replaceAll("\\\\", "").replaceAll("'", "\""), Map.class)); } catch(IOException e) { throw new IllegalArgumentException(e); } } else if(value.startsWith("[")) { try { variables.put(name, mapper.readValue(value.replaceAll("\\\\", "").replaceAll("'", "\""), List.class)); } catch(IOException e) { throw new IllegalArgumentException(e); } } else if(userAppCtx.containsBean(value)) { variables.put(name, userAppCtx.getBean(value)); } else { variables.put(name, value); } if(LOG.isDebugEnabled()) { LOG.debug("Set context variable '" + name + "' to " + variables.get(name)); } } @CliCommand(value = "var get", help = "Get a variable in this shell's context") public Object get( @CliOption( key = "name", mandatory = false, help = "The name of the variable to get") String name, @CliOption( key = "value", mandatory = false, help = "The expression to evaluate") String value) { if(null != name) { if(variables.containsKey(name)) { return variables.get(name); } else if(userAppCtx.containsBean(name)) { return userAppCtx.getBean(name); } } if(null != value && value.contains("#{")) { return eval(value); } return null; } @CliCommand(value = "var list", help = "List variables currently set in this shell's context") public String list() { try { variables.remove("env"); return mapper.writeValueAsString(variables); } catch(IOException e) { throw new IllegalStateException(e); } finally { variables.put("env", ENV); } } public Object eval(String expr) { if(null == expr || !expr.contains("#{")) { return expr; } return parser.parseExpression(expr, parserContext).getValue(evalCtx); } public String evalAsString(String expr) { Object o = eval(expr); if(null != o) { return o.toString(); } return null; } private void setup() { evalCtx = new StandardEvaluationContext(variables); List<PropertyAccessor> accessors = new ArrayList<PropertyAccessor>(); accessors.add(MAP_ACCESOR); accessors.add(BEAN_ACCESOR); accessors.add(ENV_ACCESSOR); evalCtx.setPropertyAccessors(accessors); evalCtx.setBeanResolver(beanFactoryResolver); variables.put("env", ENV); } }