velocity自定义指令不生效问题解决之旅

Posted frazeworld

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了velocity自定义指令不生效问题解决之旅相关的知识,希望对你有一定的参考价值。

一、背景现象

为了支持灵活的、可自定义的脱敏规则,工程拟采用velocity实现该目的,为此,自定义了:

  • mask、substr两个指令,其中
  • mask实现
public class MaskDirective extends Directive {

    @Override
    public String getName() {
        return "mask";
    }

    @Override
    public int getType() {
        return LINE;
    }

    @Override
    public boolean render(InternalContextAdapter ctx, Writer writer, Node node) throws IOException, ResourceNotFoundException, ParseErrorException, MethodInvocationException {

        //fieldName
        SimpleNode snField = (SimpleNode) node.jjtGetChild(0);

        String maskChar = (String)snField.value(ctx);

        snField = (SimpleNode) node.jjtGetChild(1);
        int count = (int)snField.value(ctx);

        StringBuilder buff = new StringBuilder();

        for(int i = 0; i < count; i++) {
            buff.append(maskChar);
        }

        writer.write(buff.toString());

        return true;

    }
}
  •  substr实现
public class SubstrDirective extends Directive {

    @Override
    public String getName() {
        return "substr";
    }

    @Override
    public int getType() {
        return LINE;
    }

    @Override
    public boolean render(InternalContextAdapter ctx, Writer writer, Node node) throws IOException, ResourceNotFoundException, ParseErrorException, MethodInvocationException {

        //fieldName
        SimpleNode snField = (SimpleNode) node.jjtGetChild(0);

        String fieldValue = (String)snField.value(ctx);

        if(fieldValue == null || fieldValue.length() == 0){
            return false;
        }

        int items = node.jjtGetNumChildren();

        int startIdx = Integer.MIN_VALUE;
        int endIdx = fieldValue.length();

        if(items < 1){
            return false;
        }

        try{

            snField = (SimpleNode) node.jjtGetChild(1);
            startIdx = (int)snField.value(ctx);

            if(items > 2){
                snField = (SimpleNode) node.jjtGetChild(2);
                endIdx = (int)snField.value(ctx);

                if(endIdx > fieldValue.length()){
                    endIdx = fieldValue.length();
                }

            }

            if(startIdx >= endIdx){
                return false;
            }

            StringBuilder buff = new StringBuilder();

            if(items > 2){
                writer.write(fieldValue.substring(startIdx,endIdx));
            }else{

                if(startIdx <  0){
                    startIdx = startIdx + fieldValue.length();
                }

                writer.write(fieldValue.substring(startIdx));
            }

            return true;

        }catch(Exception e){
            return false;
        }
    }
}
  •  velocity初始化方法:
public final class VelocityHelper {

    private Set<String> userExtDirective ;
    private final static String K_USERDIRECTIVE = "userdirective";

    private final static VelocityHelper VH = new VelocityHelper();
   private VelocityHelper(){

    }

    public static final VelocityHelper getInstance(){
        return VH;
    }

    /**
     * 初始化
     */
    public void init() throws Exception{

        Properties prop = new Properties();

        StringBuilder buff = new StringBuilder();

        for(String s : userExtDirective){
            buff.append(s.trim()).append(",");
        }

        LogHelper.trace("start VelocityHelper.init[" + buff.toString() + "]");

        prop.setProperty(K_USERDIRECTIVE, buff.toString().substring(0, buff.length()-1));
        Velocity.init(prop);

        LogHelper.trace("end VelocityHelper.init");

    }

    /**
     * 根据报文模板和<code>VelocityContext</code>,渲染出实际的业务报文
     *
     * @param context 业务上下文信息
     * @param template 模板
     * @return
     * @throws IOException
     */
    public String evaluate(String template, final VelocityContext context) throws IOException {
        Writer writer = new StringWriter();
        try {
            Velocity.evaluate(context, writer, StringUtil.EMPTY_STRING, template);
            return writer.toString();
        } finally {
            IOUtils.closeQuietly(writer);
        }
    }

    /**
     *
     * @param userExtDirective
     *
     */
    public void setUserExtDirective(Set<String> userExtDirective){

        this.userExtDirective = userExtDirective;
    }

}

   

   部署到服务器后,以下表达式始终未得到替换:#substr($maskField, 0, 3)#mask(‘*‘, 4)#substr($maskField, -4) 

   

二、原因分析

    开始怀疑是配置等问题,经过以下方法尝试均为解决问题:

    1、怀疑spring xml配置问题----未解决

    2、硬编码指令,在构造函数塞入指令----未解决

    

    无奈,登录服务器grep velocity 得到了以下的输出(其中之一),茅塞顿开。

    2019-02-02 07:49:52,315 [  - /// - ] INFO  assemble.VMRender - resource=class path resource [velocity/velocity.properties]   

    Velocity.ini及Velocity.evalute均是全局单一模式,既然有人捷足先登,自然不在做第二次初始化。

三、解决方案

     采用VelocityEngine替换Velocity,实现jar包间隔离,独立管理自己的资源。

private VelocityEngine velocityEngine = null;
velocityEngine = new VelocityEngine(prop);
velocityEngine.evaluate(context, writer, StringUtil.EMPTY_STRING, template);

以上是关于velocity自定义指令不生效问题解决之旅的主要内容,如果未能解决你的问题,请参考以下文章

nvue 踩坑之旅

Confluence 6 升级自定义的站点和空间关闭缓存

Confluence 6 升级自定义的站点和空间关闭缓存

我的C/C++语言学习进阶之旅解决使用algorithm库里面的sort函数的时候,编译报错:未能使函数模板“unknown-type std::less<void>::operator ()(代码片

我的C/C++语言学习进阶之旅解决使用algorithm库里面的sort函数的时候,编译报错:未能使函数模板“unknown-type std::less<void>::operator ()(代码片

ik分词器 自定义字典无效问题以及解决