package com.github.ompc.greys.core.command.hacking; import com.github.ompc.greys.core.GlobalOptions; import com.github.ompc.greys.core.GlobalOptions.Option; import com.github.ompc.greys.core.command.Command; import com.github.ompc.greys.core.command.annotation.Cmd; import com.github.ompc.greys.core.command.annotation.IndexArg; import com.github.ompc.greys.core.server.Session; import com.github.ompc.greys.core.textui.TTable; import com.github.ompc.greys.core.textui.TTable.ColumnDefine; import com.github.ompc.greys.core.util.affect.RowAffect; import com.github.ompc.greys.core.util.matcher.EqualsMatcher; import com.github.ompc.greys.core.util.matcher.Matcher; import com.github.ompc.greys.core.util.matcher.TrueMatcher; import org.apache.commons.lang3.reflect.FieldUtils; import java.lang.instrument.Instrumentation; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import static com.github.ompc.greys.core.textui.TTable.Align.LEFT; import static com.github.ompc.greys.core.util.GaCheckUtils.isIn; import static com.github.ompc.greys.core.util.GaStringUtils.newString; import static java.lang.String.format; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; /** * 选项开关命令 * Created by oldmanpushcart@gmail.com on 15/6/6. */ @Cmd(isHacking = true, name = "options", summary = "Greys options", eg = { "options dump true", "options unsafe true" } ) public class OptionsCommand implements Command { @IndexArg(index = 0, name = "options-name", isRequired = false, summary = "Option name") private String optionName; @IndexArg(index = 1, name = "options-value", isRequired = false, summary = "Option value") private String optionValue; @Override public Action getAction() { // show the options if (isShow()) { return doShow(); } // show the options name else if (isShowName()) { return doShowName(); } // change the name/value else { return doChangeNameValue(); } } /* * 判断当前动作是否需要展示整个options */ private boolean isShow() { return isBlank(optionName) && isBlank(optionValue); } /* * 判断当前动作是否需要展示某个Name的值 */ private boolean isShowName() { return isNotBlank(optionName) && isBlank(optionValue); } private RowAction doShow() { return new RowAction() { @Override public RowAffect action(Session session, Instrumentation inst, Printer printer) throws Throwable { final RowAffect affect = new RowAffect(); final Collection<Field> fields = findOptions(new TrueMatcher<String>()); printer.print(drawShowTable(fields)).finish(); affect.rCnt(fields.size()); return affect; } }; } private RowAction doShowName() { return new RowAction() { @Override public RowAffect action(Session session, Instrumentation inst, Printer printer) throws Throwable { final RowAffect affect = new RowAffect(); final Collection<Field> fields = findOptions(new EqualsMatcher<String>(optionName)); printer.print(drawShowTable(fields)).finish(); affect.rCnt(fields.size()); return affect; } }; } private Collection<Field> findOptions(Matcher<String> optionNameMatcher) { final Collection<Field> matchFields = new ArrayList<Field>(); for (final Field optionField : FieldUtils.getAllFields(GlobalOptions.class)) { if (!optionField.isAnnotationPresent(Option.class)) { continue; } final Option optionAnnotation = optionField.getAnnotation(Option.class); if (optionAnnotation != null && !optionNameMatcher.matching(optionAnnotation.name())) { continue; } matchFields.add(optionField); } return matchFields; } private String drawShowTable(Collection<Field> optionFields) throws IllegalAccessException { final TTable tTable = new TTable(new ColumnDefine[]{ new ColumnDefine(6, false, LEFT), new ColumnDefine(10, false, LEFT), new ColumnDefine(), new ColumnDefine(), new ColumnDefine(20, false, LEFT), new ColumnDefine(50, false, LEFT) }) .addRow("LEVEL", "TYPE", "NAME", "VALUE", "SUMMARY", "DESCRIPTION") .padding(1); for (final Field optionField : optionFields) { final Option optionAnnotation = optionField.getAnnotation(Option.class); tTable.addRow( optionAnnotation.level(), optionField.getType().getSimpleName(), optionAnnotation.name(), optionField.get(null), optionAnnotation.summary(), optionAnnotation.description() ); } return tTable.rendering(); } private RowAction doChangeNameValue() { return new RowAction() { @Override public RowAffect action(Session session, Instrumentation inst, Printer printer) throws Throwable { final RowAffect affect = new RowAffect(); final Collection<Field> fields = findOptions(new EqualsMatcher<String>(optionName)); // name not exists if (fields.isEmpty()) { printer.println(format("options[%s] not found.", optionName)).finish(); return affect; } final Field field = fields.iterator().next(); final Option optionAnnotation = field.getAnnotation(Option.class); final Class<?> type = field.getType(); final Object beforeValue = FieldUtils.readStaticField(field); final Object afterValue; try { // try to case string to type if (isIn(type, int.class, Integer.class)) { writeStaticField(field, afterValue = Integer.valueOf(optionValue)); } else if (isIn(type, long.class, Long.class)) { writeStaticField(field, afterValue = Long.valueOf(optionValue)); } else if (isIn(type, boolean.class, Boolean.class)) { writeStaticField(field, afterValue = Boolean.valueOf(optionValue)); } else if (isIn(type, double.class, Double.class)) { writeStaticField(field, afterValue = Double.valueOf(optionValue)); } else if (isIn(type, float.class, Float.class)) { writeStaticField(field, afterValue = Float.valueOf(optionValue)); } else if (isIn(type, byte.class, Byte.class)) { writeStaticField(field, afterValue = Byte.valueOf(optionValue)); } else if (isIn(type, short.class, Short.class)) { writeStaticField(field, afterValue = Short.valueOf(optionValue)); } else { printer.println(format("Options[%s] type[%s] desupported.", optionName, type.getSimpleName())).finish(); return affect; } affect.rCnt(1); } catch (Throwable t) { printer.println(format("Cannot cast option value[%s] to type[%s].", optionValue, type.getSimpleName())).finish(); return affect; } final TTable tTable = new TTable(4) .padding(1) .addRow("NAME", "BEFORE-VALUE", "AFTER-VALUE") .addRow(optionAnnotation.name(), newString(beforeValue), newString(afterValue)); printer.print(tTable.rendering()).finish(); return affect; } }; } }