自定义maven enforcer验证规则

    现在有一个需求,要求所有web项目在上线前都得在一个properties文件里配有一个属性。假设需求是在每个web项目的WEB-INF\classes目录下的settingConfig.properties文件里需要有一个value值为login.jsp,key可以随意定义。我们的web项目有二、三十个,为避免马虎的同学没按照要求来,也为减少运维同学的工作量我决定引入强制检查。我们在用maven,就自然的想到了enforcer这个插件。

    上Maven Enforcer Plugin网站一查,果然没有直接可用的规则,那就自己写吧。

    其实maven的插件机制还是挺不错的,它内部也用了一个IoC控制反转的框架,叫plexus,你自己编写的插件只要实现它提供的一个接口,外加几个注解即可搞定,这是Maven Enforcer Plugin的源码,一看就基本知道什么个意思:

@Mojo( name = "enforce", defaultPhase = LifecyclePhase.VALIDATE, threadSafe = true )
public class EnforceMojo
    extends AbstractMojo
    implements Contextualizable{
@Parameter( property = "enforcer.fail", defaultValue = "true" )
    protected boolean fail = true;
    //省略……
}

    我呢,是增加Enforcer Plugin的一个规则,这就更简单了,引用它提供的jar包,实现它提供的org.apache.maven.enforcer.rule.api.EnforcerRule接口即可。官网的例子其实已经很详细了。链接在这儿

    我实现的代码在下面,首先是两个公用类:

public abstract class AbstractStandardEnforcerRule implements EnforcerRule {
    /**
     * Specify a friendly message if the rule fails.
     */
    private String message = null;
    /**
     * 是否跳过检查
     */
    private boolean skip = false;
    //getter and setter……
}

public abstract class AbstractNonCacheableEnforcerRule
        extends AbstractStandardEnforcerRule{
    /*
     * @see org.apache.maven.enforcer.rule.api.EnforcerRule#getCacheId()
     */
    public String getCacheId(){
        return "0";
    }
    /*
     * @see org.apache.maven.enforcer.rule.api.EnforcerRule#isCacheable()
     */
    public boolean isCacheable(){
        return false;
    }
    /*
     * @see org.apache.maven.enforcer.rule.api.EnforcerRule#isResultValid(org.apache.maven.enforcer.rule.api.EnforcerRule)
     */
    public boolean isResultValid( EnforcerRule cachedRule ){
        return false;
    }
}

    这个是我定义的配置Bean,maven插件把xml到java Bean的转换都做了,太方便了。

public class RequirePropertyFileBean {
    private String location;
    private String property;
    private String regex;
    //getter and setter……
}

    这三个属性一看便知意思。location是要检查的properties文件的路径,property是properties文件里的key的名字,regex是value值的验证规则,是正则表达式。

    虽然需求是只要求验证当前文件里是否有对应的value值,但我做的更通用一些:

location property regex 解释
检查properties文件里是否有该key
检查properties文件里value是否符合指定的正则

检查properties文件里key对应的value是否符合指定的正则

    这是代码,也就是if、else的事了:

public class FileRequirePropertyEnforcerRule extends AbstractNonCacheableEnforcerRule {
    private List<RequirePropertyFileBean> files;
    @Override
    public void execute(EnforcerRuleHelper helper) throws EnforcerRuleException {
        Log log = helper.getLog();
        if (isSkip()) {
            log.info("skip FileRequireProperty Enforcer Rule");
            return;
        }
        if (files == null || files.size() == 0) {
            log.warn("FileRequireProperty EnforcerRule check files is empty");
            return;
        }
        String failures = checkEnforcerRules(files);
        if (failures.length() > 0) {
            throw new EnforcerRuleException( failures.toString() );
        }
    }
    private String checkEnforcerRules(List<RequirePropertyFileBean> checkedFiles){
        StringBuilder failures = new StringBuilder();
        for (RequirePropertyFileBean fileBean : checkedFiles) {
            boolean fileReadFail = false;
            boolean validateFail = false;
            Properties properties = null;
            if (isNotEmpty(fileBean.getLocation())) {
                File file = new File(fileBean.getLocation());
                if(file.exists()) {
                    try {
                        properties = readPropertyFile(file);
                    } catch (Exception e) {
                        fileReadFail = true;
                    }
                }else {
                    fileReadFail = true;
                }
            } else {
                fileReadFail = true;
            }
            if (isNotEmpty(fileBean.getProperty()) || isNotEmpty(fileBean.getRegex())) {
                if (!checkFileBean(fileBean, properties)) {
                    validateFail = true;
                }
            } else {
                validateFail = true;
            }
            if (fileReadFail) {
                failures.append(fileBean.getLocation() + " read Fail!\n");
            }
            if(validateFail){
                failures.append(fileBean.getLocation()+" not pass the enforcer rule: [property: "+fileBean.getProperty()+",regex:"+fileBean.getRegex()+"]\n");
            }
        }
        return failures.toString();
    }
    private Properties readPropertyFile(File file) throws Exception {
            InputStream fis = null;
            Properties properties=null;
            try {
                fis = new FileInputStream(file);
                if (fis != null) {
                    properties = new Properties();
                    properties.load(fis);
                    fis.close();
                }
            } finally {
                if (fis != null) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                    }
                }
            }
        return properties;
    }
    private boolean checkFileBean(RequirePropertyFileBean fileBean,Properties properties) {
        boolean validateFail = false;
        String checkedProperty = fileBean.getProperty();
        String checkedRegex = fileBean.getRegex();
        if (isNotEmpty(checkedProperty) && isEmpty(checkedRegex)) {
            if (properties.containsKey(checkedProperty)) {
                validateFail = true;
            }
        } else if (isEmpty(checkedProperty) && isNotEmpty(checkedRegex)) {
            Pattern pattern = Pattern.compile(checkedRegex);
            for (Object propertyValue : properties.values()) {
                Matcher matcher = pattern.matcher(propertyValue.toString());
                if (matcher.matches()) {
                    validateFail = true;
                    break;
                }
            }
        } else if (isNotEmpty(checkedProperty) && isNotEmpty(checkedRegex)) {
            Pattern pattern = Pattern.compile(checkedRegex);
            String propertyValue = properties.getProperty(checkedProperty);
            if (isNotEmpty(propertyValue)) {
                Matcher matcher = pattern.matcher(propertyValue);
                if (matcher.matches()) {
                    validateFail = true;
                }
            }
        }
        return validateFail;
    }
    private boolean isNotEmpty(String value) {
        return null != value && !"".equals(value);
    }
    private boolean isEmpty(String value) {
        return null == value || "".equals(value);
    }
    public List<RequirePropertyFileBean> getFiles() {
        return files;
    }
    public void setFiles(List<RequirePropertyFileBean> files) {
        this.files = files;
    }
}

可以设置skip参数,在执行前会检查,如果是true,会跳过检查。

下面是我定义的pom:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>me.riverslob</groupId>
    <artifactId>enforce-rules</artifactId>
    <packaging>jar</packaging>
    <version>1.0.0-SNAPSHOT</version>
    <name>maven enforce rules</name>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <api.version>1.3.1</api.version>
        <maven.version>3.0.5</maven.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.apache.maven.enforcer</groupId>
            <artifactId>enforcer-api</artifactId>
            <version>${api.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>plexus-container-default</artifactId>
                    <groupId>org.codehaus.plexus</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-project</artifactId>
            <version>3.0-alpha-2</version>
            <exclusions>
                <exclusion>
                    <artifactId>plexus-container-default</artifactId>
                    <groupId>org.codehaus.plexus</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-core</artifactId>
            <version>${maven.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-artifact</artifactId>
            <version>${maven.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-plugin-api</artifactId>
            <version>${maven.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.6</version>
                <configuration>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.5.1</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                    <showWarnings>true</showWarnings>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

使用方法就像下面这样:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-enforcer-plugin</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>me.riverslob</groupId>
            <artifactId>enforce-rules</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
    <executions>
        <execution>
            <id>enforce</id>
            <configuration>
                <rules>
                    <requireJavaVersion>
                        <version>[1.3,1.6]</version>
                    </requireJavaVersion>
                    <requireMavenVersion>
                        <version>2.0.6</version>
                    </requireMavenVersion>
                    <fileRequireProperty implementation="me.riverslob.maven.enforcer.rule.FileRequirePropertyEnforcerRule">
                        <!--<skip>true</skip>-->
                        <files>
                            <file>
                                <location>${basedir}/src/main/resources/settingConfig.properties</location>
                                <regex>.*login\.jsp$</regex>
                            </file>
                            <file>
                                <location>${basedir}/src/main/resources/settingConfig.properties</location>
                                <regex>.*non-login2\.jsp$</regex>
                            </file>
                        </files>
                    </fileRequireProperty>
                </rules>
            </configuration>
            <goals>
                <goal>enforce</goal>
            </goals>
        </execution>
    </executions>
</plugin>

很简单吧?

现在说说debug,代码都测试差不多了,google一把,发现maven在2.0.9版本后增加了mvnDebug命令,执行命令,然后用idea开远程debug就行,就这么简单!

mvnDebug validate

然后远程debug连8000端口即可。

你可能感兴趣的:(自定义maven enforcer验证规则)